Optimizing Video Player Performance: A Technical Analysis
Optimizing video player performance requires analyzing the entire playback pipeline—from network request and format detection through parsing, decoding, synchronization, and rendering—while implementing robust error monitoring, adaptive buffering, MP4 moov/mdat handling, stream‑specific optimizations, local proxy caching with video‑id reuse, and fallback to soft decoding or GLSurfaceView for smooth, artifact‑free playback.
优化播放器性能,我们首先要知道播放的完整流程,从播放的各个阶段分析视频播放存在的问题。
下面是播放的完整流程:
播放器加载一个网络url,首先要进行网络请求,网络如何优化,涉及到网络优化的方方面面。
网络拉取回来数据之后,识别一下当前视频的具体封装格式,这个可以正式流式视频,也可以是普通视频,优化的手段有点不同。
识别到具体的封装格式,按照封装格式的要求,开始解析封装格式,解析其中的音频流、视频流、字幕流等等。
音频流要解码成音频原始数据,视频流要解码成视频原始数据。
解码过程中注意音视频同步。
音频播放,同时视频开始渲染。
播放器发生状态异常,开发者可以明确获知播放器当前所处的状态。
每个状态都可能发生异常,发生异常都有具体的原因。利用播放器状态、播放器出错情况构建一个较为完善的播放监控体系。
播放卡顿,就是播放过程中发生loading,UI直接显示转圈,这对用户体验的损害是巨大的,用户在不断的吐槽中默默地卸载了我们的app。卡顿的主要原因是网络状况不好,很小的一部分原因是源的问题。
单次播放平均卡顿次数和卡顿时间是我们衡量播放流畅度的重要指标。
如果是源的问题,例如出现播放视频的时候,进度条在走,但是画面不走,就是视频解码出现问题,但是又没有出错,只是解码出的数据有问题。
解码出的数据有问题,有两种情况:原始数据就存在问题,这种情况下基本无法优化;另一种情况下是解码线程异常。
MediaCodec发生异常,解码线程异常错误。
监控发生问题时系统codec的具体状态,然后上报,便于分析问题。
播放失败的原因很多,使用播放器播放视频,最终都会在Player.onError回调中通知开发者播放失败了,最多返回一个错误码,对应一个播放错误。
总结而言,播放错误主要分为下面几类:
网络加载错误:网络请求发生问题,可能是网络请求的任何一个阶段。
视频格式识别错误:不支持当前的格式,或者当前格式识别出错。
解码出错:不支持当前视频、音频解码导致的出错,或者系统codec异常导致的问题。
文件的IO异常:读取缓存文件发生问题。
网络加载错误一般要视情况而定,网络超时要做好超时重试机制。
视频格式支持使用ffmpeg能解决基本上所有的视频格式的识别和处理工作。
MediaCodec解码受到手机硬件的制约,解码有时候会出错,出错可以切换到软解码。
本地代理可以实现将播放器的网络模块独立出来:
这样我们可以全局持有一个播放器实例:既可以做到预加载,而且可以解决播放器占用资源过多的问题。一举多得。
指定封装格式和解码格式:
针对一些视频,我们已经明确知道它们的封装格式和音视频的编解码格式,那我们就可以提前告知播放器这些信息,播放器直接使用特定的封装格式去嗅探,直接起特定的解码器去解码。
例如信息流视频基本上都是MP4的封装格式,H264的视频编码,AAC的音频编码。
这样我们可以节省嗅探和MediaCodec检索的时间。
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的首帧。
流式视频优化:
除了MP4视频,还有一些流式点播的视频,例如HLS格式,这些视频是一个一个的ts分片组成的。针对这些视频首帧的优化,我建议直接将前几个ts的分片的数据压缩,例如针对一个3s的ts视频,原来的分辨率是1280 * 720,现在可以压缩到320 * 180的大小,数据量大大降低,这样首帧就能快速加载下来。
边下边播:
我们在播放视频的时候,最好能边播放边缓存到本地,这样我二次打开这个视频的时候,就可以不用请求了,直接复用本地的数据。
边下边播可以使用本地代理来完成。
video-id复用缓存:
我们实行边下边播之后,二次打开可以复用缓存了,但是我们是根据视频的url来复用的,现在信息流视频的url经常变化的,即使是同一个视频,半小时视频url就发生了变化。
那这样的复用效率不是很低吗?
还好现在信息流都是传过来一个video-id,这个video-id不会随着视频url变化而变化,只要是同一个视频,video-id不会变化,那我们可以利用这个video-id来实现一次缓存,多次复用。
播放的时候出现丢帧:
播放时丢帧主要是直播应用会出现,当出现播放丢帧的情况,服务器应该主动推流低码率的流,防止客户端出现丢帧严重甚至卡住不走的情况。
播放画面出现锯齿:
播放视频的时候出现锯齿,这时候一般是两种情况:
视频清晰度较高,MediaCodec对搞清的视频解码支持地较弱,画面细腻感不强。建议切换到软解码开始解码,软解码使用CPU解码,对细节的支持度很强,甚至8K的视频都能很好的解析。
使用GLSurfaceView替换SurfaceView或者TextureView,GLSurfaceView通过OpenGL绘制纹理实现视频细节的细腻绘制,对视频画面支持效果很好。
END
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.