关于客户端:播放器性能优化之路

48次阅读

共计 3322 个字符,预计需要花费 9 分钟才能阅读完成。

上面是播放的残缺流程:

  • 播放器加载一个网络 url,首先要进行网络申请,网络如何优化,波及到网络优化的方方面面。
  • 网络拉取回来数据之后,辨认一下以后视频的具体封装格局,这个能够正式流式视频,也能够是一般视频,优化的伎俩有点不同。
  • 辨认到具体的封装格局,依照封装格局的要求,开始解析封装格局,解析其中的音频流、视频流、字幕流等等。
  • 音频流要解码成音频原始数据,视频流要解码成视频原始数据。
  • 解码过程中留神音视频同步。
  • 音频播放,同时视频开始渲染。

一、播放痛点

依据咱们平时的开发实际,咱们总结出播放过程中常见的几类问题:

  • 播放失败率高
  • 播放首帧慢
  • 播放卡顿
  • 播放器占用 CPU、内存过高

面对这些问题,咱们急切需要晓得两方面的数据:

  • 怎么监控这些问题
  • 怎么解决这类问题

这两个问题是有有递进关系的,“怎么监控这些问题”就是为了更好地“解决这类问题”。

二、监控伎俩

咱们得悉下面的痛点,在产生这些问题时,咱们要收集相应的数据分析这类问题,不然开发者一头雾水,不晓得播放过程的数据信息,解决问题全靠运气。

1、网络加载监控

播放视频首要的是网络加载,网络申请是一个简单的过程,全链路的点太多,将全链路的所有点收集起来,能够在播放器中加上网络的全链路监控:

这样咱们对网络的整体加载状况有了全面的把握,产生网络加载问题,也晓得是哪个点呈现了问题,剖析解决问题有了更加全的数据。

2、播放器全链路监控

开篇就剖析了播放器的残缺流程,其实开发者也十分须要以后播放器的运行状态:

播放器的工作状态也能够拆解一下:

播放器产生状态异样,开发者能够明确获知播放器以后所处的状态。

每个状态都可能产生异样,产生异样都有具体的起因。利用播放器状态、播放器出错状况构建一个较为欠缺的播放监控体系。

3、播放器晦涩度监控

播放卡顿,就是播放过程中产生 loading,UI 间接显示转圈,这对用户体验的侵害是微小的,用户在一直的吐槽中默默地卸载了咱们的 app。卡顿的次要起因是网络情况不好,很小的一部分起因是源的问题。

  • 卡顿的次数
  • 卡顿的时长
  • 卡登时的网速

单次播放均匀卡顿次数和卡顿工夫是咱们掂量播放晦涩度的重要指标。

如果是源的问题,例如呈现播放视频的时候,进度条在走,然而画面不走,就是视频解码呈现问题,然而又没有出错,只是解码出的数据有问题。

解码出的数据有问题,有两种状况:原始数据就存在问题,这种状况下根本无奈优化;另一种状况下是解码线程异样。

  • MediaCodec 产生异样
  • 解码线程异样谬误

监控产生问题时零碎 codec 的具体状态,而后上报,便于剖析问题。

三、播放成功率优化

播放失败的起因很多,应用播放器播放视频,最终都会在 Player.onError 回调中告诉开发者播放失败了,最多返回一个错误码,对应一个播放谬误。

总结而言,播放谬误次要分为上面几类:

  • 网络加载谬误: 网络申请产生问题,可能是网络申请的任何一个阶段。
  • 视频格式辨认谬误: 不反对以后的格局,或者以后格局辨认出错。
  • 解码出错: 不反对以后视频、音频解码导致的出错,或者零碎 codec 异样导致的问题。
  • 文件的 IO 异样: 读取缓存文件产生问题。

网络加载谬误个别要视状况而定,网络超时要做好超时重试机制。

视频格式反对应用 ffmpeg 能解决基本上所有的视频格式的辨认和解决工作。

MediaCodec 解码受到手机硬件的制约,解码有时候会出错,出错能够切换到软解码。

四、播放性能优化

1、复用链接:

平时刷信息流视频的时候,其实很多视频的域名都是雷同的,这些链接都是能够复用的,网络建连的工夫须要 30ms 到 200ms 不等,如果能复用链接,这部分的工夫是能够节省下来的。

2、预加载

一个播放器实例持有的数据十分大,player 初始化的时候会初始化 MediaCodec,MediaCodec 对应底层的 AVCodec,操作底层的 /dev/codec-node,Android 零碎规定了零碎最大持有的 MediaCodec 实例是 16 个,当然每个手机会有所不同,但总的来说不会有大的不同,就是 MediaCodec 的实例个数是无限的,不可能有限创立实例。

咱们预加载多个播放器实例的时候,就会创立多个 codec 实例,超过 codec 实现限度,零碎 codec 就无奈失常工作,极易造成 OOM 或者 ANR。

咱们再平时解决问题的时候常常发现 media.codec 过程导致 SystemServer 卡死的。个别都是 media.codec 使用不当造成的。

那当初是否预加载不起播放器实例?

咱们预加载的目标是为了申请视频资源,其实只须要网路模块就能够的。

本地代理能够实现将播放器的网络模块独立进去:

  • 播放器不间接和视频源服务器交互,两头通过本地代理层交互。
  • 本地代理层的网络加载模块是独立于播放器的,能够是播放器发动申请,也能够是其余的内部调用发动申请。
  • 最终 setDataSource 到播放器的 url 是一个 http://127.0.0.1:port 的申请,本地代理层会通过 Socket 向这个 url 中发送数据,播放器能够间接解析数据流。跟失常的播放流程齐全一样。

这样咱们能够全局持有一个播放器实例:既能够做到预加载,而且能够解决播放器占用资源过多的问题。一举多得。

3、指定封装格局和解码格局:

针对一些视频,咱们曾经明确晓得它们的封装格局和音视频的编解码格局,那咱们就能够提前告知播放器这些信息,播放器间接应用特定的封装格局去嗅探,间接起特定的解码器去解码。

例如信息流视频基本上都是 MP4 的封装格局,H264 的视频编码,AAC 的音频编码。

这样咱们能够节俭嗅探和 MediaCodec 检索的工夫。

4、MP4 视频优化:

MP4 格局的视频解析进去如下:

其中 moov 中蕴含着 MP4 文件特有的属性数据,mdat 是具体的音频和视频数据,MP4 格局规定,只有解析出 moov 数据之后,能力解析出 mdat 中具体的音频和视频数据。

然而 moov 有时候在 mdat 之前,有工夫在 mdat 之后,如上,moov 在 mdat 之前,那么咱们程序申请就没有问题。

然而如果 moov 在 mdat 之后,咱们程序申请就播放不进去,这时候须要起双 IO 缓冲加载:一个从头开始检索 moov,另一个从开端检索 moov,虽找到 moov,就能够先解析出 moov,而后解析 mdat,播放视频了。

然而双 IO 毕竟比拟耗时,如果能在服务器提前将 MP4 视频的 moov 移到 mdat 之前,就能够晋升 MP4 的首帧。

5、流式视频优化:

除了 MP4 视频,还有一些流式点播的视频,例如 HLS 格局,这些视频是一个一个的 ts 分片组成的。针对这些视频首帧的优化,我倡议间接将前几个 ts 的分片的数据压缩,例如针对一个 3s 的 ts 视频,原来的分辨率是 1280 720,当初能够压缩到 320 180 的大小,数据量大大降低,这样首帧就能疾速加载下来。

6、边下边播

咱们在播放视频的时候,最好能边播放边缓存到本地,这样我二次关上这个视频的时候,就能够不必申请了,间接复用本地的数据。

边下边播能够应用本地代理来实现。

7、video-id 复用缓存

咱们履行边下边播之后,二次关上能够复用缓存了,然而咱们是依据视频的 url 来复用的,当初信息流视频的 url 常常变动的,即便是同一个视频,半小时视频 url 就产生了变动。

那这样的复用效率不是很低吗?

还好当初信息流都是传过来一个 video-id,这个 video-id 不会随着视频 url 变动而变动,只有是同一个视频,video-id 不会变动,那咱们能够利用这个 video-id 来实现一次缓存,屡次复用。

五、其余播放体验优化倡议

1、播放的时候呈现丢帧

播放时丢帧次要是直播利用会呈现,当呈现播放丢帧的状况,服务器应该被动推流低码率的流,避免客户端呈现丢帧重大甚至卡住不走的状况。

2、播放画面呈现锯齿

播放视频的时候呈现锯齿,这时候个别是两种状况:

  • 视频清晰度较高,MediaCodec 对搞清的视频解码反对地较弱,画面细腻感不强。倡议切换到软解码开始解码,软解码应用 CPU 解码,对细节的反对度很强,甚至 8K 的视频都能很好的解析。
  • 应用 GLSurfaceView 替换 SurfaceView 或者 TextureView,GLSurfaceView 通过 OpenGL 绘制纹理实现视频细节的细腻绘制,对视频画面反对成果很好。

作者:Li Tianpeng

正文完
 0