项目中使用的播放器是ijkPlayer,发现播放切片特点的hls流(m3u8格式的视频)拖动seekBar的时候会莫名的跳转或者seek不到准确的位置,发现网友也遇到了同样的问题,ijk的开发者也说明了是因为UI层的问题导致的,需要自己排查。涉及到该问题的链接:
通过ijkPlayer播放m3u8视频时快进不准确的解决方案
为什么Sample里面的进度条,往前拖动进度条后,还会往后退几秒
向前拖动,进度条会往回跳
bbcallen commented
UI部分seekbar的回调处理得不太合理,如果放手很快,最后一个位置不会被传给播放器,建议自行修改。
既然开发者都说了,那么就老实分析代码吧。因为项目中用到的MediaController
继承自Android系统的MediaController
,所以还得看看源码,分析得出系统中实现是将seek的listener监听器放在onProgressChanged
这个方法中,这也是为什么我们断断续续拖动的时候播放器也会播放,知道这点就够了,把onProgressChanged
中的mPlayer.seekTo((int) newposition);
放到onStopTrackingTouch
方法中。
执行顺序是:
onStartTrackingTouch(执行一次) —> onProgressChanged(拖动就会不停的执行) —> onStopTrackingTouch(停止后最后执行一次)
实现代码如下:
public class CustomMediaController extends MediaController implements ICustomMediaController { // ....................代码省略............................. // There are two scenarios that can trigger the seekbar listener to trigger: // // The first is the user using the touchpad to adjust the posititon of the // seekbar's thumb. In this case onStartTrackingTouch is called followed by // a number of onProgressChanged notifications, concluded by onStopTrackingTouch. // We're setting the field "mDragging" to true for the duration of the dragging // session to avoid jumps in the position in case of ongoing playback. // // The second scenario involves the user operating the scroll ball, in this // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications, // we will simply apply the updated position without suspending regular updates. private OnSeekBarChangeListener mSeekListener=new OnSeekBarChangeListener(){ long newposition; public void onStartTrackingTouch(SeekBar bar){ show(3600000); mDragging=true; if(seekerBarDraggingListener!=null) seekerBarDraggingListener.getCurrentDraggingstatus(mDragging); // By removing these pending progress messages we make sure // that a) we won't update the progress while the user adjusts // the seekbar and b) once the user is done dragging the thumb // we will post one of these messages to the queue again and // this ensures that there will be exactly one message queued up. mHandler.removeMessages(SHOW_PROGRESS); } public void onProgressChanged(SeekBar bar,int progress,boolean fromuser){ if(!fromuser){ // We're not interested in programmatically generated changes to // the progress bar's position. return; } long duration=mPlayer.getDuration(); newposition=(duration*progress)/1000L; // 系统原来的实现是在progress改变的时候时刻都在进行videoplayer的seek //这会导致seek m3u8切片文件的时候拖动seek时不准确,所以需要在拖动完成后才进行播放器的seekTo() // mPlayer.seekTo((int) newposition); if(mCurrentTime!=null) mCurrentTime.setText(stringForTime((int)newposition)); } public void onStopTrackingTouch(SeekBar bar){ mDragging=false; mPlayer.seekTo((int)newposition); if(seekerBarDraggingListener!=null) seekerBarDraggingListener.getCurrentDraggingstatus(mDragging); setProgress(); updatePausePlay(); if(isntNeedStayShowAfterDrag){ show(sDefaultTimeout); // Ensure that progress is properly updated in the future, // the call to show() does not guarantee this because it is a // no-op if we are already showing. mHandler.sendEmptyMessage(SHOW_PROGRESS); } } }; // ....................代码省略.............................}