恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Win10下编译ijkplayer完整指南:MSYS2+NDK避坑实战
首页
资讯中心
/
Win10下编译ijkplayer完整指南:MSYS2+NDK避坑实战
Win10下编译ijkplayer完整指南:MSYS2+NDK避坑实战
发布时间:2026/10/13 22:02:29
ijkplayer在Win10下编译这个话题我印象太深了。去年有个项目需要给Android端集成一套支持RTSP协议的视频播放器开发环境全是Windows我硬着头皮在Win10上编译ijkplayer前前后后折腾了三天中间踩过的坑用一只手数不过来。这个项目本身是为移动端设计的官方构建链路全是bash脚本Windows下要跑通环境、版本、配置每一步都有不少讲究。这篇就把我实际遇到的问题、排查过程、最终可行的方案完整记录下来给正好在做同样事情的你一个参考。不管你是第一次听说ijkplayer还是已经在Win10上被报错折磨这篇应该都能帮到你。1. 先把编译这件事的来龙去脉说清楚1.1 你真正要编译的是什么很多人第一次在Win10下碰ijkplayer上来就按网上的教程跑compile-ffmpeg.sh结果报错以后整个人都是懵的。我建议先弄清楚自己在干嘛。ijkplayer本质上是一个基于FFmpeg的跨平台播放器框架代码仓库里包含了三块东西一套定制过的FFmpeg源码、一个核心播放引擎ijkplayer、还有Android和iOS的封装层。在Win10环境下编译绝大多数场景其实是编译Android版本的ijkplayer最终产物是几个so文件比如libijkffmpeg.so、libijkplayer.so、libijksdl.so供Android工程集成使用。这里的“编译”动作准确说是通过NDK在Windows上做交叉编译在x86架构的Windows开发机上面生成ARM架构的Android动态库。这个逻辑一旦清楚后面遇到报错你就知道该往哪个方向排查而不是瞎试。1.2 官方编译链路为什么要绕一个MSYS2ijkplayer的编译没有提供纯Windows的原生脚本官方构建流程是用一系列bash脚本串起来的。整个流程分三步init-android.sh负责拉取子模块compile-ffmpeg.sh负责交叉编译FFmpegcompile-ijk.sh负责把核心引擎和JNI层一起编成so。这些脚本里用的工具链、sed、awk、make等在Windows原生CMD或PowerShell里都跑不了。这时就需要MSYS2提供一个类Linux的运行时环境。它不是虚拟机而是一套在Windows上运行的GNU工具集合自带bash、make、gcc等。我实际用下来MSYS2的兼容性比Git Bash好很多Git Bash里很多路径转换问题很别扭MSYS2对configure脚本的兼容度明显更高。这里有个关键点编译Android版ijkplayer的NDK工具链本身就支持在Windows上运行所以在MSYS2里调用NDK里的交叉编译器是行得通的。1.3 先想清楚要不要走这条路动手之前不妨先问自己一句你的目标平台到底是什么。如果你的目标是Android端播放器ijkplayer在Win10下编译是一条可行的路目前资料相对齐整照着做能跑通。但如果你是想在Windows桌面程序里集成ijkplayer那我劝你趁早放弃。官方一直没有提供Windows桌面版的分支强行把Android的so用到桌面是不可能的网上看到的“Windows版ijkplayer”基本都是个人魔改不稳定也不维护。另一个现实建议如果能用Linux环境编译哪怕是虚拟机或者一台Linux服务器都会比在Win10下省心不止一倍。我后来把编译放到Linux环境后几乎没有遇到环境层面的问题。但现实中确实有很多公司开发环境固定是Windows不允许随便架服务器那这篇的解决方案就有用了。2. 环境准备MSYS2与NDK直接决定成败2.1 MSYS2安装与基础依赖环境准备这块是整个编译过程里最值得花时间的地方因为后面的所有报错有很大比例都能回溯到环境没配好。先去MSYS2官网下载安装包装的时候保持默认即可。装完以后打开MSYS2 MSYS终端第一件事是更新软件源和系统组件pacman -Sy pacman -Su注意如果长时间没更新直接pacman -Su可能会报错需要先把pacman -Sy跑一遍把数据库同步到最新再升级。升级完之后安装编译需要的工具pacman -S make yasm diffutils pkg-config gityasm是汇编器FFmpeg编译时处理汇编代码必须用到少了它后面必然报错。diffutils提供diff、cmp等命令configure脚本运行时会依赖它们。make就不用说了整个编译过程的驱动器。这里还有一个容易被忽略的点MSYS2安装完以后桌面上会出现MSYS2 MSYS、MSYS2 MINGW64等好几个不同入口的终端。我推荐固定使用MSYS2 MSYS这个终端来执行编译命令不要混着用。MINGW64终端里默认带的是MinGW的工具链环境变量跟我们要的Android交叉编译工具链混在一起容易把路径探测带偏出现一些很奇怪的报错。2.2 NDK版本Win10编译的第一个分水岭NDK版本的选择可以说是整个编译过程中最敏感的一个变量。ijkplayer的官方编译脚本默认假设NDK里存在独立的GCC交叉编译工具链而且对工具链的目录结构、名称前缀都有硬编码。比如编译ARM平台时脚本会去找类似arm-linux-androideabi-gcc这样的可执行文件。关键问题在于NDK r17开始官方彻底移除了GCC只保留ClangNDK r18之后目录结构也做了大幅调整。如果你直接下载最新版的NDK去跑ijkplayer的脚本大概率会卡在工具链找不到这一步会出现类似checking for arm-linux-androideabi-gcc... no的报错然后configure就失败了。我实测下来最稳的组合是ijkplayer 0.8.8 NDK r10e。这个组合是当年官方文档推荐的经典搭配编译脚本和NDK之间的匹配度最高几乎不需要改任何东西。如果NDK r10e下载不到r13b、r14b也可以用但建议优先找r10e。可能有人会问用新版NDK难道不行吗也不是完全不行但你要去改编译脚本让它适配Clang工具链和新的目录结构这个过程会牵扯到FFmpeg configure参数、JNI的Application.mk等等复杂度一下就上去了。我不建议为了追求“新版”而在编译上折腾自己工具链就是个生产工具能顺利产出so的就是好工具。2.3 环境变量的正确写法NDK装好之后需要把路径告诉编译脚本这里有一个非常典型的Win10坑路径写法。在MSYS2环境里Windows的盘符路径要写成/d/这种形式而不是D:\。如果你直接写export ANDROID_NDKD:\android-ndk-r10e后面任何脚本里只要执行cd $ANDROID_NDK都会因为反斜杠和后缀冒号的问题直接失败报一个完全看不出来原因的目录不存在错误。正确的是这种写法export ANDROID_NDK/d/dev/android-ndk-r10e export ANDROID_SDK/d/dev/android-sdk export PATH$PATH:$ANDROID_NDK另外路径里千万不要有空格和中文。我见过有人把开发工具放在C:\Program Files\android-ndk-r10e下面这种路径在MSYS2里处理起来极其痛苦各种转义问题层出不穷。最好的做法是在某个盘的根目录下建一个纯英文、无空格的目录比如D:\dev\把NDK和SDK都放进去。这个钱省不得其实就是为了少踩坑。2.4 给新手的建议如果你是第一次在Win10下编译ijkplayer我的建议是先别急着追求完整功能先跑通一个最小编译链路。具体来说先只编译armv7a一个架构关闭所有用不到的解码器和协议成功产出第一个so之后再去扩展其他ABI和功能。如果你直接把所有ABI和所有模块全部打开编译时长会和报错排查难度都大幅上升。另外FFmpeg编译对CPU和内存的要求不低编译时可以顺便干别的但尽量别跑到内存爆掉。我自己的机器16G内存编译单架构时基本能吃满四核CPU大概持续十几分钟。3. 拉取源码与配置裁剪少踩一个是一个3.1 仓库与子模块环境准备好之后开始拉源码。ijkplayer的官方仓库包含了主项目代码但FFmpeg是作为一个子模块存在的所以必须用--recursive参数拉取git clone --recursive 官方仓库地址如果忘记加--recursive参数拉完之后你会发现ffmpeg目录是空的。补救方法是进到目录里执行git submodule update --init。由于FFmpeg子模块体积很大尤其是从国外托管平台拉取时速度慢、中途断掉都是常态。遇到网络中断不要慌重新执行子模块更新命令它会断点续传。我遇到过几次拉取中断的情况反复执行git submodule update --init --recursive多试几次最终会拉全。还有一个细节拉完子模块后默认会处于某个特定的提交点。如果你发现拉下来的FFmpeg版本跟编译脚本预期的对不上编译时会出现奇奇怪怪的接口缺失报错。需要检查一下版本的一致性。最稳妥的方式就是不要自己切换分支或随意升级子模块保持仓库默认状态这是编译成功的重要保障。3.2 module.sh裁剪不要什么都开ijkplayer默认的模块配置把所有能开的解码器、协议、滤镜几乎都打开了。如果照默认配置直接编FFmpeg的configure阶段和make阶段都会非常耗时而且很容易在链接阶段出现内存不足的问题。实际项目里你大概率用不了里面那么多功能裁剪是必须的。我需要先说明我的配置基于常见实践补充打开config/module.sh能看到很多export COMMON_FF_CFG_FLAGS$COMMON_FF_CFG_FLAGS --enable-xxx这样的行。把不需要的行注释掉即可。以我当时的项目为例我需要RTSP、RTMP、HLS这三种协议以及H.264、HEVC解码那我就保留对应的开关其他全部注释掉。这个文件里其实有很多是注释状态需要你自己按需打开或关闭。最关键的是要理解一点这里的开关最终会变成FFmpeg configure的参数配置越精简编译越快、产物越小但也会带来兼容性问题比如以后遇到某种封装格式发现播不了就得回来重新开对应的demuxer再编一次。所以我建议裁剪不要太激进把常用的格式容器mp4、flv、mkv的解复用器和常用的音频编码AAC、MP3保留宁可稍微多编一点也不要等上线以后发现少个什么解码器到时候重新编译又要折腾一轮。3.3 换行符这个隐形杀手这个坑我敢说十个在Windows下编译的人至少五个会碰到而且报错信息非常有迷惑性。Windows下用Git拉取代码时如果Git的core.autocrlf配置为trueGit会自动把仓库里的LF换行符转换成CRLF换行符。问题来了bash脚本文件里如果行尾是CRLF脚本在执行时会报类似$\r: command not found的错误或者脚本里每一行都莫名其妙的执行失败。我第一次遇到时完全没反应过来以为是代码文件坏了还重新clone了好几次。解决办法git config --global core.autocrlf false配置好之后把拉下来的目录删掉重新clone一遍确保所有文件都是LF换行。如果不想重新拉取也可以用dos2unix工具批量转换脚本文件的换行符pacman -S dos2unix find . -name *.sh -exec dos2unix {} \;我个人的建议是两种方法结合先设置core.autocrlf false再检查一下关键脚本的换行符。这个坑属于典型的环境问题光看报错信息很难联想到换行符上去但提前处理掉能省很多事。4. 编译全流程实录三条命令背后的真实挑战4.1 init-android.sh拉子模块的那几分钟环境配好、源码拉好后正式编译的第一步是运行./init-android.sh这个脚本主要做两件事检查子模块是否完整以及在需要时切换分支到正确的版本。正常情况下这个过程很快几分钟内完成。如果前面子模块拉取不完整脚本在这里就会报错提示缺少某个目录或某个文件这时候回到3.1节把子模块补齐再重跑。这里提醒一句init脚本执行时依赖git命令但MSYS2自带的git版本可能和你Windows上安装的Git会冲突。我遇到过在MSYS2里执行git时调用到Windows版Git的情况导致路径转换有问题。如果你碰到类似情况检查一下which git指向的是哪个路径确保用的是MSYS2环境里的git。4.2 compile-ffmpeg.sh最长最难啃的一步接下来是最核心、也是最容易出问题的一步./compile-ffmpeg.sh这个脚本默认会编译所有ABI你可以通过传入参数来指定只编某个架构。比如只编armv7a./compile-ffmpeg.sh armv7a脚本做的事可以理解为“用NDK里的交叉编译工具链把FFmpeg源码配置、编译、安装到指定目录”。中间分为三个阶段configure、make、make install。configure阶段主要检测交叉编译环境和各项特性这个阶段如果报错在ffmpeg-armv7a/目录下会生成一个config.log文件里面记录了详细的探测过程和失败原因。这个文件是我排查问题最重要的依据报错时第一反应应该去找它而不是盯着终端输出猜。make阶段是真正编译源码的阶段耗时最长。以我的机器为例单架构编译十五到二十分钟很常见。如果编译中途报错修改配置后重新编译时强烈建议先执行make distclean把上一次编译产生的缓存文件清理干净再说。不然你改了配置但configure的缓存还在它可能继续沿用旧配置导致你改了个寂寞问题复现后还误以为是别的原因。这个阶段还有一个Windows平台特有的问题杀毒软件的实时防护。编译FFmpeg时会生成海量的小文件并频繁读写Windows Defender的实时扫描有时会干扰这个过程表现为某些文件被占用、报文件找不到甚至编译进程被莫名其妙终止。如果你编译总在某个固定位置报错且日志看不出原因可以试试临时关闭Windows Defender的实时保护或者把编译目录加入白名单。这个操作不常有但遇到过一次就知道它多耽误时间。4.3 compile-ijk.shJNI层的坑FFmpeg编译通过之后运行./compile-ijk.sh这一步是编译ijkplayer核心引擎和JNI层产出最终的so文件。它内部实际上是调用ndk-build来构建位于android/ijkplayer目录里的NDK工程。这一步常见的报错集中在找不到NDK路径、JNI头文件路径不对、Application.mk里指定的平台版本与NDK不兼容等。大部分情况下如果前面2.3节的环境变量配置正确NDK版本合适这一步的通过率还是挺高的。这里有一个小经验compile-ijk.sh会为不同ABI分别执行ndk-build如果某个ABI编译失败它会中断整个流程。排查时可以先把android/ijkplayer下的所有ABI中间产物清理掉再对单一ABI重新执行ndk-build比如cd android/ijkplayer $ANDROID_NDK/ndk-build NDK_PROJECT_PATH. NDK_OUT./obj NDK_APPLICATION_MK./Application.mk APP_ABIarmeabi-v7a这种方式比反复跑整个脚本更容易定位问题。4.4 编译产物在哪、怎么验证全部编译成功后产物目录在android/ijkplayer/ijkplayer-armv7a/src/main/libs/具体路径按你的ABI和模块名会略有不同里面应该有libijkffmpeg.so、libijkplayer.so、libijksdl.so等文件。拿到so之后我建议先用file命令看一眼架构信息是否对file libijkplayer.so正常输出里会包含ARM、EABI、shared object等字样如果显示的是x86或者链接到不存在的库说明编译链路有问题这样的so包即使集成到Android工程里也跑不起来。还有一个细节如果打算多个ABI一起用注意产物目录里的so文件是否被后一次编译覆盖。默认脚本输出目录是分开的但如果手动改过输出路径就容易出现混淆。我习惯每个ABI编译完先把so文件复制到单独的备份目录所有ABI编完后统一拷贝这样不会搞混。5. 高频报错速查表与经验建议5.1 高频报错速查表我把编译过程中遇到的、以及周边同事和网友反馈频率最高的几类报错整理成一张表方便你对照排查。报错特征常见原因解决方案ANDROID_NDK not set环境变量没配置或未在当前会话导出按2.3节配置环境变量注意MSYS2路径写法$\r: command not found脚本换行符被Windows Git转为CRLF设置core.autocrlf false后重新拉取或dos2unix转换checking for arm-linux-androideabi-gcc... noNDK版本过新GCC工具链被移除换用NDK r10e/r13b/r14byasm not found缺少汇编器pacman -S yasmmake: command not found未安装基础工具pacman -S makeffmpeg configure error裁剪配置冲突或子模块版本不对查看config.log日志确认模块开关是否逻辑矛盾Could not find method compile()Gradle版本过新DSL语法已变更在Android工程里把Gradle版本降到3.x或改用implementationFailed to find Build Tools revision 23.0.3Android SDK中未安装对应Build Tools版本在SDK Manager中安装对应版本或调整工程里的版本号编译进程被终止/文件被占用Windows Defender实时保护干扰临时关闭实时保护或添加目录白名单这里面最有价值的其实是第一类和第三类它们占了我排查时间的七成以上。你如果遇到报错别急着Google报错原文先对号入座看看是不是版本组合问题往往比自己瞎调快得多。5.2 我的几条经验建议第一不要死磕版本升级。ijkplayer这类型的老牌开源项目官方代码更新并不频繁社区积累的经验大多基于某个特定版本组合。我把ijkplayer的FFmpeg子模块保持官方默认版本不试图升级到新版FFmpeg换来的是省心。播放器解码能力也够用不存在“必须用新版FFmpeg”的需求。第二编译目录的路径一定要干净。前面提到不能用中文、空格这里再补充一点尽量避免放在桌面或系统盘的深层目录因为NDK工具链里有些工具对路径长度和特殊字符敏感路径一长就可能莫名失败。我后来把编译目录统一放在D:\work\下并且所有工程名都用短英文之后再也没出现过路径类报错。第三备份编译配置。当你终于把module.sh和各种参数调到一个能稳定编译的状态立刻备份这个目录。我当时是把整个编译目录在另一个盘做了一份拷贝后面无论环境怎么换、代码怎么改随时可以回到这个基准。没有这个备份以后再被某个新改动坑一次又要从头排查。第四真的不要忽略CI的作用。如果你所在团队有CI环境优先把ijkplayer编译丢到Linux的CI上Windows本地只做开发调试。CI每改动一次配置可以自动重新出包省去大量的本地重复操作。这个建议不针对技术能力纯粹是从效率角度考虑的。5.3 编译成功之后编译成功只是第一步so文件是要和Java层代码配套使用的。ijkplayer的Android目录下提供了完整的上层封装包括IjkMediaPlayer这样的Java类。你做集成时把so放到app模块的src/main/jniLibs/对应abi目录下再引入ijkplayer的java代码之后的调用流程就和平常使用标准播放器接口一样了。有一点要提前提醒你没有裁剪的模块最终会直接体现在包体积上。如果你开了解码器全家桶一个ABI下的so体积可能好几十MB三个ABI加起来直接让安装包膨胀。这也是为什么我一直强调要裁剪。相反如果裁剪得当三个ABI的总so体积可以压到十几MB到二十几MB对于很多工具类应用来说是可以接受的。我个人现在的习惯是只要条件允许都会把ijkplayer的编译丢到Linux环境去处理Windows下能编译成功但这个环境的前提条件太多。如果你已经被某个报错卡住不妨把我上面说的版本组合当基准去对齐MSYS2、NDK r10e、关闭core.autocrlf、裁剪module.sh。先还原到最小可用状态再逐步增加功能这比对着报错一条条猜有效得多。