☰
ijkplayer 0.8.7实战:用FFmpeg软解mp2/mpeg4老格式
2026/10/27 14:40:08 网站建设 项目流程

简介:ijkplayer 0.8.7版本的zip资源包,基于FFmpeg并由B站开源维护,面向需要在Android等平台快速集成多媒体播放能力的开发者,解决了mp2、mpeg4及AAC等格式的播放支持问题。压缩包约62.07MB,共1599个文件,涵盖Java源码、XML资源配置、PNG图标以及预编译的SO动态库与A静态库,整体目录结构完整,便于定位和引入。已有466人学习下载,适合希望直接使用或研究ijkplayer内部实现的人群。其中既提供了可直接放入jniLibs的SO库,也保留了上层Java接口与工程配置,开发者可省去从源码编译的繁琐流程,快速构建定制播放器。通过分析工程内的gradle、mk、properties等文件,还能进一步理解FFmpeg集成与多媒体框架的构建方式。 看到ijkplayer.zip 0.8.7这个包名,我第一反应是:有点年头了。但转念一想,这几年我做播放器集成,真正在生产环境里跑得最久的,反而是这种看起来“过时”的版本。0.8.7停在ijkplayer还比较朴素的年代,API简单,文档多,社区里能踩的坑基本都被踩平了。再加上这个zip明确写了支持mp2和mpeg4,等于告诉你它的FFmpeg编译配置里没把这两个老解码器裁掉,对付DVB录制的ts流、DivX/Xvid老AVI这类素材,比现拉一个新版本还要省事。这篇文章就结合我自己的实操,聊聊这个zip包怎么用、为什么值得留、以及容易在哪翻车。

1. 版本定位:0.8.7在ijkplayer体系里是什么段位

1.1 时间线和这个包为什么还在传

ijkplayer是B站开源的移动端播放器方案,核心思路是把FFmpeg强大的解码能力搬上Android和iOS,再用系统或自研的渲染层把画面画出来。0.8.7这个版本大概是2016年前后发布的,那段时间刚好是Android系统播放器对封装格式支持很看脸的时代。mkv、flv、ts这些常见格式,很多手机自带播放器根本打不开,各厂商的MediaPlayer实现又千奇百怪,所以ijkplayer靠一套FFmpeg解码链统一了体验,成了做视频App、电视盒子、监控回放等场景的热门选择。

后来官方把重心往新版本推,版本号也跳到了k0.9.x,代码结构更复杂,配套的ExoPlayer分支、硬解策略也改了不少。但奇怪的是,我现在还经常能在各种项目群里看到0.8.7的zip在传,甚至不少老项目的gradle配置就锁死在0.8.8之前的某个commit上。原因很简单:新版本功能多,但改动也大,老项目不敢随便升,再加上0.8.7的API用法在网上沉淀了大量示例,出了问题一搜就有答案,团队接手成本极低。

1.2 老版本的优势和局限

优势这块我体会很深。首先,源码结构清晰,模块划分不像后来那样绕。想编译,Android Studio配一个低版本NDK就能跑,编译脚本也直接放在zip里。其次,API稳定,IjkMediaPlayer的核心用法和现在的主流播放器差不多,网上教程一抓一把。最后,社区已经把坑排得差不多了,比如so加载顺序、硬解兼容性、SurfaceView黑屏这些问题,搜一下全是现成答案。

局限也很明显:官方早就停止维护了,新系统上可能出现Surface生命周期、动态权限、音频焦点等兼容问题,遇到只能自己动手改。另外,它默认的构建配置更偏向软解,在高分辨率高码率素材下对CPU的压力比较大,这也是后面版本花大力气优化硬解的原因。所以0.8.7适合的是“稳定优先、素材可控”的场景,而不是从零起步的新项目。

2. “支持mp2 mpeg4”到底意味着什么

2.1 两种老编码的来历和现状

先拆一下这两个词。mp2不是MP3,而是MPEG-1 Audio Layer II,音频压缩效率比MP3低一点,但编码器简单、容错性好,在DVB数字电视广播、DVD、部分专业录音机和老式HDV摄像机里很常见。你从机顶盒导出的ts文件、某些老旧采集卡录出来的音频,很可能就是mp2。麻烦的是,手机自带播放器经常不认这个格式,放出来只有画面没有声音,或者干脆整个文件打不开。

mpeg4这里指的是MPEG-4 Part 2 Visual,也就是我们熟悉的DivX、Xvid背后的视频编码标准。它和H.264(MPEG-4 Part 10 / AVC)是两代技术。早年网上流传的AVI电影,很多就是Xvid压缩的。现在不少新设备没有针对这格式的硬解单元,系统播放器识别也看缘分,所以需要ijkplayer这种自带FFmpeg的播放器来兜底。

2.2 FFmpeg的解码链和裁剪问题

ijkplayer的编解码能力全部来自FFmpeg,FFmpeg本来就内置了mp2音频解码器和mpeg4视频解码器。但这里有个关键坑:ijkplayer在构建时不是把所有解码器都编进去的,官方提供了不同的module配置,比如精简版module-lite.sh就会去掉一部分不常用的格式,以达到减小so体积的目的。我见过不少“精简魔改版”播放mp4没问题,一放DivX老片就提示不支持,就是因为编译时把mpeg4解码器裁掉了。

所以zip里特意写明“支持mp2 mpeg4”,等于在告诉你:这份包的FFmpeg构建配置里保留了mp2_decoder和mpeg4_decoder。对做素材兼容的老项目来说,这个信息比版本号本身更重要。用的时候可以留意一下播放日志,如果是FFmpeg在软解,通常会输出类似Audio: mp2, 48000 Hz, stereo和Video: mpeg4 ...的行,看到这个就说明对应解码器已经生效。

2.3 软解还是硬解怎么选

这是播放老格式时最容易纠结的地方。手机SoC通常对H.264和HEVC有硬解支持,走的是MediaCodec,功耗低、性能好。但mp2和mpeg4这种老格式,硬解单元往往没有对应解码器,就算有,厂商的实现也可能不完整。所以我建议处理这两类素材时,直接在代码里强制关掉MediaCodec,让FFmpeg走软解。

软解的代价是CPU占用高。mpeg4的视频分辨率如果到了1080P,老中端机器发热会比较明显。好在这类老素材通常码率不高,实际跑起来还是能接受的。如果遇到卡顿,可以配合丢帧策略和缓冲调整来缓解,后面我把参数写出来。

3. 从zip包到能跑的App:Android集成实操

3.1 先认清zip包里到底有什么

拿到zip包后别急着解压往工程里塞,先看目录结构。以我见过的大多数0.8.7打包为例,有两种典型形态:一种是完整源码工程,里面会包含android/目录、config/目录、contrib/目录以及一堆编译脚本,这种适合想自己重新编译、或者调整codec配置的情况;另一种是预编译产物,里面通常是libs/目录加ijkplayer-java的jar或aar,甚至直接给你打好了一个可直接引用的module。

源码工程的结构通常长这样:

  • android/ijkplayer/:Android Studio工程根目录
  • android/ijkplayer/ijkplayer-java/:Java层API
  • android/ijkplayer/ijkplayer-armv7a/、ijkplayer-arm64/等:各ABI的so模块
  • config/module.sh:FFmpeg解码器裁剪配置
  • contrib/:FFmpeg等Native库生成脚本

预编译版的目录就简单得多,核心就是libijkplayer.so、libijkffmpeg.so、libijksdl.so这三个so,再配套一个Java层的jar/aar。不管怎么打包,这三个so都是根基,缺一个都跑不起来。

3.2 方案A:整个源码工程自己编译

如果你拿到的是源码zip,又需要改FFmpeg配置,那可以走完整编译路线。老规矩,先准备好Android SDK和NDK。这里我必须提醒一句:0.8.7年代对应的NDK版本不能太新,很多人用新版NDK一编就报错,不是脚本不兼容就是编译器行为变了。我自己的经验是用NDK r10e这个级别最稳,后面折腾新版本花的时间比编译本身还久。

编译命令不复杂,按顺序执行:

./init-android.sh cd android/contrib ./compile-ffmpeg.sh clean ./compile-ffmpeg.sh all cd .. ./compile-ijk.sh all

init-android.sh负责拉取FFmpeg源码并打补丁,compile-ffmpeg.sh负责按config/module.sh生成对应的so库,最后compile-ijk.sh把ijkplayer自身代码和SDL基础库一起编译。如果你只是想确认某个解码器有没有被编进去,编译完后到android/ijkplayer/ijkplayer-armv7a/src/main/libs/armeabi-v7a/下看so文件列表,再结合日志验证。

3.3 方案B:用预编译so快速集成

大多数情况下你不需要自己编译,直接用zip里的so和jar就行。操作方式很简单:把so文件按ABI目录放到app/src/main/jniLibs/下,比如:

app/src/main/jniLibs/ armeabi-v7a/ libijkplayer.so libijkffmpeg.so libijksdl.so arm64-v8a/ libijkplayer.so libijkffmpeg.so libijksdl.so

Java层的jar或aar放到libs/目录,然后在build.gradle里加一句:

implementation files('libs/ijkplayer-java-0.8.7.jar')

如果你手上的是0.8.8及以后的版本,也可以直接用官方仓库坐标拉依赖:

implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8' implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'

但0.8.7在公共仓库里不一定好拉,还是建议优先走本地包集成。

3.4 播放mp2/mpeg4的核心代码

集成完成后,初始化播放器的代码在0.8.7里长这样:

IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer(); // 这两个参数很关键:强制走软解,别让系统硬解去碰老格式 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); // 开启opensles输出,对低延迟播放有帮助 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1); // 允许丢帧,缓解软解卡顿 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0); ijkMediaPlayer.setDataSource("/sdcard/Movies/old_sample.avi"); ijkMediaPlayer.prepareAsync(); ijkMediaPlayer.setOnPreparedListener(IMediaPlayer::start);

mediacodec设为0就是关闭硬解,这是播放mp2/mpeg4时最不容易出错的配置。framedrop和packet-buffering是经验参数,低配机上丢帧比音画不同步更能被用户接受。还有个容易忽略的点:Android 6以上要记得动态申请存储权限,否则setDataSource直接给你抛IOException,别问我怎么知道的。

4. 避坑实录:老版本播放器容易在哪翻车

4.1 常见错误速查表

我把这些年遇到的高频问题整理成了一张表,按“现象-原因-处理”三列来查就行。

现象原因处理
启动即崩溃,报UnsatisfiedLinkError三个so没拷全,或ABI目录不对确认libijkplayer、libijkffmpeg、libijksdl三个so都放到位
播放mp4有声音没画面硬解通道不支持mpeg4,解码失败后没回退关闭mediacodec,强制FFmpeg软解
mp2音频文件只有画面没声音音频输出链路没跑通开启opensles,或检查音频焦点
老AVI卡顿严重mpeg4软解CPU占用高开framedrop,适当降低缓冲,或裁剪更高分辨率
模拟器上直接崩预编译包往往只有armv7a,x86模拟器跑不了换真机测试,或自己编译x86版本
Android 8以上黑屏Surface生命周期和旧版硬解冲突改用SurfaceView,或在onPause/onResume里手动处理
混淆打包后找不到方法Java层API类被混淆掉keep相关类,比如tv.danmaku.ijk.media.player包

4.2 日志定位和强制软解的开关

出问题时别瞎猜,先看日志。老规矩三板斧:

adb logcat | grep IJK adb logcat | grep FFmpeg adb logcat | grep AndroidRuntime

如果视频能正常解码,FFmpeg的日志里会打印编码信息,比如Video: mpeg4或者Audio: mp2。如果解码器没被编译进去,你会看到类似Could not find codec parameters或者decoder not found的提示,这时候就要确认zip里的so是不是真的支持这两个格式。

强制软解最保险的写法是把软解开关放到初始化之后、prepare之前,并且在所有mediaCodec相关的option上都加一层保险:

ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "mediacodec", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-error", 0);

这几个参数都指向同一个目的:不让系统硬解去碰老编码,避免出现解码失败后播放器卡死在黑屏状态。

4.3 体积和性能优化

老版本最被诟病的就是so体积大。如果你的App只在armeabi-v7a和arm64-v8a两个主力ABI上运行,那就只留这两个目录,别把x86的so打进去,包体积直接小一截。另一个更彻底的做法是自己重新编译FFmpeg,按需裁剪解码器。比如你只需要mp2和mpeg4,就在config/module.sh里把其他不用的decoder注释掉,只保留:

FFMPEG_CMD_DECODERS="mp2_decoder mpeg4_decoder ..."

重新编译后so会小很多。这里有个血的教训:改完配置重新编译前,一定先把当前能用的so备份一份。我有一回把配置改坏,折腾了两天才发现原来的so没备份,只能重新拉代码再编,浪费了半天时间。

性能方面,软解mpeg4在老机型上负载不低,建议播放前主动判断视频参数,如果分辨率超过设备能力,就降一级画面输出,或者在业务层限制播放码率。字幕、音轨切换这些功能能关就关,0.8.7的额外功能很轻,但也别让它增加不必要的开销。

5. 关于老版本,我的一些个人心得

5.1 为什么我一直留着这些zip

做过播放器集成的朋友应该能理解,很多项目不是死在功能上,而是死在“素材兼容”上。我做监控回放项目的时候,现场设备吐出的ts流音频就是mp2,视频还有一部分是MPEG-4 Part 2编码。系统播放器放不了,换新播放器内核又要重新适配半年,最后还是回到ijkplayer 0.8.8这条线上。说实话,0.8.7这个zip对我个人来说就是救场模板,遇到素材打不开的反馈,先用它跑一遍,能定位是不是播放器内核的问题。

5.2 什么时候该换掉0.8.7

如果是新项目、新设备、对硬解要求高,那我不建议从0.8.7起步,直接去看主流维护中的播放器方案更合适。但如果你手里有大量老素材,项目架构又已经跑稳了,那0.8.7或者0.8.8这个版本真的没必要急着换。给它配一个独立的module,锁好NDK版本,整理好so的备份,它能稳定陪你很久。最后再分享一个实操习惯:每次拿到这类zip,我会第一时间把so的SHA-256记录下来,防止版本混淆,也能在多人协作时快速确认大家用的是不是同一份产物。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询