ijkplayer完整so指南:rmvb、h265与SSL支持的真实验证方法
简介基于ijkplayer 0.8.82021年11月GitHub版本的全平台全协议JNI libijkffmpeg.so面向需要集成音视频播放能力的Android开发者解决自编译ffmpeg门槛高、协议支持不全等问题。资源已用Gradle打包成Android module可直接编译为aar也可将so库拷贝至现有工程调用覆盖arm64-v8a、armeabi、armeabi-v7a、x86、x86_64五种架构支持OpenSSL、H.265、RM/RMVB、RTSP、RTMP等直播与本地播放场景。压缩包共53个文件含28个Java源码、15个so动态库、Gradle配置及混淆规则等包体约31.48MB结构清晰便于集成与二次开发。已有1920人学习下载适合希望快速获得可编译ijkplayer模块、避免从零编译复杂FFmpeg环境的Android中高级开发者。1. 拿到一套带 rmvb、h265、ssl 的 ijkplayer so先搞清楚这套东西到底值不值得用你大概率是这么遇上的手里压着一批 rmvb 老资料或者要接某个平台的 https 直播流Android 自带的 MediaPlayer 死活打不开VLC 能播但软解烫手然后有人丢给你一句“用 ijkplayer 0.8.8 编的完整 solibijkffmpeg.so 都在支持 ssl、h265、rm、rmvb”。这句话听起来像解药但等到你把 so 塞进工程、跑起来才会意识到“支持”这两个字的水分有多大——它可能只是解封装支持也可能是解码器齐全、协议完整、还带硬解回退的真完整包两者在体量、依赖和播放表现上完全是两回事。这类完整 so 的目标用户其实很明确不想自己啃 FFmpeg 交叉编译、又需要老格式兼容和现代协议能力的 Android 播放器开发者。你要做的东西通常是一个资源播放器、工具箱或者内网终端。这篇文章不教你造轮子而是带着你把“支持 ssl / h265 / rm / rmvb”逐个拆开告诉你怎么验证手里的 so 有没有这些能力、缺了怎么补、集成时从哪里开始翻车。2. 用 ijkplayer 0.8.8 编译完整 so环境准备、核心脚本与产物说明2.1 构建环境三件套NDK 选型、SDK 许可证与 Linux 依赖ijkplayer 0.8.8 是 2017 年前后的版本它拉取的 FFmpeg 分支还停留在 3.x 时代。这个时间点决定了你的编译环境不能太“新”尤其是 NDK。常见做法是下载 NDK r10e 这类老版本因为 FFmpeg 3.x 的 configure 脚本还在用 gcc 那套探测逻辑新 NDK 从 r18 开始移除 gcc只留 clang会让 configure 阶段直接报错或者生成一堆奇怪的交叉编译前缀。如果你手头只有新版 NDK也不是不能编但需要额外写 clang wrapper 骗过 configure属于浪费时间。系统方面Ubuntu 16.04/18.04 这类老 LTS 是最稳的高版本 Ubuntu 上 OpenSSL 1.0.2 的编译可能遇到 Perl 模块兼容问题。需要装的基础包是 git、make、yasm、python 2.7 和 JDK 8。yasm 千万别漏FFmpeg 汇编优化没有它跑不起来。Android SDK 方面只需要 platform-tools 和一个 platform 包用于后续打包验证编译本身不依赖 SDK 版本。所有流程跑通后你会得到一堆按 ABI 分层的 so 文件。其中最关键的是libijkffmpeg.so它是 FFmpeg 各库的合体包括 libavcodec、libavformat、libavutil 等另外两个是libijkplayer.so和libijksdl.so前者是 IJKPlayer 的核心封装后者是它魔改过的 SDL 抽象层。这三个 so 构成一套完整的最小播放内核标题里说的“完整 so”指的就是它们齐整、且 FFmpeg 功能没被阉割。2.2 最小构建流程init、compile-ffmpeg、compile-ijkplayer 三步跑通编译 ijkplayer 的官方路径是固定的先把仓库切到 k0.8.8 标签然后初始化 FFmpeg 源码再分别编译 contrib 和 player。下面是最小命令序列# 取指定版本源码 git clone -b k0.8.8 https://github.com/bilibili/ijkplayer.git ijkplayer cd ijkplayer # 初始化 ffmpeg 与 openssl 源码到 extra 目录 ./init-android.sh # 进入 android/contrib这里管的是 ffmpeg 和 openssl 的交叉编译 cd android/contrib # 清掉上一次的残留产物避免缓存把新参数覆盖掉 ./compile-ffmpeg.sh clean # 编译所有 ABI包括 armv7a、arm64-v8a、x86 ./compile-ffmpeg.sh all # 回到 android 目录编译 ijkplayer 自身的 so cd .. ./compile-ijkplayer.sh allinit-android.sh这一步很多人会跳过但它负责把 FFmpeg 源码和 OpenSSL 源码拉进extra目录没有这一步后面 configure 会直接失败。compile-ffmpeg.sh clean很关键因为compile-ffmpeg.sh默认会在 configure 参数改变后自动重编但有时缓存检测不可靠clean能保证你改的参数真的生效。all表示编全部目标 ABI如果只想先跑通可以只编armv7a和arm64-v8ax86 主要用于模拟器调试真机场景用处不大。这套流程在干净的 Linux 机器上大概需要 20 到 40 分钟取决于 CPU 核心数。编译过程会打印大量 configure 探测日志如果卡住不要光盯着最后几行往上翻找到第一个 error 才是真正的失败原因。2.3 configure 里的真实开关一份参数表与动态检查方法compile-ffmpeg.sh不是凭空编 FFmpeg 的它会在android/contrib下生成一份 configure 命令参数来自config/module.sh。所谓“支持 ssl / h265 / rm / rmvb”最后全部落在 configure 参数上。先看一份最常用的参数表参数作用备注--enable-openssl开启 OpenSSL 支持https 协议依赖它需要先交叉编译 OpenSSL--enable-bzlib开启 bzlib 库支持rm/rmvb 索引解压依赖它需要一个可用的 libbz2--enable-small裁剪 FFmpeg 体积减少无用代码会略降编码性能通常开着--disable-doc关闭文档生成省编译时间必开--disable-programs不生成 ffprobe/ffmpeg 可执行文件想验证 so 就别开--enable-ffprobe生成 ffprobe 工具验证解码器/协议的神器--disable-decoders关闭指定解码器检查是否有 rv30/rv40/hevc这表里最容易被忽视的是--disable-programs和--enable-ffprobe的组合。很多精简版完整 so 为了体积会关掉 programs导致你拿到 so 后无法用 ffprobe 自检。如果你要验证别人的 so先看有没有附带 ffprobe 可执行文件没有的话只能靠代码跑播放测试工作量翻倍。还有一种更直接的检查方法直接看编译日志里的 configure 行。FFmpeg 会把完整配置写进config.h编译完成后在ffmpeg-armv7a目录下的config.h里搜CONFIG_OPENSSL、CONFIG_RV30_DECODER、CONFIG_HEVC_DECODER值为 1 就代表编进去了。这个方法不仅适用于自己编译也适用于任何拿到手的源码树。3. 把 rm / rmvb 重新请回来解码器开关、bzlib 依赖与验证命令3.1 rm / rmvb 为什么是“默认丢失”的格式rmvb 是 RealMedia 的可变码率容器视频轨通常是 RV30/RV40音频轨是 Cook 或 AAC。FFmpeg 3.x 本身是支持这些解码器的不存在“编不出来”的问题。真正的问题出在 ijkplayer 的默认 module 配置上——官方为了减小 so 体积在module-lite.sh里会通过--disable-decoders把一批老旧格式关掉rm/rmvb 就在清单里。另外一个隐蔽的坑是 bzlib。rm/rmvb 文件的头部索引信息可能是 bzip2 压缩过的FFmpeg 解封装时要用 bzlib 去解压没有这个库文件根本打不开连解码器的机会都没有。所以“支持 rm/rmvb”其实是两道关demux 阶段要 bzlibdecode 阶段要 rv30/rv40/cook。很多号称支持的 so 只过了第二道关文件名能识别播放就卡死或直接返回错误。判断手里的 so 卡在哪道关有个土办法先放一个正常的 rmvb 进去如果 logcat 里出现could not find codec parameters或Invalid data found when processing input大概率是 bzlib 缺失如果文件头能读出来、时长也有但画面一直黑那就是解码器没编全。3.2 一次补依赖的完整操作bzip2 交叉编译 configure 改参如果你决定自己重编补齐 rm/rmvb 支持的完整链路是先交叉编译 bzip2再把编出来的 libbz2.a 交给 FFmpeg configure最后确认 rv30/rv40/cook 不在禁用列表里。bzip2 本身是 C 源码用目标架构的交叉编译器编一个静态库即可# 下载 bzip2 源码后进入目录以 armv7a 为例 export CC$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi16-clang export AR$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar # 只编静态库 libbz2.a make -f Makefile-libbz2_so CC$CC AR$AR bzip2 # 将产物复制到 FFmpeg 编译能找到的位置 cp libbz2.a $PATH_TO_IJKPLAYER/android/contrib/build/armv7a/lib/这里有个细节交叉编译时头文件和库的搜索路径经常对不上FFmpeg configure 找不到 bzlib.h 的话--enable-bzlib会静默降级为 disable。保险做法是把 bzip2 源码里的bzlib.h一并复制到 include 目录让 configure 的探测逻辑直接通过。改完库之后还要在 module 配置里加一行--enable-bzlib然后重新跑compile-ffmpeg.sh clean compile-ffmpeg.sh all。记得先clean否则 configure 可能沿用旧参数。3.3 用 ffprobe -decoders 确认 rv30 / rv40 是否真正编入代码编译完成后验证工作比再翻十遍日志都直接。前提是你在 configure 里开了--enable-ffprobe这样android/contrib/build/armv7a/ffmpeg-armv7a目录下会生成一个ffprobe可执行文件。用它查解码器和解复用器# 查看解码器里有没有 rv30/rv40/cook ./ffprobe -decoders | grep -E rv30|rv40|cook # 查看解复用器里有没有 rm ./ffprobe -formats | grep -i rm # 查看 bzlib 是否编进配置 ./ffprobe -version | grep -i bzlib-decoders输出的是 FFmpeg 编译时启用的全部解码器rv30和rv40出现就代表视频轨能解cook代表音频轨能解。三个都有输出rmvb 这条路基本通了。注意 ffprobe 是 Linux ELF 文件在 macOS 上跑不了需要放到 Linux 环境或者 Android 设备的 shell 里执行。我一般会在拿到任何一套第三方 so 时先做这个验证把 ffprobe 推到设备上跑一遍-decoders然后拿一个真实的 rmvb 文件试播。这一步能过滤掉大半“号称支持”的水分。4. 面向直播与高压缩片源ssl 与 h265 的配置边界4.1 ssl 的最小开关openssl 进入 FFmpeg configure 的两种方法FFmpeg 的 https 支持不是自带的它在协议层做了一层 TLS 抽象底层可以用 OpenSSL、mbedTLS 或 GnuTLS。ijkplayer 0.8.8 默认走 OpenSSL因为它的init-android.sh会同时拉一份 OpenSSL 源码到extra目录。也就是说你不需要手动去编 OpenSSL只要在 module 配置里打开开关编译脚本就会先编 OpenSSL再编 FFmpeg 时把链接参数带上。做法是在config/module.sh里加一行export COMMON_FF_CFG_FLAGS$COMMON_FF_CFG_FLAGS --enable-openssl加了这行之后compile-ffmpeg.sh会自动在 configure 阶段追加--enable-openssl和对应的 LDFLAGS。这里有个版本老坑0.8.8 时代预设的 OpenSSL 是 1.0.2 系列而较新的系统环境可能已经装了 1.1.x交叉编译 OpenSSL 时版本差异会体现在符号表上。1.1.x 版本的SSL_CTX_new等符号与 1.0.x 有细微差别但一般只影响老设备上的动态链接新设备反而不敏感。如果你编完发现进程启动就崩优先查nm -D libijkffmpeg.so | grep SSL_CTX看符号是否真实存在。还有一类场景需要额外注意rtmp 流带加密时走的是rtmpe协议它也需要 OpenSSL还有 hls 列表里的 AES-128 加密分片解密用的 crypto 协议同样依赖 OpenSSL。所以“支持 ssl”不只是 https 直播流它还顺带决定了你能不能播加密 hls。4.2 h265 软解与硬解hevc decoder、MediaCodec 与分辨率回退h265 在 FFmpeg 里的解码器名字叫hevc不是h265configure 时找CONFIG_HEVC_DECODER。软解层面FFmpeg 3.x 已经内置了完整的 HEVC 解码器不需要额外引入 libx265——libx265 是编码器和解码无关。所以“支持 h265”的底线就是--disable-decoders里没有 hevc或者--enable-decoderhevc被显式打开。硬解是另一套逻辑。ijkplayer 的硬解走 MediaCodec代码里通过OPT_CATEGORY_PLAYER下的mediacodec和mediacodec-hevc开关控制。打开硬解后ijkplayer 会优先把压缩数据交给 MediaCodec如果 MediaCodec 不支持该分辨率或该 profile再自动回退软解。这个回退不是每次都成功尤其是 4K HEVC 主 10 profile 的片源很多设备的 MediaCodec 只支持主 profile硬解直接失败。播放器初始化的 Java 侧代码通常这么配IjkMediaPlayer player new IjkMediaPlayer(); if (Build.VERSION.SDK_INT Build.VERSION_CODES.LOLLIPOP) { player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, mediacodec, 1); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, mediacodec-hevc, 1); player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, mediacodec-auto-rotate, 0); }mediacodec-hevc这个开关不是所有 ijkplayer 分支都有0.8.8 官方版本里有。mediacodec-auto-rotate建议关掉否则部分设备在硬解时画面方向会旋转异常。真机测试时你会发现低端机上硬解 h265 的开销其实不小MediaCodec 本身要分配解码缓冲区如果片源是 1080p 以上的高码率黑屏概率不低。更稳的做法是在播放前根据设备型号和视频分辨率做一个策略表超过一定码率直接走软解。4.3 编译后自检protocols 清单与常见黑屏归因验证 ssl 是否真正编入不能只看日志里有没有“https supported”字样要直接查协议表# 查看协议列表tls、https、crypto 必须出现 ./ffprobe -protocols | grep -E tls|https|crypto # 确认 OPENSSL 宏是否开启 ./ffprobe -version | grep -i openssl如果-protocols里没有tls说明 OpenSSL 链接失败configure 静默降级了。这种情况最常见的原因是交叉编译 OpenSSL 时生成的 libssl.a随后 FFmpeg LDFLAGS 里少了-lssl -lcrypto链接时没有报错是因为 FFmpeg 会对未启用功能做容错处理。此时只能回去检查compile-openssl.sh的产物是否存在、架构是否匹配。自检完成后建议用三个真实场景验证一段 https 的 m3u8 直播流、一个 h265 的 mp4 文件、一个 hls AES-128 加密流。三个都能出画面ssl 和 h265 这关才算真的过了。5. 避坑把动态库放进 jniLibs 前后最常见的 5 个翻车点5.1 UnsatisfiedLinkError 与 abi 混装现象与解决现象so 文件都放进了src/main/jniLibs运行时却报java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol avcodec_register_all。原因通常是 ABI 目录不完整比如armeabi-v7a下有libijkffmpeg.so但arm64-v8a目录漏了这个文件64 位设备加载时优先找arm64-v8a目录找不到就会去其它目录捞捞到了也会因为格式不匹配而丢符号。解决四个 ABI 目录必须每个都放齐三个 so缺一不可。检查脚本可以用adb shell ls /data/app/.../lib/arm64或者读 APK 里的 so 清单。5.2 dlopen failed: cannot locate symbol SSL_CTX_new现象so 能正常加载但一播放 https 流就崩日志明确指向SSL_CTX_new找不到。原因libijkffmpeg.so里确实编了 OpenSSL 代码但链接时用了动态符号表导出而 Android 的 so 解析器在加载时没有找到这个符号的依赖库更常见的是这套 so 是用 OpenSSL 1.0.2 编的运行环境却是只带 1.1.x 符号的系统符号表对不上。解决查看nm -D libijkffmpeg.so | grep SSL_CTX_new确认符号存在再确认设备系统版本。如果符号确实没有只能换一套 so 或重编。5.3 rmvb 播放只有音轨没有画面或直接打不开现象rmvb 文件能识别时长读得出来但画面黑屏偶尔只有声音。原因demux 阶段 bzlib 没问题但视频解码器 rv30/rv40 没编进去FFmpeg 找不到对应解码器只能跳帧。解决用 ffprobe 验证解码器列表确认rv30/rv40存在若只有 rv40 没有 rv30老一点的 rmvb 还是播不了。这个坑在第三方 so 里很常见因为很多“精简版”会把 rv30 当老格式裁掉。5.4 硬解 h265 黑屏但软解正常现象同一条 h265 视频关掉 MediaCodec 能正常播放打开就黑屏甚至整个 Surface 卡住。原因MediaCodec 初始化成功但解码失败多半是设备硬解不支持该 profile 或分辨率ijkplayer 的回退逻辑没有触发。解决设置mediacodec-hevc开关之后再设置一个按分辨率判断的策略1080p 强制软解另外把mediacodec-auto-rotate关掉部分设备黑屏其实是旋转元数据解析卡死。实话说这算 ijkplayer 的“玄学”区最有效的排查方式是把mediacodec开关逐项测试。5.5 编出来的 so 过大或过小判断“完整包”真伪现象有人说这是完整 so但libijkffmpeg.so只有不到 8MB。原因FFmpeg 在--enable-small配合大量--disable-*时能把体积压得非常小但功能也被裁得厉害。正常一个带 OpenSSL、hevc、rmvb、各类协议全开的libijkffmpeg.soarmv7a 体积通常在 25MB 上下arm64-v8a 更大。解决拿到 so 后先看体积再逐一验证关键符号用nm -D检查avcodec_find_decoder是否导出用ffprobe -decoders查关键解码器。体积能说明问题但不是唯一标准——有些包用--enable-small压到 15MB功能也不缺。6. 用 ffprobe 与 logcat 验收完整 so解码器、协议、链接库一次查清拿到任何一套标榜“完整”的 so我不建议直接集成先跑一轮验收脚本。把ffprobe如果有推到设备或者用 Linux 环境依次检查三类东西解码器、协议、动态符号。命令可以连起来看# 解码器层面重点查 rv30/rv40/cook/hevc ./ffprobe -decoders | grep -E rv30|rv40|cook|hevc # 协议层面重点查 https/tls/crypto ./ffprobe -protocols | grep -E tls|https|crypto # 链接符号层面确认 OpenSSL 与 bzlib 是否导出 nm -D libijkffmpeg.so | grep -E SSL_CTX_new|BZ2_bzDecompressInit|avcodec_register_all这三行命令的输出就是对“完整”二字的真实注解。如果 so 没带 ffprobe退而求其次在播放器里加一段日志播放一个 rmvb、一个 https m3u8、一个 h265 mp4看 logcat 里 IJKFFmpeg 模块的报错。FFmpeg log通常会把Could not find codec parameters这类关键信息打到IJKFFMpeg标签下用adb logcat -s IJKFFMpeg:V去抓。整条链路里rmvb 是最挑剔的验收样本——它同时考验 demux、bzlib、老解码器这一关过了其余格式不太会翻车。我还习惯把一个真实 rmvb 放进断言测试里启动播放器等 3 秒断言视频首帧回调到达。这个环节能提前暴露解码器缺失和硬解冲突问题比人工盯着屏幕可靠。把这条测试放进 CI后续每次换 so 包都会自动验证省掉大量人工回归。我自己曾经吃过一次亏拿到一套 so体积合理、文档写得很全结果 rmvb 播不出声音查到最后是 cook 解码器被静默裁掉。从那以后凡是接手第三方编译产物先跑自检命令再看文档文档只是参考符号和解码器清单才是真相。希望这些步骤能帮你少走一段弯路。本文还有配套的精品资源点击获取