web-dev-qa-db-ja.com

Android ExoPlayer onProgressChanged

ExoPlayerの進行状況の変更を監視するにはどうすればよいですか?
非表示のMediaControllersetOnSeekBarChangeListenerメソッドをオーバーライドしようとしましたが、今のところ成功していません。 ExoPlayerの進行状況を聞く別の方法があるかどうか疑問に思っています。

29
ascallonisi

私はこの質問が非常に古いことを知っています。しかし、ExoPlayerを実装している間にこれを見つけました。これは、後で同じことをする他の人を助けるためです:)

そこで、次の方法に従って、再生の進行状況を追跡しました。これは、ExoPlayer Googleドキュメントで行われる方法です。必要に応じて機能します。

チェックアウト_PlayerControlView.Java_ in Google ExoPlayerリポジトリ

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

再生が停止するまで、updateProgressAction内でupdateProgressBar()を繰り返し呼び出します。この関数は、状態が変化するたびに最初に呼び出されます。 removeCallbacks(Runnable runnable)を使用して、常に1つのupdateProgressActionが存在するようにします。

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

お役に立てれば!

21
Ankit Aggarwal

これを試してください、それは私のために働いています:

_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():ミリ秒単位のトラックの継続時間。
9
SANAT

それが最善の方法かどうかはわかりませんが、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
Nezo

RxJavaを使用した非常にエレガントなソリューションを見つけました。これにはポーリングパターンも含まれますが、必ず1秒ごとにポーリングする間隔を使用してください。

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

ここでのロジックは、1秒ごとに連番を発行するObservableを作成することです。次に、map演算子を使用して、数値を現在の再生位置に変換します。

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

最後にこれをフックするには、subscribeを呼び出すだけで、進行状況の更新が毎秒発行されます。

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

注:_Observable.interval_は、Schedulers.computation()のデフォルトのSchedulerで実行されます。したがって、おそらく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()
_
3
Felipe Roriz

これは少なくともExoplayer 2で動作します。

4つの再生状態があります:STATE_IDLE、STATE_BUFFERING、STATE_READY、およびSTATE_ENDED。

再生状態の確認は簡単です。少なくとも2つの解決策があります:ifステートメントまたはswitchステートメント。

再生状態が何であれ、メソッドを実行したり、プログレスバーなどの別の設定をしたりできます。

@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
user7459244

明確にするために、進捗イベント用のEventListenerにはビルドはありませんが、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
ufxmeng

それが正しいアプローチかどうかはわかりませんが、 EventBus およびTimerTaskを使用して、再生中のオーディオの進行状況を更新しました。 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);
    }
2
Javad Arjmandi

現在のプレーヤークラス(例: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
Sough

これを達成したい場合は、onPositionDiscontinuity()を聞いてください。 seekbarがスクラブされている場合、情報を提供します

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