分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。
分类: Android平台
2020-11-25 09:38:35
下面是播放的完整流程:
根据我们平时的开发实践,我们总结出播放过程中常见的几类问题:
面对这些问题,我们急切需要知道两方面的数据:
这两个问题是有有递进关系的,“怎么监控这些问题”就是为了更好地“解决这类问题”。
我们得知上面的痛点,在发生这些问题时,我们要收集相应的数据分析这类问题,不然开发者一头雾水,不知道播放过程的数据信息,解决问题全靠运气。
播放视频首要的是网络加载,网络请求是一个复杂的过程,全链路的点太多,将全链路的所有点收集起来,可以在播放器中加上网络的全链路监控:
这样我们对网络的整体加载情况有了全面的把握,发生网络加载问题,也知道是哪个点出现了问题,分析解决问题有了更加全的数据。
开篇就分析了播放器的完整流程,其实开发者也非常需要当前播放器的运行状态:
播放器的工作状态也可以拆解一下:
播放器发生状态异常,开发者可以明确获知播放器当前所处的状态。
每个状态都可能发生异常,发生异常都有具体的原因。利用播放器状态、播放器出错情况构建一个较为完善的播放监控体系。
播放卡顿,就是播放过程中发生loading,UI直接显示转圈,这对用户体验的损害是巨大的,用户在不断的吐槽中默默地卸载了我们的app。卡顿的主要原因是网络状况不好,很小的一部分原因是源的问题。
单次播放平均卡顿次数和卡顿时间是我们衡量播放流畅度的重要指标。
如果是源的问题,例如出现播放视频的时候,进度条在走,但是画面不走,就是视频解码出现问题,但是又没有出错,只是解码出的数据有问题。
解码出的数据有问题,有两种情况:原始数据就存在问题,这种情况下基本无法优化;另一种情况下是解码线程异常。
监控发生问题时系统codec的具体状态,然后上报,便于分析问题。
播放失败的原因很多,使用播放器播放视频,最终都会在Player.onError回调中通知开发者播放失败了,最多返回一个错误码,对应一个播放错误。
总结而言,播放错误主要分为下面几类:
网络加载错误一般要视情况而定,网络超时要做好超时重试机制。
视频格式支持使用ffmpeg能解决基本上所有的视频格式的识别和处理工作。
MediaCodec解码受到手机硬件的制约,解码有时候会出错,出错可以切换到软解码。
平时刷信息流视频的时候,其实很多视频的域名都是相同的,这些链接都是可以复用的,网络建连的时间需要30ms到200ms不等,如果能复用链接,这部分的时间是可以节省下来的。
一个播放器实例持有的数据非常大,player初始化的时候会初始化MediaCodec,MediaCodec对应底层的AVCodec,操作底层的/dev/codec-node,Android系统规定了系统最大持有的MediaCodec实例是16个,当然每个手机会有所不同,但总的来说不会有大的不同,就是MediaCodec的实例个数是有限的,不可能无限创建实例。
我们预加载多个播放器实例的时候,就会创建多个codec实例,超过codec实现限制,系统codec就无法正常工作,极易造成OOM或者ANR。
我们再平时解决问题的时候经常发现media.codec进程导致SystemServer卡死的。一般都是media.codec使用不当造成的。
那现在能否预加载不起播放器实例?
我们预加载的目的是为了请求视频资源,其实只需要网路模块就可以的。
本地代理可以实现将播放器的网络模块独立出来:
这样我们可以全局持有一个播放器实例:既可以做到预加载,而且可以解决播放器占用资源过多的问题。一举多得。
针对一些视频,我们已经明确知道它们的封装格式和音视频的编解码格式,那我们就可以提前告知播放器这些信息,播放器直接使用特定的封装格式去嗅探,直接起特定的解码器去解码。
例如信息流视频基本上都是MP4的封装格式,H264的视频编码,AAC的音频编码。
这样我们可以节省嗅探和MediaCodec检索的时间。
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的首帧。
除了MP4视频,还有一些流式点播的视频,例如HLS格式,这些视频是一个一个的ts分片组成的。针对这些视频首帧的优化,我建议直接将前几个ts的分片的数据压缩,例如针对一个3s的ts视频,原来的分辨率是1280 * 720,现在可以压缩到320 * 180的大小,数据量大大降低,这样首帧就能快速加载下来。
我们在播放视频的时候,最好能边播放边缓存到本地,这样我二次打开这个视频的时候,就可以不用请求了,直接复用本地的数据。
边下边播可以使用本地代理来完成。
我们实行边下边播之后,二次打开可以复用缓存了,但是我们是根据视频的url来复用的,现在信息流视频的url经常变化的,即使是同一个视频,半小时视频url就发生了变化。
那这样的复用效率不是很低吗?
还好现在信息流都是传过来一个video-id,这个video-id不会随着视频url变化而变化,只要是同一个视频,video-id不会变化,那我们可以利用这个video-id来实现一次缓存,多次复用。
播放时丢帧主要是直播应用会出现,当出现播放丢帧的情况,服务器应该主动推流低码率的流,防止客户端出现丢帧严重甚至卡住不走的情况。
播放视频的时候出现锯齿,这时候一般是两种情况:
作者:Li Tianpeng