这只是一篇记录
又爱又恨的ijkplayer
刚开始准备改造视频播放器的时候,对比了ijkplayer
和ExoPlayer
,我们的用例是HLS源点播,因为ijkplayer中文资料更多,支持更多的格式,而且b站这么多的用户,”兼容性”应该更好,所以毫不犹豫选择了ijkplayer。实际使用起来也并不复杂,简单封装修改了IjkVideoView,根据需要修改编译配置,编译so库,API使用非常方便。但是在使用了大半年ijkplayer后,被几个问题一直困扰,导致现在不得不考虑更换。
遇到的问题:
- 经常出现加载失败,一直在加载
- 错误状态不清晰,比较难定位问题
- IjkMediaPlayer不保证复用安全
- 在某些4.4机型上频繁创建IjkMediaPlayer会崩溃
加载失败的问题最严重,虽然用户量不算大,但经常收到反馈,出现的情况比如播放时频繁加载、播放一段时间就卡住了,一直显示在加载,由于IjkMediaPlayer不保证复用安全,只能重新创建MediaPlayer。起初我以为只是用户的网络问题,或者是视频服务、CDN问题。但随着类似的反馈越来越多,问题肯定出在播放器上,我也尝试过各种设置,修改了各种参数,还有类似ijkhttphook
等等,都无法解决。加载慢还带来了seek慢问题,seek之后需要等待很长时间才生效,当然这和视频源也有很大的关系,但在iOS、web端都正常。
错误状态不清晰也让我逐渐对她失去信心,视频源、播放设备都可能会出现问题,因为无法定位问题,浪费了很多时间联系用户,错误内容也没有直观的信息,每次收到出错日志都慌的一匹。
其他的如在某些4.4机型上频繁创建IjkMediaPlayer会崩溃,不断累积的native异常等,可靠性也不算好。不能确定是否是硬解软解与显示surface的问题,在Profiler里观察到播放效率(耗电量)明显偏高,内存占用也不少。
我并不擅长FFmpeg和c语言,没有信心在ijkplayer的基础上修改好,再加上一些安全性问题,需要修改之前的自定义协议方式,综合以上,在前段时间开始准备迁移到ExoPlayer。
迁移到ExoPlayer
首先想好做什么
目标很清晰,替换IjkMediaPlayer -> Player
、IjkVideoView -> PlayerView
,因为并不打算继续使用ijkplayer,而且两个播放器API差异不算小,所以没有使用接口抽象继续兼容ijkplayer,播放器与业务相关的API改动不大,只需要替换player即可。除此之外我还需要加入部分解密算法,实现自定义协议、支持解密,native代码混淆。
研究原理
ExoPlayer资料非常丰富详细,包含官方文档、google io视频、丰富的github issue。通过这些方法解决了我遇到的所有问题,并且让我了解了很多相关知识,后面会介绍我涉及到的部分。解密的部分可以通过OpenSSL
配合自定义DataSource
实现,native代码混淆可以使用ollvm
,他们的用法资料也非常多。
选择方案
得益于ExoPlayer清晰灵活的结构,很明确,ExoPlayer自定义数据源 + OpenSSL解密数据源 + ollvm native代码混淆。
初步实现
生产环境的大项目可不适合试错,打开配置好的模板demo,编写简单可播放sample的示例,自定义数据源参考ExoPlayer OkHttp extension
1 | // 创建数据源 |
OpenSSL解密数据源,配置native开发环境,编译OpenSSL动态(静态)链接库,导入头文件,参考官方demo实现AES解密的相关jni方法
1 | jbyte *key = (*env)->GetByteArrayElements(env, decryptKey, JNI_FALSE); |
使用FFmpeg准备好加密的HLS源,验证sample正确播放
1 | ffmpeg -i 0_blv.mp4 \ |
ollvm native代码混淆,编译支持混淆的llvm,修改ndk toolchain,配置支持混淆的ndk路径,因为native代码并不多,支持的混淆方式都加上了,最后用IDA Pro查看是否混淆成功,一行简单的return代码被加上了几十层循环。
1 | externalNativeBuild { |
优化细节
在应用到项目之前,可以尝试优化一些细节,比如在线播放的时候加上CacheDataSource
作为临时缓存,能极大提升拖动进度条的体验,减少流量消耗
1 | SimpleCache(downloadContentDirectory, LeastRecentlyUsedCacheEvictor(CACHE_MAX_SIZE)) |
提前尝试好各种参数配置,如loadControl
,LoadErrorHandlingPolicy
,整理好一些工具、辅助类方便处理MediaSession
、PlayerNotification
、截图、状态栏等等。清晰的错误报告这次不会错过了,处理好方法的异常,返回值,关键点日志,API,注释,混淆配置。
应用到项目
最后就需要耐心的接入到项目中了,发布SDK到maven仓库,细心的修改不同功能模块,细粒度的对比检查、提交代码。经过几周的测试,生产环境试用,最终优雅的解决了之前自定义协议带来的问题,网络加载问题得到了极大的改善,内存消耗减少,性能也显著提升。
通过使用ijkplayer了解到很多视频播放相关的知识,但每当我想一窥究竟的时候,里面的内容却是不熟悉的c代码。亲切的Java代码,加上ExoPlayer清晰的结构,把里面复杂的模块拆分展现出来,终于认识到我写的一串newSimpleInstance
代码到底是什么了。
参考资料: