1. 为什么要在 Windows 上折腾 MinGW/MSYS2 编译 FFmpeg很多做音视频开发的同行第一次接触 FFmpeg 都是在 Windows 上直接下载官方预编译包解压、配好环境变量ffmpeg -version能跑起来就算完事。但只要项目稍微深入一点问题就来了预编译包默认不带某些编码器想加个libx264或者libfdk_aac发现根本没法动态加载想调试进 FFmpeg 源码看某个函数到底怎么走的预编译包只有二进制没有符号想裁掉一堆用不上的模块把体积压下来预编译包又是全量功能。这时候唯一的出路就是自己编译。在 Windows 上编译 FFmpeg主流路线有三条一是用 MSVC vcpkg二是用 WSL 走 Linux 那套三是用 MinGW/MSYS2。前两条各有各的坑——MSVC 对 FFmpeg 的 configure 脚本支持一直不算友好很多第三方库的编译脚本默认按 GCC 那套写WSL 虽然省心但产物是 Linux 的 ELF 格式没法直接在 Windows 原生环境里调用做 Windows 桌面端或者游戏引擎集成的场景就不合适。所以 MinGW/MSYS2 这条路线成了很多需要在 Windows 上产出原生.dll/.exe的开发者绕不开的选择。MSYS2 本质上是一套在 Windows 上模拟 POSIX 环境的工具链集合它自带包管理器pacman可以很方便地装 MinGW-w64 的 GCC 工具链、make、pkg-config、nasm 这些编译 FFmpeg 必需的组件。用它编译出来的产物是标准的 Windows PE 格式能被 MSVC 工程、C# 的 P/Invoke、Python 的 ctypes 正常加载。这篇文章我就把整套流程从头到尾拆一遍包括工具链怎么选、依赖库怎么编、configure 参数怎么配、踩过的坑怎么填目标是让你照着做能一次跑通而不是在某个报错上卡三天。适合读这篇的人需要在 Windows 上产出 FFmpeg 原生库的音视频开发者、做播放器或转码工具的工程师、想把 FFmpeg 集成进自己 C/C# 项目的同学。如果你只是想用命令行转个格式那直接下预编译包就行没必要折腾编译。但只要你有定制需求这篇就是给你准备的。2. 环境搭建MSYS2 与工具链的正确打开方式2.1 MSYS2 安装与 pacman 基础操作MSYS2 的安装包去官网下最新的 x86_64 版本一路下一步即可安装路径强烈建议不要带空格和中文比如C:\msys64就是最省心的选择。为什么强调这个因为 FFmpeg 的 configure 脚本和很多第三方库的 Makefile 里路径处理并不总是加引号一旦路径里有空格C:\Program Files\...这种就会被拆成两段报出一堆莫名其妙的 No such file or directory。我见过太多人卡在这里最后发现只是装到了 Program Files 下。装完之后打开 MSYS2 的终端注意这里有个关键选择MSYS2 提供了好几个不同的启动快捷方式分别是MSYS2 MSYS、MSYS2 MINGW64、MSYS2 MINGW32、MSYS2 UCRT64、MSYS2 CLANG64等。很多人随手打开第一个MSYS2 MSYS就开始装东西这是错的。MSYS2 MSYS环境用的是 MSYS 自己的运行时编译出来的东西依赖msys-2.0.dll不是原生 Windows 程序。我们要编译原生 FFmpeg必须用MSYS2 MINGW6464 位或MSYS2 UCRT64。先更新包数据库和基础包pacman -Syu这条命令跑完可能会提示你关闭终端重新打开因为 pacman 自身更新了。重新打开后再跑一次pacman -Su2.2 MINGW64 与 UCRT64 的选择这里要展开讲一下 MINGW64 和 UCRT64 的区别因为选错了后面会有一堆兼容性问题。MINGW64 用的是 msvcrt 作为 C 运行时这是 Windows 上最老牌的 C 运行时兼容性极好从 Windows XP 到 Windows 11 都能跑。但它的缺点是 C99/C11 的一些新特性支持不完整而且和 MSVC 编译的库混用时涉及到 FILE 指针、内存分配跨模块传递时容易出问题。UCRT64 用的是 Universal C Runtime这是微软从 Windows 10 开始主推的运行时对 C99/C11 支持更完整和 MSVC 的兼容性也更好。如果你的目标平台是 Windows 10 及以上且需要和 MSVC 编译的模块混用UCRT64 是更好的选择。但如果你的程序还要兼容 Windows 7那就只能选 MINGW64。我个人的建议是新项目一律用 UCRT64老项目或者需要兼容老系统的用 MINGW64。下面为了通用性我以 MINGW64 为例UCRT64 的操作完全一样只是把命令里的mingw-w64-x86_64-前缀换成mingw-w64-ucrt-x86_64-。2.3 安装编译 FFmpeg 必需的工具链在 MINGW64 终端里执行pacman -S --needed base-devel mingw-w64-x86_64-toolchain这个toolchain组包含了 GCC、binutils、make 等核心工具。安装过程中会问你要装哪些包直接回车全装即可。装完之后验证一下gcc --version make --version除了基础工具链FFmpeg 编译还需要几个专门的工具pacman -S mingw-w64-x86_64-nasm mingw-w64-x86_64-yasm mingw-w64-x86_64-pkg-confignasm和yasm是汇编器FFmpeg 里大量的 SIMD 优化代码比如 x86 的 AVX2、SSE4 指令都是用汇编写的没有它们编译会直接报错。pkg-config用来在 configure 阶段查找第三方库的头文件和链接信息是依赖管理的核心工具。注意nasm 和 yasm 装一个其实也能编但 FFmpeg 的 configure 会分别检测两者某些汇编文件只认其中一个。两个都装上最省事反正体积也不大。2.4 目录规划源码、依赖、产物分开放在开始编译之前先把目录结构规划好不然后面依赖库和 FFmpeg 的路径会乱成一锅粥。我的习惯是在某个盘符下建一个工作目录比如D:\ffmpeg-build里面分三个子目录D:\ffmpeg-build\sources放所有源码包包括 FFmpeg 和各个第三方库D:\ffmpeg-build\install所有第三方库编译后的安装前缀统一装到这里D:\ffmpeg-build\outputFFmpeg 最终的安装目录这样规划的好处是所有第三方库都装到同一个前缀下FFmpeg 的 configure 只需要指定一个--prefix就能找到全部依赖不用一个个写路径。而且将来想清理重来直接删掉整个ffmpeg-build目录就行不会污染系统。在 MSYS2 终端里Windows 路径D:\ffmpeg-build要写成/d/ffmpeg-build。先建好目录mkdir -p /d/ffmpeg-build/{sources,install,output}3. 第三方依赖库的编译顺序与实操FFmpeg 本身是个框架真正干活的是各种外部库。要不要编第三方库取决于你的需求。如果只是想要个能转常见格式的 FFmpeg其实不编任何第三方库也能跑内置的编解码器够用。但如果你要 H.264 编码libx264、H.265 编码libx265、AAC 编码libfdk_aac 或内置的 aac、VP9 编码libvpx这些就必须先把对应的库编出来。3.1 依赖库的编译顺序为什么重要第三方库之间有依赖关系顺序错了会导致后面的库找不到前面的库。正确的顺序大致是zlib最底层很多库都依赖它libiconv字符编码转换某些库需要libx264 / libx265视频编码核心libvpxVP8/VP9 编解码libfdk-aac高质量 AAC 编码libmp3lameMP3 编码libopusOpus 音频编解码libass / freetype / harfbuzz / fribidi字幕渲染相关FFmpeg最后编这个顺序不是绝对的但遵循被依赖的在前这个原则就不会错。下面挑几个最常用的库讲一下编译方法其他的套路基本一致。3.2 zlib 的编译zlib 是很多库的隐式依赖先把它编了。下载源码解压到sources目录进入目录后make -f win32/Makefile.gcc PREFIXx86_64-w64-mingw32- \ BINARY_PATH/d/ffmpeg-build/install/bin \ INCLUDE_PATH/d/ffmpeg-build/install/include \ LIBRARY_PATH/d/ffmpeg-build/install/lib \ installzlib 的 Makefile 比较特殊它不用 configure直接用win32/Makefile.gcc这个文件。PREFIX指定交叉编译工具前缀后面三个路径分别指定二进制、头文件、库文件的安装位置。编完之后检查一下install/lib下有没有libz.a。3.3 libx264 的编译libx264 是 H.264 编码的事实标准编译它需要先装好 nasm。下载源码后进入目录执行./configure --prefix/d/ffmpeg-build/install \ --hostx86_64-w64-mingw32 \ --enable-static \ --enable-shared \ --disable-cli \ --extra-cflags-O2 -pipe这里解释几个关键参数。--host指定目标平台告诉 configure 这是交叉编译到 Windows 64 位。--enable-static和--enable-shared同时开启静态库方便链接进最终程序动态库方便调试。--disable-cli关掉命令行工具我们只要库不要那个x264.exe能省不少编译时间。--extra-cflags加上-O2优化和-pipe用管道代替临时文件加速编译。configure 跑完后make -j$(nproc) make install-j$(nproc)表示用所有 CPU 核心并行编译能大幅缩短时间。libx264 编译比较快几分钟就能搞定。3.4 libx265 的编译libx265 是 H.265/HEVC 编码器编译方式稍微不同它用 CMake 而不是 configure。先确认装了 cmakepacman -S mingw-w64-x86_64-cmake然后下载源码进入源码目录下的source子目录注意是 source 子目录不是根目录执行cmake -G MSYS Makefiles \ -DCMAKE_INSTALL_PREFIX/d/ffmpeg-build/install \ -DCMAKE_BUILD_TYPERelease \ -DENABLE_SHAREDOFF \ -DENABLE_CLIOFF \ -DHIGH_BIT_DEPTHON \ -DEXPORT_C_APION \ .-G MSYS Makefiles指定生成 MSYS 风格的 Makefile这是关键用默认的生成器可能会出问题。-DHIGH_BIT_DEPTHON开启高位深支持处理 10bit 视频需要。-DEXPORT_C_APION导出 C 接口FFmpeg 链接时需要。make -j$(nproc) make installlibx265 编译比较慢因为它的代码量比 x264 大得多耐心等一会儿。3.5 libfdk-aac 的编译libfdk-aac 是公认质量最好的 AAC 编码器但它的 license 和 FFmpeg 的 LGPL 不兼容所以官方预编译包从来不带它。自己编的话注意它的构建系统是 autotools但源码包里可能没有 configure 脚本需要先生成autoreconf -fiv ./configure --prefix/d/ffmpeg-build/install \ --hostx86_64-w64-mingw32 \ --enable-static \ --disable-shared make -j$(nproc) make installautoreconf -fiv会根据configure.ac重新生成 configure 脚本-f强制覆盖-i安装缺失的辅助文件-v显示详细过程。如果源码包里已经有 configure这步可以跳过。3.6 依赖库编译的通用避坑要点编了这么多库有几个坑是反复出现的这里集中说一下。第一个坑是pkg-config找不到刚装的库。原因是PKG_CONFIG_PATH环境变量没设对。在编译 FFmpeg 之前一定要执行export PKG_CONFIG_PATH/d/ffmpeg-build/install/lib/pkgconfig:$PKG_CONFIG_PATH这样 pkg-config 才能找到我们装到自定义前缀下的.pc文件。验证方法pkg-config --list-all | grep x264能列出x264就说明路径对了。第二个坑是静态库和动态库混用导致的链接错误。有些库默认只编静态库有些默认只编动态库FFmpeg 链接时如果找不到对应的库就会报 undefined reference。我的建议是统一都编静态库最终 FFmpeg 也编静态库这样产物是一个独立的.exe或.dll不依赖一堆外部 dll部署最省心。第三个坑是头文件路径。有些库安装时把头文件装到了install/include下的子目录里比如install/include/x264.h而有些装到了install/include/libfoo/foo.h。FFmpeg 的 configure 通过 pkg-config 获取头文件路径一般不会有问题但如果某个库没有提供.pc文件就需要手动用--extra-cflags指定头文件路径。4. FFmpeg 的 configure 参数详解与编译4.1 获取源码与目录准备FFmpeg 的源码建议从官方 git 仓库克隆这样能拿到最新的代码也方便切换版本cd /d/ffmpeg-build/sources git clone https://git.ffmpeg.org/ffmpeg.git cd ffmpeg如果网络条件不好也可以下载 release 版本的 tar 包。克隆完成后建议先切到一个稳定的 release 分支比如git checkout release/6.1避免 master 分支上的一些实验性改动导致编译问题。4.2 configure 参数逐条拆解FFmpeg 的 configure 脚本参数非常多./configure --help能列出一大屏。这里我挑最核心的几类参数讲清楚理解了这些你就能根据自己的需求组合出合适的配置。安装路径类--prefix/d/ffmpeg-build/output指定make install的安装位置。注意这里用的是 MSYS2 风格的路径不是 Windows 路径。交叉编译类--archx86_64 --target-osmingw32 --cross-prefixx86_64-w64-mingw32---arch指定目标架构--target-os指定目标操作系统Windows 下 MinGW 编译统一用mingw32即使是 64 位也是这个值这是历史遗留。--cross-prefix指定工具链前缀这样 configure 会去找x86_64-w64-mingw32-gcc而不是gcc。功能开关类--enable-shared --disable-static --enable-gpl --enable-version3 --enable-nonfree--enable-shared和--disable-static决定产物是动态库还是静态库。如果你要做的是给其他程序调用的库用动态库如果是独立的命令行工具用静态库。--enable-gpl开启 GPL 协议下的功能比如 libx264--enable-version3升级到 GPLv3--enable-nonfree允许链接非自由协议的库比如 libfdk-aac。注意这三个开关一旦开启你的产物就受相应协议约束商用前务必搞清楚 license 问题。外部库启用类--enable-libx264 --enable-libx265 --enable-libfdk-aac --enable-libmp3lame --enable-libvpx --enable-libopus每个--enable-libxxx对应一个外部库前提是这个库已经编译安装好且 pkg-config 能找到。如果 configure 报 libxxx not found先检查PKG_CONFIG_PATH和库是否真的装好了。裁剪类--disable-programs --disable-doc --disable-avdevice --disable-postproc --disable-swscale--disable-programs不编译 ffmpeg.exe、ffprobe.exe 这些命令行工具只要库。--disable-doc不编译文档省时间。后面几个是关掉不需要的模块能减小体积。但要注意关掉某些模块可能导致其他模块编译失败因为 FFmpeg 内部模块之间有依赖。裁剪要谨慎建议先全量编一遍跑通再逐步裁剪。4.3 一份可直接抄的完整 configure 命令把上面的参数组合起来一份适合大多数场景的配置如下./configure \ --prefix/d/ffmpeg-build/output \ --archx86_64 \ --target-osmingw32 \ --cross-prefixx86_64-w64-mingw32- \ --enable-shared \ --disable-static \ --enable-gpl \ --enable-version3 \ --enable-nonfree \ --enable-libx264 \ --enable-libx265 \ --enable-libfdk-aac \ --enable-libmp3lame \ --enable-libvpx \ --enable-libopus \ --disable-doc \ --extra-cflags-O2 -pipe \ --extra-ldflags-static-libgcc--extra-ldflags-static-libgcc这个参数值得单独说。MinGW 编译出来的动态库默认会依赖libgcc_s_seh-1.dll如果目标机器上没这个 dll程序就跑不起来。加上-static-libgcc把 libgcc 静态链接进去产物就不依赖这个 dll 了部署更省心。4.4 编译与安装configure 跑完后如果最后输出里没有 error就可以开始编译了make -j$(nproc)FFmpeg 的编译时间取决于你的配置和机器性能全量编译大概十几分钟到半小时不等。如果中途报错先看错误信息里是哪个文件、哪个函数大部分情况是某个依赖库没找到或者版本不匹配。编译完成后安装make install安装完成后去output目录下看看应该有bin、include、lib三个子目录。bin下是 dll 和 exeinclude下是头文件lib下是导入库.dll.a文件。4.5 验证编译产物验证分两步。第一步用命令行工具验证/d/ffmpeg-build/output/bin/ffmpeg.exe -version如果输出了版本信息并且 Configuration 那一行里能看到你启用的那些库--enable-libx264等说明编译成功。第二步验证编码功能/d/ffmpeg-build/output/bin/ffmpeg.exe -f lavfi -i testsrcduration5:size1280x720:rate30 -c:v libx264 -preset fast -crf 23 test.mp4这条命令用 FFmpeg 内置的测试源生成一段 5 秒的 720p 视频用 libx264 编码成 mp4。如果成功生成了test.mp4且能正常播放说明 libx264 链接正确、编码功能正常。提示验证时如果报 libx264-164.dll not found 之类的错误说明 dll 没在 PATH 里。把output/bin加到 PATH或者把 dll 拷到 exe 同目录下即可。5. 常见报错与排查技巧实录编译 FFmpeg 的过程中报错是常态一次就过的反而是少数。这一节我把这些年踩过的典型坑整理成速查表遇到问题先查表能省不少时间。5.1 configure 阶段报错报错信息原因解决方法nasm/yasm not found汇编器没装pacman -S mingw-w64-x86_64-nasm mingw-w64-x86_64-yasmlibx264 not foundpkg-config 找不到库检查PKG_CONFIG_PATH是否包含install/lib/pkgconfigC compiler test failed工具链配置错误检查--cross-prefix是否正确gcc --version能否运行ERROR: libfdk_aac not found库没装或版本不匹配确认库已make install且.pc文件存在configure 阶段的报错90% 都是依赖库找不到。排查思路很简单先确认库装没装去install/lib下看有没有对应的.a或.dll.a再看 pkg-config 能不能找到pkg-config --exists libx264 echo ok最后看路径对不对。5.2 编译阶段报错编译阶段的报错五花八门但有几类是高频的。undefined reference to xxx链接错误说明某个函数声明了但没实现。常见原因是库的链接顺序不对或者静态库和动态库混用。解决方法是检查--extra-ldflags里的库顺序被依赖的库放在后面。error: xxx undeclared头文件缺失或版本不匹配。比如某个库的新版本改了 API而 FFmpeg 的代码还是按老 API 写的。解决方法是换用 FFmpeg 官方推荐的库版本或者打补丁。internal compiler error编译器内部错误通常是 GCC 的 bug 或者内存不足。尝试降低优化等级把-O2改成-O1或者减少并行编译的 job 数。5.3 运行阶段报错编译成功不代表能跑运行阶段也有坑。找不到 dll前面提过用-static-libgcc解决 libgcc 依赖其他第三方库的 dll 要么静态链接要么拷到 exe 同目录。程序崩溃或功能异常最常见的原因是运行时库不匹配。比如用 MINGW64 编译的库却链接了 UCRT64 编译的库两者的 C 运行时不同跨模块传递 FILE 指针或 malloc 的内存就会崩。解决方法是所有库用同一个环境编译。编码结果异常比如视频花屏、音频杂音。这通常是某个库的版本和 FFmpeg 不兼容导致的。FFmpeg 官方文档里有推荐的库版本尽量按推荐版本编译。5.4 几个独家避坑技巧第一个技巧编译前先make distclean。如果你改了 configure 参数重新编译一定要先清理否则旧的配置缓存会导致各种诡异问题。make distclean比make clean更彻底连 configure 生成的 Makefile 都删掉。第二个技巧把编译命令写成脚本。每次手动敲一长串 configure 参数容易出错写成一个build.sh参数改起来也方便。我的习惯是每个库一个脚本FFmpeg 一个脚本按顺序执行。第三个技巧保留编译日志。make -j$(nproc) 21 | tee build.log这样报错时能翻日志找上下文比在终端里往上滚屏方便得多。第四个技巧用ccache加速重复编译。如果你需要反复调整 configure 参数重新编译装个 ccache 能大幅缩短时间pacman -S mingw-w64-x86_64-ccache export CCccache x86_64-w64-mingw32-gccccache 会缓存编译结果第二次编译同样的代码直接命中缓存速度快好几倍。6. 产物集成与后续扩展6.1 在 C/C 项目中使用编译产物编译出来的 FFmpeg 库集成到自己的项目里无非就是配头文件路径和库路径。以 CMake 为例set(FFMPEG_ROOT D:/ffmpeg-build/output) include_directories(${FFMPEG_ROOT}/include) link_directories(${FFMPEG_ROOT}/lib) target_link_libraries(your_target avcodec avformat avutil swscale swresample)注意链接顺序avcodec依赖avutil所以avcodec要放在avutil前面。如果用的是动态库运行时还要确保 dll 在 PATH 或 exe 同目录下。6.2 在 C# 中通过 P/Invoke 调用C# 调用 FFmpeg 的 dll需要用DllImport声明函数。比如调用avcodec_version[DllImport(avcodec-60.dll, CallingConvention CallingConvention.Cdecl)] public static extern uint avcodec_version();注意CallingConvention.CdeclFFmpeg 的 API 都是 cdecl 调用约定不指定的话默认是 stdcall会导致栈不平衡崩溃。另外 dll 名字里的版本号60要和你编译出来的实际文件名一致。6.3 裁剪体积的进阶玩法如果你的项目对体积敏感比如要做成安装包分发那 FFmpeg 的全量库动辄几十 MB 就太大了。裁剪的思路有两个一是关掉不需要的编解码器二是关掉不需要的模块。关编解码器用--disable-encoderxxx和--disable-decoderxxx比如你只需要 H.264 解码那就--disable-everything \ --enable-decoderh264 \ --enable-parserh264 \ --enable-demuxermov \ --enable-protocolfile--disable-everything先把所有东西关掉然后按需开启。这样编出来的库可能只有几百 KB。但要注意--disable-everything之后要手动开启的东西很多包括 demuxer、parser、protocol 等漏一个就跑不起来。建议先用--disable-everything编一遍跑测试用例报什么错就开什么逐步补全。6.4 交叉编译到 ARM64 Windows现在 Windows on ARM 设备越来越多如果需要编译 ARM64 版本的 FFmpegMSYS2 也提供了对应的工具链pacman -S mingw-w64-clang-aarch64-toolchain然后在 CLANG64 环境下把--cross-prefix改成aarch64-w64-mingw32-其他参数基本不变。不过 ARM64 的第三方库生态还不如 x64 完善有些库可能没有现成的 ARM64 版本需要自己从源码编。6.5 版本升级与重新编译FFmpeg 更新很频繁第三方库也会更新。当你需要升级某个库时流程是下载新版本源码重新 configure、make、make install然后重新编译 FFmpeg。注意升级库之后FFmpeg 一定要make distclean后重新 configure因为 configure 会缓存库的版本信息不清理的话可能还在用旧的配置。我个人的习惯是每半年左右更新一次整个工具链和依赖库保持版本相对较新但不会追最新的 master因为新版本可能引入不兼容的改动。稳定优先除非新版本有你需要的关键特性。最后分享一个小技巧把整个编译过程容器化或者脚本化用 CI 自动跑。这样每次升级只需要改版本号剩下的交给流水线省心又不容易出错。我自己维护了一套编译脚本从下载源码到产出成品全自动需要的时候改个版本号就能出一套新的库。这套流程跑顺了之后编译 FFmpeg 这件事就从折腾变成了例行公事。