1. 背景
今天TV产品优化,发现一个难以修改的问题,使用gsyVideoPlayer的时候,切换的是系统内核播放视频,然后遥控器在控制快进快退的时候会有进度条回退的问题,这对用户就不友好了啊。
2. 原因探究
调查了之后发现,是因为关键帧的问题,快进的时候是按照视频的关键帧来进行快进的,如果视频背压缩的过于严重会导致视频的关键帧比较少,那么就会出现以下这种情况:
现在播放到8秒,你要快进到18秒,但是视频的关键帧在第15秒,那么进度条就会从18退到15,因为进度条是按照真实的视频进度来进行走动的。
3. 解决方案
知道了原因之后那么有两种方案:
方案一:从系统内核切换到Exo内核,因为我发现三种内核都测试过之后,Exo内核对于关键帧的处理更好,基本上不会出现快进回退的问题。
方案二:维持原有的系统内核,禁用gsyVideoPlayer原有的进度条展示,自己实现进度条,时间的展示。
4. 实现
4.1 方案一测试(失败)
采用了之后播放.mp4视频一点问题没有,非常流畅,实现了我想要的效果,但是现网的视频后缀是.m3u8格式的,使用Exo内核播放之后,报了如下错误:
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): Source error.
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (MatroskaExtractor, FragmentedMp4Extractor, Mp4Extractor, Mp3Extractor, AdtsExtractor, Ac3Extractor, TsExtractor, FlvExtractor, OggExtractor, PsExtractor, WavExtractor, AmrExtractor) could read the stream.
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractorHolder.selectExtractor(ExtractorMediaPeriod.java:927)
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:849)
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:320)
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
05-18 11:26:07.632: E/ExoPlayerImplInternal(10506): at java.lang.Thread.run(Thread.java:841)
05-18 11:26:07.632: E/EventLogger(10506): playerFailed [0.78]
05-18 11:26:07.632: E/EventLogger(10506): com.google.android.exoplayer2.ExoPlaybackException
05-18 11:26:07.632: E/EventLogger(10506): at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:349)
05-18 11:26:07.632: E/EventLogger(10506): at android.os.Handler.dispatchMessage(Handler.java:98)
05-18 11:26:07.632: E/EventLogger(10506): at android.os.Looper.loop(Looper.java:136)
05-18 11:26:07.632: E/EventLogger(10506): at android.os.HandlerThread.run(HandlerThread.java:61)
05-18 11:26:07.632: E/EventLogger(10506): Caused by: com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (MatroskaExtractor, FragmentedMp4Extractor, Mp4Extractor, Mp3Extractor, AdtsExtractor, Ac3Extractor, TsExtractor, FlvExtractor, OggExtractor, PsExtractor, WavExtractor, AmrExtractor) could read the stream.
05-18 11:26:07.632: E/EventLogger(10506): at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractorHolder.selectExtractor(ExtractorMediaPeriod.java:927)
05-18 11:26:07.632: E/EventLogger(10506): at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:849)
05-18 11:26:07.632: E/EventLogger(10506): at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:320)
05-18 11:26:07.632: E/EventLogger(10506): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
05-18 11:26:07.632: E/EventLogger(10506): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
05-18 11:26:07.632: E/EventLogger(10506): at java.lang.Thread.run(Thread.java:841)
05-18 11:26:07.632: D/EventLogger(10506): loading [false]
这个报错看了半天,主要意思是说Exoplayer中没有可用的解析器,然后我去找了解决办法,说是需要设置如下代码:
//EXOPlayer内核,支持格式更多
PlayerFactory.setPlayManager(Exo2PlayerManager.class);
//exo缓存模式,支持m3u8,只支持exo
CacheFactory.setCacheManager(ExoPlayerCacheManager.class);
试了并不行,不知道是我的问题还是播放器的问题,最后也没找到解决办法,直接放弃。
4.2 方案二测试(可行)
首选我继承StandardGSYVideoPlayer创建了一个新的Player,然后重写其中的 setTextAndProgress方法,将super.setTextAndProgress(secProgress);方法注释掉即可,运行发现进度条不走,当前时间跟总时间一直在00:00.,然后只需要自己写一个计时器,让进度条每隔1s刷新一次将当前时间跟进度更新掉就好。写到这我就在想另外一个问题,它本身已经写了一个计时器了在更新进度了,我在写一个控制岂不是多余了嘛?本着不重复造轮子的态度,我进行了如下改动:
/**
* 处理快进快退问题
* @param isForward
*/
public void step(boolean isForward) {
if (!getGSYVideoManager().isPlaying()) {
PLog.i("fastForward is not playing");
getStartButton().callOnClick();
}
int videoLength = (int) getGSYVideoManager().getDuration();
if (videoLength == -1) {
return;
}
int size = videoLength / LENGTH;// LENGTH快进快退的步长
final int[] postion = new int[size + 2];
postion[size + 1] = videoLength;
for (int i = 0; i <= size; i++) {
postion[i] = LENGTH * i;
}
mBottomContainer.setVisibility(VISIBLE);
if (isForward) { // 快进
if (mCurrentPosition == videoLength) {
setProgress(videoLength, videoLength);
return;
}
for (int i = 0; i < postion.length; i++) {
if (postion[i] - mCurrentPosition > 0) {
PLog.i("forwart postion=" + postion[i]);
setProgress(postion[i], videoLength);
getGSYVideoManager().seekTo(mCurrentPosition);
break;
}
}
} else { // 快退
if (mCurrentPosition == 0) {
setProgress(0, videoLength);
return;
}
for (int i = 0; i < postion.length; i++) {
if (postion[i] - mCurrentPosition >= 0) {
setProgress(postion[i - 1], videoLength);
getGSYVideoManager().seekTo(mCurrentPosition);
break;
}
}
}
}
/**
* 快进快退 自己控制 进度条 避免快进 回退问题
*
* @param position
* @param videoLength
*/
public void setProgress(int position, int videoLength) {
mCurrentPosition = position;
int progress = (int) (mCurrentPosition / (videoLength / 100));
mProgressBar.setProgress(progress);
mTotalTimeTextView.setText(CommonUtil.stringForTime(videoLength));
mCurrentTimeTextView.setText(CommonUtil.stringForTime(position));
mHadler.postDelayed(new Runnable() {
@Override
public void run() {
mBottomContainer.setVisibility(GONE);
}
}, 500);
}
@Override
protected void setTextAndProgress(int secProgress) { // 避免关键帧 回退的问题
if (!mProgressBar.isShown()) {
super.setTextAndProgress(secProgress);
}
}
我做了一个假象,在进度条展示的时候,我会按照自己分割的关键帧去快进快退,这样控制强行让进度条永远按照你想要的方向走,但是当500ms进度条消失之后,我让进度条按照gsyVideoPlayer原有的计时器进行更新,这样我不仅省了一个计时器并且实现了了想要的效果,进度条在显示的时候永远不会回退,消失之后可能会有稍微回退,但是用户看不到了,问题应该不大。
5. 总结
最后还是希望不要将视频压缩的太过严重,关键帧太少,视频清晰度降低,对用户的体验更不好,在用户能看到的地方一定要将体验做好,可以在看不到的地方做优化,本次优化主要针对于遥控器控制的快进快退,手机开发的小伙伴可以借鉴下思路。
6. 参考
1. https://blog.csdn.net/yhroppo/article/details/104494850
2. github地址 :https://github.com/CarGuo/GSYVideoPlayer