Android ExoPlayer onProgressChanged

时间:2015-02-18 18:10:00

标签: android listener progress exoplayer

如何监控ExoPlayer的进度变化?
我尝试实现隐藏的MediaController和重写setOnSeekBarChangeListener方法,但现在没有成功。我想知道是否还有另一种方式可以听取ExoPlayer进展。

16 个答案:

答案 0 :(得分:17)

我知道这个问题很老了。但是,我在实施ExoPlayer时落实了这一点。这是为了帮助后来做同样事情的其他人:)

因此,我已按照以下方法跟踪播放进度。这是在ExoPlayer Google文档中完成的方式。它可以根据需要运作。

Google ExoPlayer repository

中的结帐PlayerControlView.java

updateProgressBar()是更新SeekBar进度的函数:

private void updateProgressBar() {
    long duration = player == null ? 0 : player.getDuration();
    long position = player == null ? 0 : player.getCurrentPosition();
    if (!dragging) {
        mSeekBar.setProgress(progressBarValue(position));
    }
    long bufferedPosition = player == null ? 0 : player.getBufferedPosition();
    mSeekBar.setSecondaryProgress(progressBarValue(bufferedPosition));
    // Remove scheduled updates.
    handler.removeCallbacks(updateProgressAction);
    // Schedule an update if necessary.
    int playbackState = player == null ? Player.STATE_IDLE : player.getPlaybackState();
    if (playbackState != Player.STATE_IDLE && playbackState != Player.STATE_ENDED) {
        long delayMs;
        if (player.getPlayWhenReady() && playbackState == Player.STATE_READY) {
            delayMs = 1000 - (position % 1000);
            if (delayMs < 200) {
                delayMs += 1000;
            }
        } else {
            delayMs = 1000;
        }
        handler.postDelayed(updateProgressAction, delayMs);
    }
}

private final Runnable updateProgressAction = new Runnable() {
    @Override
    public void run() {
        updateProgressBar();
    }
};

我们反复在updateProgressBar()内呼叫updateProgressAction,直到播放停止。 只要状态发生变化,就会第一次调用该函数。我们使用removeCallbacks(Runnable runnable),因此总有一个updateProgressAction需要关注。

@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
  updateProgressBar();
}

希望这有帮助!

答案 1 :(得分:7)

试试这个,它为我工作:

handler = new Handler();
runnable = new Runnable() {
      @Override
      public void run() {
           progressbar.setProgress((int) ((exoPlayer.getCurrentPosition()*100)/exoPlayer.getDuration()));
           handler.postDelayed(runnable, 1000);
      }
};
handler.postDelayed(runnable, 0);

在这里,

  • getCurrentPosition():返回当前播放位置(以毫秒为单位)。
  • getDuration():以毫秒为单位的音轨持续时间。

答案 2 :(得分:3)

不确定这是最好的方法,但我通过重载TrackRenderer来实现这一点。

我使用videoPlayer.getBufferedPercentage(),但您也可以自己计算百分比,只需使用TrackRenderer&#39; getBufferedPositionUs()和{{1 }}

getDurationUs()

答案 3 :(得分:2)

这至少适用于Exoplayer 2.

有四种播放状态:STATE_IDLE,STATE_BUFFERING,STATE_READY和STATE_ENDED。

检查播放状态很容易。至少有两个解决方案:if-statement或switch-statement。

无论播放状态如何,您都可以执行您的方法或设置其他内容,例如进度条。

@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
    if (playbackState == ExoPlayer.STATE_ENDED) {
        showControls();
        Toast.makeText(getApplicationContext(), "Playback ended", Toast.LENGTH_LONG).show();
    }
    else if (playbackState == ExoPlayer.STATE_BUFFERING)
    {
        progressBar.setVisibility(View.VISIBLE);
        Toast.makeText(getApplicationContext(), "Buffering..", Toast.LENGTH_SHORT).show();
    }
    else if (playbackState == ExoPlayer.STATE_READY)
    {
        progressBar.setVisibility(View.INVISIBLE);
    }

}

答案 4 :(得分:2)

为清楚起见,在ProgressListener中没有为progress事件构建,但你可以在updateProgress()函数中调用Handler.postDelayed来获取当前进度

 private void updateProgress(){

    //get current progress 
    long position = player == null ? 0 : player.getCurrentPosition();
    //updateProgress() will be called repeatedly, you can check 
    //player state to end it
    handler.postDelayed(updateProgressAction,1000)

}

 private final Runnable updateProgressAction = new Runnable() {
    @Override
    public void run() {        
      updateProgress();
    }
};

有关详细信息,请参阅Exoplayer中的PlaybackControlView.java来源

答案 5 :(得分:2)

我使用RxJava找到了一个非常优雅的解决方案。这也涉及轮询模式,但我们确保每隔1秒使用一个间隔轮询。

public Observable<Long> playbackProgressObservable = 
                            Observable.interval(1, TimeUnit.SECONDS)

这里的逻辑是我们创建一个Observable,它将每秒发出一个序号。然后我们使用map运算符将数字转换为当前播放位置。

public Observable<Long> playbackProgressObservable = 
                        Observable.interval(1, TimeUnit.SECONDS)
                                  .map( { exoPlayer.getCurrentPosition() } );

最后将这个联系在一起,只需调用subscribe,ad就会每秒发出进度更新:

playbackProgressObservable.subscribe( { progress -> // Update logic here } )

注意:Observable.intervalScheduler的默认Schedulers.computation()上运行。因此,您可能需要添加observeOn()运算符以确保将结果发送到正确的线程。

playbackProgressObservable
    .observeOn(AndroidSchedulers.mainThead())        
    .subscribe(progress -> {}) // Update logic here 

上述声明将为您提供一次性,必须在您完成观察后处理。 你可以这样做 - &gt;

private var playbackDisposable: Disposable? = null

       playbackDisposable = playbackProgressObservable
        .observeOn(AndroidSchedulers.mainThead())        
        .subscribe(progress -> {}) // Update logic here

然后处置资源 - &gt;

playbackDisposable?.dispose()

答案 6 :(得分:2)

我不确定它是否是正确的方法,但我使用EventBusTimerTask来更新正在播放的音频的进度。 在我的MusicController类中,我把:

private void sendElapsedDuration() {
    //To send the current elapsed time
    final Timer t = new Timer();
    Runnable r = new Runnable() {
        @Override
        public void run() {
            t.schedule(new TimerTask() {
                @Override
                public void run() {
                    EventBus.getDefault().post(
                            new ProgressNotification(
                                    player.getCurrentPosition(), player.getDuration())
                    );
                    if (player.getCurrentPosition() >= player.getDuration() ){
                        // The audio is ended, we pause the playback,
                        // and reset the position
                        player.seekTo(0);
                        player.setPlayWhenReady(false);
                        this.cancel();
                        // stopping the Runnable to avoid memory leak
                        mainHandler.removeCallbacks(this);
                    }
                }
            },0,1000);
        }
    };
    if(player != null) {
        if (player.getPlaybackState() != Player.STATE_ENDED)
            mainHandler.postDelayed(r, 500);
        else {
            //We put the TimerTask to sleep when audio is not playing
            t.cancel();
        }
    }

}

然后,在将侦听器添加到SimpleExoPlayer实例时,我在onPlayerStateChanged内调用了该方法。上面的代码通过EventBus每隔1秒(1000毫秒)发送正在播放的音频的经过时间和总持续时间。然后在托管SeekBar的活动中:

@Subscribe(threadMode = ThreadMode.MAIN)
    public void updateProgress(ProgressNotification pn) {
        seekBar.setMax((int) pn.duration);
        seekBar.setProgress((int) pn.currentPosition);
    }

答案 7 :(得分:1)

我也遇到了这个问题,我找到了这个link

的解决方案

但是解决方案:

1。。创建一个这样的类:

git log --ancestry-path --format="%ae, %an" origin/development..origin/OtherBranch

2。,最后在您的代码中使用它:

git log

3。在最后一步中,当您要释放播放器(重要)

时,请不要忘记调用purgeHandler。
   public class ProgressTracker implements Runnable {

        public interface PositionListener{
            public void progress(long position);
        }
    
        private final Player player;
        private final Handler handler;
        private PositionListener positionListener;
    
        public ProgressTracker(Player player, PositionListener positionListener) {
            this.player = player;
            this.positionListener = positionListener;
            handler = new Handler();
            handler.post(this);
        }
    
        public void run() {
            long position = player.getCurrentPosition();
            positionListener.progress(position);
            handler.postDelayed(this, 1000);
        }
    
        public void purgeHandler() {
            handler.removeCallbacks(this);
        }
    }

答案 8 :(得分:1)

好吧,我是通过 kotlin flow 做到的..

private fun audioProgress(exoPlayer: SimpleExoPlayer?) = flow<Int> {
        while (true) {
            emit(((exoPlayer?.currentPosition?.toFloat()?.div(exoPlayer.duration.toFloat())?.times(100))?.toInt()!!))
            delay(1000)
        }
    }.flowOn(Dispatchers.IO)

然后像这样收集进度...

val audioProgressJob = launch {
            audioProgress(exoPlayer).collect {
                MP_progress_bar.progress = it
            }
        }

答案 9 :(得分:0)

扩展您当前的播放器类(例如SimpleExoPlayer)并添加

public interface PlayerEventsListener {
        void onSeek(int from, int to);
        void onProgressUpdate(long progress);
}

private PlayerEventsListener mListener;
private Handler mHandler;
private Runnable mProgressUpdater;
private boolean isUpdatingProgress = false;

public SomePlayersConstructor(Activity activity, /*...*/) {
    //...
    mListener = (PlayerEventsListener) activity;
    mHandler = new Handler();
    mProgressUpdater = new ProgressUpdater();
}

// Here u gain access to seek events
@Override
public void seekTo(long positionMs) {
    mListener.onSeek(-1, (int)positionMs/1000);
    super.seekTo(positionMs);
}

@Override
public void seekTo(int windowIndex, long positionMs) {
    mListener.onSeek((int)getCurrentPosition()/1000, (int)positionMs/1000);
    super.seekTo(windowIndex, positionMs);
}

// Here u gain access to progress
public void startProgressUpdater() {

    if (!isUpdatingProgress) {
        mProgressUpdater.run();
        isUpdatingProgress = true;
    }
}

private class ProgressUpdater implements Runnable {

    private static final int TIME_UPDATE_MS = 500;

    @Override
    public void run() {
        mListener.onProgressUpdate(getCurrentPosition());
        mHandler.postDelayed(mProgressUpdater, TIME_UPDATE_MS);
    }
}

然后在玩家活动内部实现界面并使用player.startProgressUpdater();

开始更新

答案 10 :(得分:0)

仅将onTouchListener与MotionEvent.ACTION_UP一起使用

    SeekBar exo_progress = (SeekBar) findViewById(R.id.exo_progress);
    exo_progress.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_UP) {
                        //put your code here!!
                    }
                    return false;
                }
            });

答案 11 :(得分:0)

如果要完成此操作,只需听onPositionDiscontinuity()。如果您正在擦洗seekbar,它将为您提供信息

答案 12 :(得分:0)

对我来说,一个简单的线程起作用了:

DESKTOP-PJD3O2Q\John Doe

答案 13 :(得分:0)

rx java 实现:

private val disposablesVideoControlsDisposable = CompositeDisposable()

fun showVideoControlsAndSimilarTray() {
    videoSeekBar?.setOnSeekBarChangeListener(object :
        SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
            if (fromUser) seekVideoProgress(progress)
        }

        override fun onStartTrackingTouch(seekBar: SeekBar?) {}
        override fun onStopTrackingTouch(seekBar: SeekBar?) {}

    })
    val disposable = Observable.interval(0, 1, TimeUnit.SECONDS)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe {
            calculateVideoProgress()
        }
    disposablesVideoControlsDisposable.add(disposable)
}

private fun calculateVideoProgress() {
    val currentMill = exoPlayer.currentPosition
    val totalMillis = exoPlayer.duration
    if (totalMillis > 0L) {
        val remainMillis = (totalMillis - currentMill).toFloat() / 1000
        val remainMins = (remainMillis / 60).toInt()
        val remainSecs = (remainMillis % 60).toInt()
        videoProgressText.setText("$remainMins:${String.format("%02d", remainSecs)}")
        seekBarProgress.set((currentMill.toFloat() / totalMillis * 100).toInt())
    }
    
}

private fun seekVideoProgress(progress: Int) {
    val seekMillis = exoPlayer.duration.toFloat() * progress / 100
    exoPlayer.seekTo(seekMillis.toLong())
}

最后当你完成时:

   fun disposeVideoControlsObservable() {
    disposablesVideoControlsDisposable.clear()
}

答案 14 :(得分:0)

很简单

var player = SimpleExoPlayer.Builder(context).build();
player.addListener(object:Player.Listener{
     override fun onEvents(player: Player, events: Player.Events) {
        super.onEvents(player, events)
                if (events.containsAny(
                Player.EVENT_IS_LOADING_CHANGED,
                Player.EVENT_PLAYBACK_STATE_CHANGED,
                Player.EVENT_PLAY_WHEN_READY_CHANGED,
                Player.EVENT_IS_PLAYING_CHANGED
              )) {
                   log(msg="progres ${player.currentPosition}") 
               }
     }

})

您可以在 1356 行查看 com.google.android.exoplayer2.ui.PlayerControlView.java

答案 15 :(得分:-1)

如果您正在完全或部分使用播放器视图或播放器控件视图(仅用于按钮或其他功能),则可以直接从其中设置进度监听器:

 PlayerControlView playerControlView = miniPlayerCardView.findViewById(R.id.playerView);
    ProgressBar audioProgressBar = miniPlayerCardView.findViewById(R.id.audioProgressBar);

    playerControlView.setProgressUpdateListener((position, bufferedPosition) -> {
        int progressBarPosition = (int) ((position*100)/player.getDuration());
        int bufferedProgressBarPosition = (int) ((bufferedPosition*100)/player.getDuration());
        audioProgressBar.setProgress(progressBarPosition);
        audioProgressBar.setSecondaryProgress(bufferedProgressBarPosition);
    });