Android ExoPlayer onProgressChanged

问题描述 投票:26回答:10

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

android listener progress exoplayer
10个回答
19
投票

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

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

PlayerControlView.java结账Google ExoPlayer repository

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();
}

希望这可以帮助!


0
投票

如果你想完成这个,就听听onPositionDiscontinuity()。如果seekbar正在擦洗,它会给你信息


9
投票

试试吧,它为我工作:

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():轨道的持续时间,以毫秒为单位。

3
投票

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

我正在使用videoPlayer.getBufferedPercentage(),但你也可以自己计算百分比,只需使用TrackRenderergetBufferedPositionUs()getDurationUs()

public interface ProgressListener {
    public void onProgressChange(long progress);
}

public class CustomVideoRenderer extends  MediaCodecVideoTrackRenderer {

    long progress = 0;
    private final CopyOnWriteArraySet<ProgressListener> progressListeners = new CopyOnWriteArraySet();

    // [...]
    // Skipped constructors
    // [...]

    public void doSomeWork(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
        super.doSomeWork(positionUs, elapsedRealtimeUs);

        long tmpProgress = videoPlayer.getBufferedPercentage();

        if (tmpProgress != this.progress) {
            this.progress = tmpProgress;

            for (ProgressListener progressListener : this.progressListeners) {
                progressListener.onProgressChange(progress);
            }
        }
    }

    public void addProgressListener(ProgressListener listener) {
        this.progressListeners.add(listener);
    }

}

3
投票

我使用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.interval运行在Scheduler的默认Schedulers.computation()上。因此,您可能需要添加observeOn()运算符以确保将结果发送到正确的线程。

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

上述声明将为您提供一个Disposable,必须在您完成观察后处理。你可以这样做 - >

private var playbackDisposable: Disposable? = null

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

然后处置资源 - >

playbackDisposable?.dispose()

2
投票

这至少适用于Exoplayer 2。

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

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

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

@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);
    }

}

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的来源


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);
    }

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();开始更新


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;
                }
            });
© www.soinside.com 2019 - 2024. All rights reserved.