news 2026/10/13 23:03:52

Win10下编译ijkplayer全流程:环境配置、NDK踩坑与so库生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Win10下编译ijkplayer全流程:环境配置、NDK踩坑与so库生成

1. 写在前面:Win10上编译ijkplayer会遇到什么问题

先说结论:在Win10下编译ijkplayer确实能成功,但过程远没有官方文档写的那么顺滑。我前后折腾了差不多三天,真正执行编译可能只花了半小时,其余时间全部在跟各种奇奇怪怪的环境问题作斗争。这篇文章不打算复述官方文档,而是完整记录我在Win10下编译ijkplayer踩过的坑、试过的方案、改过的配置,以及最后稳定的编译路径。

ijkplayer是什么,这里先给不熟悉的读者交代一下。它是国内一家视频平台开源的多媒体播放器内核,核心基于FFmpeg,针对Android和iOS做了大量优化。它相比直接用FFmpeg裸调的好处在于封装好了播放控制、音视频同步、硬解切换等逻辑,同时保留了自定义底层解码器的灵活性。做直播、点播、短视频、甚至智能硬件上需要嵌入式播放能力的场景,ijkplayer都是一个绕不开的选项。

我这次的目标环境很明确:Win10 64位系统,编译目标是Android平台下的armv7a架构的so库,最终集成到一个模拟项目X(一个用来做视频回放功能的demo App)里。这个任务在过去通常是在Linux或者macOS上完成的,Win10下的资料相对零散,而且网上很多帖子已经过时了——官方推荐的是NDK r10e,但如今很多人装的是新版NDK,直接套老教程就会撞得头破血流。

如果你跟我一样,只有一台Windows电脑,又要编译ijkplayer,那么这篇文章就是为你写的。我会从环境搭建、版本选择、编译配置、常见报错排查到最终集成,完整走一遍流程。全程操作可复现,所有命令语句都会给全。

2. 环境搭建与工具链准备

2.1 使用MSYS2而不是Cygwin

编译ijkplayer需要一套类Linux的Shell环境,Win10下最常见的方案是MSYS2或者Cygwin。我直接用MSYS2,原因很简单:ijkplayer官方脚本就是针对MSYS2环境做的适配,同时它的包管理器pacman用起来非常顺手,依赖缺失时安装效率远高于Cygwin的setup.exe手动勾选。

MSYS2安装没什么难度,从官网下载64位安装包,安装路径我建议直接放C:\msys64。注意别放到带空格的目录,比如C:\Program Files\msys64,后面编译脚本对路径解析很敏感,空格和中文都会出幺蛾子。

安装完成后,先把MSYS2的包管理器源更新到可用状态。这一步在首次安装后必须做,否则后面pacman装包容易报错。打开MSYS2终端,依次执行:

pacman -Syu

如果提示需要关闭终端,就关掉重开,再执行一次,确保核心包全部更新完毕。然后安装编译需要的基础工具包:

pacman -S git make diffutils yasm pkg-config unzip

这里每个包都有用,我简要说明一下。git用来拉代码,make是编译驱动,diffutils提供diff等命令,一些自动配置脚本会用到,yasm是FFmpeg汇编代码的编译器,缺了它FFmpeg编译会直接中断,pkg-config用于检查第三方库依赖,unzip处理脚本自动解压下载的压缩包。这些包缺哪个、补哪个的原则也是后面排查问题的主要思路。

2.2 NDK版本选择是成败关键

ijkplayer官方README里特别强调使用NDK r10e,这绝不是随便写的。原因在于,ijkplayer的编译脚本依赖NDK中附带的gcc交叉编译器,而从NDK r11开始,Google逐步用clang取代gcc,到r13之后gcc基本上就找不到了。如果你装了新版NDK,执行编译脚本时最常见的报错就是找不到arm-linux-androideabi-gcc,或者出现一堆奇怪的C编译器测试失败。

我实测下来,NDK r10e是最稳妥的版本,android-ndk-r10e的Windows 64位版本解压后约几个GB,直接解压到C:\android-ndk-r10e即可。如果你因为某些原因必须使用新版NDK,也可以试试手动在FFmpeg配置时强制指定clang,但这条路极其折腾,我试过之后还是换回r10e,把精力留给了真正要解决的问题。

NDK解压后,还有一步容易被忽略:老的NDK工具链是32位的,在64位系统上需要32位兼容库支持。Win10上如果缺失,编译时会提示无法启动cc1.exe之类的错误。如果你用的是较精简的Windows系统,可能需要安装“适用于Linux的Windows子系统”的某些依赖,但最直接的判断方法是运行arm-linux-androideabi-gcc -v,如果能正常输出版本信息,说明工具链可用。

2.3 SDK、JDK与路径规划

编译完成后要打包成Android库,还需要Android SDK和JDK。SDK版本不必太新,我用的API Level 26,Android Studio里编译SDK版本可以最后通过Gradle配置调整。JDK建议用JDK 8,ijkplayer项目生成的Gradle版本比较老,新版JDK会出现兼容性问题。

MSYS2环境里需要配置几个环境变量。我把这一段写在用户的环境变量里,也可以在每次编译前用export声明。

export ANDROID_NDK=/c/android-ndk-r10e export ANDROID_SDK=/c/Android/Sdk export PATH=$PATH:$ANDROID_NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin

这里再强调一次:整个工程源码的存放路径不要有中文、不要有空格。我一开始把项目放在D:\我的项目\ijkplayer,结果执行脚本时各种路径解析失败,改成D:\workspace\ijkplayer后所有类似问题都消失了,非常无解但非常真实。

3. 核心编译流程与关键配置

3.1 拉取代码与切换分支

编译时我建议直接拉取全部分支,再切到0.8.8分支。ijkplayer的master分支更新频率不高,但有些脚本细节与0.8.8有差异,官方文档中针对Windows编译的说明也多基于0.8.8。

git clone https://github.com/bilibili/ijkplayer.git cd ijkplayer git checkout -B latest 0.8.8

这一步拉下来的代码里,android/contrib目录下保存了FFmpeg的编译脚本和相关配置。接下来进入这个目录初始化FFmpeg源码:

cd android/contrib ./init-android.sh

这个脚本会去下载FFmpeg源码。问题来了:由于网络环境的限制,这个下载过程经常会中途失败。脚本是逐个模块拉取的,失败了再执行一次即可,但每次重新执行都会重新遍历,非常耽误时间。如果下载老是失败,可以考虑手动下载FFmpeg源码包,放到指定目录后跳过脚本下载逻辑,具体的做法可以看我在第4节问题排查中的记录。

3.2 定制 module.sh 选择功能模块

ijkplayer的android/contrib下有一个module.sh文件,定义你需要的FFmpeg编译模块。默认配置里包含了常见的协议和编解码器,但有些格式比如RTSP、HLS切片等可能需要自行打开。

我这次的项目需要播放RTSP流,所以手动修改了module.sh,增加了几行配置:

export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtsp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"

想查看所有可用开关,可以看FFmpeg源码目录下的configure帮助;想精简体积,则在module.sh里去掉对应的--enable-demuxer或--enable-decoder即可。这里要注意,每种协议往往不只是单独一个开关,还涉及依赖的复用器、解析器和协议栈,比如RTSP依赖TCP、UDP和RTP协议支持,如果只加了--enable-protocol=rtsp而缺少其他依赖,编译能通过但运行时会报协议找不到。

3.3 编译FFmpeg和生成so库

模块配置完成后,依次执行编译脚本:

cd android/contrib ./compile-ffmpeg.sh armv7a

这一步耗时最长,取决于CPU性能,我这边大概跑了20分钟左右。脚本会先清理旧的构建产物,然后调用FFmpeg的configure检测环境,接着用make并行编译。在MSYS2环境下,如果之前的环境变量配置正确,这一步通常不需要手动干预。

FFmpeg编译完成后,回到ijkplayer的Android工程目录,编译各个架构的so库:

cd android ./compile-so.sh armv7a

compile-so.sh做的事情是先用Gradle编译ijkplayer的Java层,再把刚刚编译出来的FFmpeg动态库与JNI层链接,最终生成带完整播放功能的so文件。这里需要注意,compile-so.sh执行时会调用gradle命令,如果你的系统里没有安装Gradle,脚本会自动下载,这一步同样容易失败。

3.4 编译产物的最终集成

所有编译完成后,在android/ijkplayer/ijkplayer-armv7a/src/main/libs/目录下会出现libijkffmpeg.so、libijkplayer.so和libijksdl.so三个核心库。对着官方文档中的工程结构,把ijkplayer-armv7a模块导入Android Studio即可。

导入时有个小坑:直接导入整个android/目录会加载多个模块,包括ijkplayer-example示例工程和翻遍架构的多个变体,一旦某个模块的SDK版本和你本地的Gradle配置不匹配,整个工程都会标红。更稳妥的做法是新建一个空工程,然后通过File -> New -> Import Module只导入ijkplayer-armv7a这个目录,依赖关系简单,排查问题也方便。

导入后还需要在Application模块的build.gradle中增加依赖:

implementation project(':ijkplayer-armv7a')

同时需要把compileOptions设为Java 8兼容,否则运行时会出现方法引用错误。这么做之后,就能在代码里通过IjkMediaPlayer类来创建播放器,并调用setSurface、prepareAsync等接口。

4. 编译期常见问题与排查实录

4.1 NDK版本过高导致的编译器缺失

这是我见过最多人踩坑的地方。具体现象是执行compile-ffmpeg.sh armv7a后,屏幕上出现arm-linux-androideabi-gcc: command not found或者C compiler test failed。

排查思路很简单:先确认自己的NDK版本,然后在终端里手动执行:

arm-linux-androideabi-gcc -v

如果没有输出,说明工具链路径没有配置好,或者这个NDK版本根本不做gcc。前者就检查PATH,后者就只能换回NDK r10e。我遇到过一种特殊情况,NDK版本是对的,但工具链的prebuilt目录名不是windows-x86_64,而是windows,需要自己对一下实际路径再改环境变量。

4.2 yasm未安装导致FFmpeg编译中止

FFmpeg编译时,yasm是硬依赖,主要用于汇编级优化。如果缺失,你会看到类似nasm/yasm not found or too old的提示,随后configure步骤直接终止。

解决方法很简单:

pacman -S yasm

这里有一个MSYS2特有的坑:如果MSYS2的源没有更新成功,pacman -S yasm会出现找不到包的错误。先执行pacman -Syu,再安装,基本就不会有问题了。

4.3 脚本换行符问题:$'\r': command not found

在Win10下使用git拉取代码后,脚本文件很可能被自动转换成CRLF换行符。MSYS2终端执行bash脚本时,会把\r当作命令的一部分,于是报出$'\r': command not found。这个问题第一次碰到时很懵,因为脚本看起来完全正常。

解决方法有两种。一种是安装dos2unix工具,把所有脚本批量转换:

pacman -S dos2unix find . -name "*.sh" -exec dos2unix {} \;

另一种更推荐的做法,是在拉代码之前先配置git关闭自动换行转换:

git config --global core.autocrlf false

然后再重新clone一遍工程。这个配置对后续所有脚本执行都是利好,尤其是跨平台开发场景下可以减少大量未知的诡异问题。

4.4 下载中断与源码包缺失

init-android.sh拉取FFmpeg源码的过程经常因为网络原因中断。脚本本身如果下载失败并不会回滚,所以genernal目录下可能残留不完整的文件,导致后续步骤报出configure找不到源码目录的错误。

我这边采用的替代方案是手动下载并放置源码包。首先看一下init-android.sh里面对应的FFmpeg版本号,比如0.8.8分支对应的是FFmpeg 4.0.x系列。然后在FFmpeg官网下载对应版本的源码tar.gz包,把压缩包放到android/contrib目录下,再执行:

./init-android.sh

脚本检测到本地已有源码包后会自动解压,跳过网络下载步骤。这样做比直接修改脚本简单很多,而且版本匹配性有保障。

如果这一环节卡住,那么后续的FFmpeg编译基本无法进行,所以建议越早处理越好。

4.5 Gradle构建时找不到SDK

执行compile-so.sh或后续导入Android Studio时,很容易遇到SDK location not found。这时需要在android/目录下手动创建local.properties文件,写入本机SDK路径:

sdk.dir=C:\\Android\\Sdk ndk.dir=C:\\android-ndk-r10e

注意这里要写双反斜杠转义,或者使用正斜杠C:/Android/Sdk,否则Gradle解析路径时会有问题。

4.6 编译内存不足或进程卡死

FFmpeg的并行编译对内存有一定要求,4GB内存的机器在make阶段有概率出现进程被杀或直接卡死。如果遇到这种情况,可以修改compile-ffmpeg.sh脚本中的make参数,把-j4改成-j2,牺牲并行度换取稳定性。

另外,MSYS2在Win10上偶尔会与Windows Defender冲突,编译过程中杀毒软件扫描临时文件导致进程异常缓慢。建议编译时把工程目录加入杀毒软件白名单,或者干脆暂时关闭实时防护。这不是一个必然出现的问题,但至少我遇到过一次。

5. 避坑技巧与实操心得

5.1 先编armv7a,验证全链路再用其他架构

ijkplayer支持armv7a、arm64、x86等多架构。新用户第一次编译,强烈建议只编armv7a。原因很简单:它兼容性最好,资料最全,绝大多数真机都能运行。先通过armv7a把整个编译链路跑通,再尝试编译arm64或x86,遇到问题时更容易排查到底是代码问题、脚本问题还是架构本身的问题。

我一开始贪心想一次把所有架构全部编出来,结果在x86架构的FFmpeg配置阶段卡了很久。后来把所有架构的编译产物清理干净,只编armv7a,半小时就完成了。回头看,多架构编译是后续优化阶段做的事,前期没必要给自己添堵。

5.2 保持一个干净的编译环境

我最大的体会是:ijkplayer编译问题里,有相当比例是环境不干净导致的。这些问题的特征极其相似——看起来是编译脚本报错,但追根究底是环境变量残留、目录权限不对、文件残留旧版本。

建议按这个流程保证环境干净:

  1. 编译前清空源码目录,重新clone,避免旧配置残留。
  2. 用专门的用户目录或工作目录,不要用系统盘下受控的目录。
  3. 每次切换NDK版本后,先重置环境变量,再开新的终端窗口。
  4. MSYS2的终端会话之间不要复用历史环境变量,因为Windows会话继承的变量容易把你带入一个“它是怎么设置来着”的困境。

5.3 掌握日志读取思路,比背报错更有用

编译ijkplayer时输出的日志非常长,初学者容易一头雾水。我个人的排查顺序是:先找红色或error关键字,然后从倒数第10行开始向两头看。多数情况下,真正的报错原因不是在报错语句本身,而是在它上方几行处的一个配置错误。

比如遇到C compiler test failed时,脚本给出的直接提示是“编译器无法生成可执行文件”,拿到这句话你其实什么信息都没有。但往上看十几行,往往能看到checking for arm-linux-androideabi-gcc... not found之类的行,那才是问题的根源。

5.4 集成阶段额外需要注意的坑

编译虽然成功了,但在集成到Android工程时,有几个和ijkplayer强相关的坑一定提醒一下。

第一,so文件命名的完整性问题。ijkplayer内部通过System.loadLibrary加载三个so,顺序是ijkffmpeg、ijksdl、ijkplayer。如果你的工程用了一些资源混淆或so压缩方案,需要确保这三个so都被正确处理。

第二,硬解码相关配置。ijkplayer启用硬解需要调用setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1)这种级别的设置。w这是个运行期功能,但在编译期需要确保FFmpeg配置中打开了对应的解码器和mediacodec支持模块,否则硬解调用会静默失效。

第三,混淆规则。如果应用开启代码混淆,需要手动添加ijkplayer的混淆规则,否则IjkMediaPlayer的native方法会被混淆器改名,运行时直接崩在链接native方法上。建议所有用到ijkplayer的模块都保留以下混淆规则,禁止混淆:

-keep class tv.danmaku.ijk.media.player.** { *; } -keep class tv.danmaku.ijk.media.player.IjkMediaPlayer { *; }

5.5 后续扩展方向

编译链路跑通后,我接下来比较想做的是把ijkplayer的自定义解码能力接入现有的业务播放器框架。具体来说,ijkplayer支持在Java层通过setOption传递大量底层参数,可以灵活调整缓冲策略、起播速度、丢帧策略等,适配不同网络场景。

另外一个值得研究的方向是动态加载。既然有了native层编译能力,就可以把so文件做版本隔离,在不发版本的情况下更新播放器内核。这个方案我已经在某跨平台系统上初步试过,可行性还不错。当然这属于另一个话题,如果后面做完,我会再整理一篇针对性的文章分享。

写在最后

三天时间,从零开始,在Win10上把ijkplayer编译出来,这个经历让我印象最深的一点是:环境问题的坑,远远多于代码问题的坑。NDK版本、MSYS2环境、路径、换行符、下载中断,这些问题没有太多技巧可言,就是一个个排查,一次次验证。

最后再分享一个小技巧:当你在网上搜索某个编译报错信息时,建议一次性摘录连续完整的三四行日志,而不是只记最后一行。很多问题的讨论贴年份很早,版本之间逻辑差异很大,完整日志更容易匹配到真正的同类问题。而我这次遇到的所有问题,最终都是通过“定位脚本对应的实际逻辑+重新梳理环境”解决的,纯粹靠搜答案是很难救急的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 23:02:42

Django自定义用户模型实战:从字段设计到JWT认证落地

写这篇分享的思路,得从一个很实际的角度说起:做Django全栈项目时,迟早要面对用户系统。用默认的User凑合一阵子没问题,但业务稍微复杂点——比如要存头像、要支持邮箱登录、要区分不同角色——默认模型就束手束脚了。这篇是系列的…

作者头像 李华
网站建设 2026/10/13 23:02:02

英语学习的关键不是努力而是思考:从词汇到口语的深度加工方法

学英语这些年,我一直被一个问题困扰:为什么一样背单词、一样刷题、一样看美剧,有人进步飞快,有人却始终在原地打转?后来我慢慢想明白了,差距不在“花了多少时间”,而在“有没有动脑子”。英语学…

作者头像 李华
网站建设 2026/10/13 23:01:57

联邦学习成绩预测实战:FedAvg聚合与Streamlit可视化

简介:这份资源围绕联邦学习在高校学生成绩预测中的应用展开,面向计算机、人工智能、数据科学等专业的学生与从业人员,可用于毕业设计、课程设计、大作业或初期项目立项演示,也适合希望入门联邦学习与隐私保护建模的开发者练手。压…

作者头像 李华
网站建设 2026/10/13 22:59:33

客户沟通记录总靠手写复盘?2026年AI录音转写工具实测横评

做销售、跑商务、管客户的人,大概都经历过同一种尴尬:一场客户沟通聊得热火朝天,回到工位准备写复盘,却发现客户提的三个顾虑只记得一个,承诺的报价细节怎么想都想不起来。等到下次跟进,只能靠模糊印象硬撑…

作者头像 李华
网站建设 2026/10/13 22:59:07

C#设计模式实战源码:用原生语法重构23种经典结构

简介:本资源是面向C#中高级开发者的设计模式学习套件,聚焦软件架构设计能力提升,帮助程序员系统掌握23种经典设计模式在C#中的落地实现与工程化应用。压缩包为RAR格式,大小3.26MB,包含完整可运行的源码工程&#xff08…

作者头像 李华
网站建设 2026/10/13 22:58:40

游戏引擎基础架构全景解析:从模块分层到帧循环

说实话,接到这个系列选题,我脑子里第一反应不是那些炫酷的渲染效果,也不是某某引擎的发布会,而是很多年前有人丢我一句「引擎不就是个换皮工具嘛,八千块钱找个人不就做出来了」。后来我自己也在行业里泡了挺多年&#…

作者头像 李华