前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >企鹅FM(Android) 播放成功率从 2 个 9 到 3 个 9 的蜕变

企鹅FM(Android) 播放成功率从 2 个 9 到 3 个 9 的蜕变

原创
作者头像
serena
修改2021-08-03 14:56:11
3.8K1
修改2021-08-03 14:56:11
举报
文章被收录于专栏:社区的朋友们社区的朋友们

作者:张陈博男

业务层播放器架构演变

企鹅FM android端的播放器架构经历过两次较大的调整

第一次是2.1版本,首次引入了以FFmpeg为基础的腾讯视频SDK,替换了之前一直使用的系统播放器,结束了不同机型上表现不一和调用其api在不同版本上出现莫名其妙崩溃的历史,点播成功率最终优化到99.7%左右,HSL直播的成功率优化到97%附近。

第二次是3.7版本,使用了Google的开源播放器内核ExoPlayer替换了腾讯视频SDK,到目前最新的3.8版本,点播成功率已经优化到99.9%,HLS直播成功率优化到99.2%

最近半年包括更换ExoPlayer的诸多努力,都是朝着99.9%这个方向去的。这个优化过程中,最艰辛的是具体问题case by case的解决,不过回过头从架构上看,也是可以提炼出一些原则,来甄别到底什么样才是一个好的业务播放器。

  1. 一套统一的代码,这也是作为一个优秀的业务播放器的必备条件,否则假如建立在系统播放器基础上(各个厂商都会修改系统播放器代码),同样的实现在不同的机型上几乎无法做到表现一致,更遑论成功率了(常常是改动了调用方式后,在一个机型ok另一个机型不行,不同系统版本间也有此类问题),所以实现一个好的播放器,第一步就是先统一播放器内核。
  2. 完善的错误信息统计,播放是一个复杂的行为,牵扯到数据的预加载,加载,解码和最终给到系统AudioTrack播放,当支持了分片加载和缓存后这个模型就变得更加复杂,于是错误是不能避免的,但最重要的是,如何通过错误的统计上报,能够直指问题的根本,在此基础上,才有空间进一步优化成功率。
  3. 播放器内核和业务层足够解耦,只有设计上的解耦,才能给更换更好的播放器内核打下基础,否则如果每次切换都会带来巨大的业务逻辑调整,本身就会引入很多和播放器无关的问题,对成功率优化会适得其反。
  4. 和播放器内核对接的功能模块尽可能结构简单,这符合KIS原则,要在可扩展性和模块的结构简单易维护上作出协调,当代码足够简单直白,问题往往会更容易暴露和得到解决。

横向对比3中播放器内核:

播放器

代码统一 错误统计

接入层复杂度

系统MediaPlayer

不完善,播放错误码分散而且很多错误错误码相同

腾讯视频SDK

不完善,过滤日志 + 播放错误 转化为业务层错误码

谷歌ExoPlayer

除MediaCodec以外是

完善,所有错误都通过java层异常抛出,直接转化成对应处理逻辑或者业务层错误码

注:这里的接入层指的是为了实现完整的业务逻辑,在播放器内核外围的逻辑层

换ExoPlayer与奥卡姆剃刀

常做优化的同学肯定很清楚,越是小数点后面的9,越来之不易,90%到99%再到99.9%,这其中的困难可以说是指数上升的。那么是什么东西去鼓动我们换掉已经维护的很成熟的腾讯视频SDK而换用谷歌的ExoPlayer呢,动力来自于寻求到3个9的突破,而思想来自于奥卡姆剃刀原则——如无必要,勿增实体。

腾讯视频很完善,具备一切我们需要的功能,但是太过于庞杂:最下层是FFmpeg,然后是C++实现的播放器逻辑,上层一个java接口层和部分逻辑。因为发起请求的逻辑封装在播放器底层,所以为了实现分片下载和缓存的策略,增加了一层本地的Http代理。

这个架构可以完整的实现所有我们要的播放功能,可以处理播放请求,也可以分片下载和缓存,也可以添加音效和改变播放速度,但是问题也有不少:

  1. C/C++层的逻辑过多,首先、这部分逻辑不易维护,产生的逻辑问题,主要通过日志来排查。其次、C/C++层的逻辑一旦出异常,堆栈极其难以定位到原因,而且就算定位到了,FFmpeg带来的问题也比较棘手。再次、处理数据就必须经过多次jni传递,这降低了效率。
  2. 本地代理带来的结构上的冗余。引入本地代理是因为腾讯视频SDK的请求部分是写死的,无法在其中再加入我们自身的比如文件头zip压缩和分片下载缓存的逻辑。但是本地代理本身把一个请求的链路拖长了,而且本地tcp socket同样有这各种各样的断开问题和连接超时问题,实质上增加了整个系统出错的概率。

于是当发现ExoPlayer能够很完善解决这两个问题的时候,我们就进行了替换

  1. 得益于ExoPlayer高度可扩展的特性,我们去除了本地代理模块,将分片加载和缓存以及音频的变速和特效处理模块直接集成进来,砍掉了很多冗余的通信
  2. 而且采用了ExoPlayer以后去除了大部分的C/C++层逻辑,剩下的jni通信基本都属于系统组件,譬如MediaCodec和AudioTrack,对于实现者来说可以当做透明,目前日登陆百万的用户量来看,MediaCodec在各个机型上兼容性较好,投诉较少(目前仅收到两例初始化MediaCodec失败的投诉)
  3. ExoPlayer纯Java的实现,也帮助了我们尽可能收归各种错误信息,转换成业务错误码

总体来讲,剔除掉了不必要的逻辑后,代码更加的简洁,而且数据的路径也更加简短,这提升了可维护度和降低了出错概率。

其他的补充手段

仅仅靠换播放器内核和重新设计业务逻辑接入是没法做到极致的,这里还针对点播和广播(HLS)做了一些额外的优化

  1. ExoPlayer是通过抛异常来上报各种播放错误的,起初我们把全部的异常都算在播放错误中,导致播放错误偏高,后来发现这里面很多异常其实是自己代码实现的逻辑问题,需要解决,所以播放错误仅仅应当统计播放下载过程中无法解决的问题,而不应该包括代码的逻辑缺陷,后者应当继续抛出crash,由bugly上报解决。
  2. MediaExtractorPeriod和HlsMediaChunk的cancelLoadable()方法都没有调用dataSource的close()方法,这里我们加上了这个调用,原因在于快速切换节目时,如果不关闭前一个正在进行的连接有可能导致大量连接堆积会耗尽socket或者是Http连接池中的资源
  3. 针对播放HLS中的BehindLiveWindow异常进行一定次数重试,该问题通常是资源问题或者连接太慢导致,可以通过重试恢复
  4. 针对免流带来的连接超时问题,3.8版本加入了针对王卡优先直连(联通王卡类支持腾讯IP免流)的策略,也进一步提升了成功率

总结

只要保持代码架构的简洁和解耦,有着良好的错误信息反馈机制,加上长期的问题跟进,打造99.9%的成功率也不是难事,对吗。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 业务层播放器架构演变
  • 换ExoPlayer与奥卡姆剃刀
  • 其他的补充手段
  • 总结
相关产品与服务
云点播
面向音视频、图片等媒体,提供制作上传、存储、转码、媒体处理、媒体 AI、加速分发播放、版权保护等一体化的高品质媒体服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档