ExoPlayer之seekto掉帧解决办法

随着项目的迭代,在调研了竞品使用的播放器方案之后,最终把播放器从原始的MediaPlayer迁移到ExoPlayer上来。原始的MediaPlayer播放器存在以下问题:
1.seekTo的时候掉帧,只能seekto到指定位置的上一个关键帧
2.进度回调不准(在轮询时间片段的时候,时间跳跃明显,多个视频串联的时候)
3.多个视频衔接的时间中卡顿(从A视频切换到B视频的过程中,需要reset播放器才能再初始化下一个MediaPlayer,无法做到无缝衔接)
4.不支持视频快慢放(或者说需要6.0以上机型)

像乐秀这样的app,在衔接处黑屏,然后再播放下一个视频,使用了IMediaPlayer,用户体验很差;Quick在播放的过程中,衔接流畅,反编译之后得知其使用了ExoPlayer。

在使用的过程中,由于无法直接使用SimExoplayerView播放,因为我们的需求需要添加滤镜,特效之类的,需要GLSurfaceView作为载体。其次,在使用SimExoplayerView播放的过程中,可以发现快速拖动进度条的时候,画面并不会实时预览,不能做到mediaplayer那样的预览效果。其后查看Quick,发现它的预览速度很快,一开始怀疑是否它们自己改了源码,实现了快速拖动的效果,后来在搞清楚了ExoPlayer的基本状态之后,弄清楚了处理的基本流程。即seekto完上一帧之后,再seekto下一帧,在seekto的过程中不再调用seekto。

public void realSeekTo(int msec) {
if (mExoPlayer != null) {
//如果上次滑动的还没结束,那么保存状态,等seekto结束再拉动
if (mTargetPlayStatue == SEEKED) {
mTargetRealSeekPos = msec;
mHandler.sendEmptyMessageDelayed(MSG_SEEK_TO, SEEK_ADJUSTMENT_TIME);
} else {
mHandler.removeMessages(MSG_PLAYING);
mHandler.removeMessages(MSG_FORCE_SEEK_TO);
mTargetSeekPos = IDLE_POS;
mExoPlayer.seekTo(msec);
mPlayStatue = SEEKING;
mTargetPlayStatue = SEEKED;
}
}
}

假设我们正在拖拽进度条,回调是2400ms, 2500ms, 2600ms,3000ms,如果我们直接ExoPlayer.seekTo(msec);那么画面会卡住,直到ExoPlayer.seekTo(3000)的事件完成。 这是因为ExoPlayer.seekTo(2400);还没完成,我们又调用了ExoPlayer.seekTo(2500);以此类推。
正确的做法是如果拖拽还没完成,我们应该把最后seekto的position保存起来。如果在seekto状态结束的时候,需要seekto的position不为0。那么继续seekto。这样,我们就可以看到画面一直在刷新,不会出现掉帧的情况。

此外,在某些情况下,seekto之后onPlayerStateChanged是不会有Player.STATE_READY:的回调,这意味着没有seekcompleted的回调。这就需要我们在seekto的时候,给当前的操作设置一个超时时间,如果超时了还未回调seekcompleted,我们把这时的状态当成seekcompleted。(这种情况出现有两种可能:1.视频的帧数太少,seekto的操作需要耗时很久; 2.seekto的目标position和当前播放器的position一样,不会有Player.STATE_READY回调)

所以,在

public void onPlayerStateChanged(boolean playWhenReady, int
playbackState) {
//准备好了,可以直接播放
case Player.STATE_READY:
stateString = “ExoPlayer.STATE_READY-“;
if (mTargetPlayStatue == SEEKED) {
handlerSeekComplete();
mTargetPlayStatue = playWhenReady ? PLAYING : PAUSE;
if (mTargetSeekPos != IDLE_POS) {
//普通拉动进度条的seekto
seekTo(mTargetSeekPos);
}
}
}

理解了这个流程,我们不难实现ExoPlayer的实时预览。
ps:ExoPlayer默认seekto的时候是精准帧,在2.7.0之后可以通过ExoPlayer.setSeekParameters(SeekParameters.XXX);修改为上一帧,下一帧,临近帧,精准帧四种。

你可能感兴趣的:(安卓,android)