ExoPlayer
の進行状況の変更を監視するにはどうすればよいですか?
非表示のMediaController
とsetOnSeekBarChangeListener
メソッドをオーバーライドしようとしましたが、今のところ成功していません。 ExoPlayer
の進行状況を聞く別の方法があるかどうか疑問に思っています。
私はこの質問が非常に古いことを知っています。しかし、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();
}
_
お役に立てれば!
これを試してください、それは私のために働いています:
_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()
:ミリ秒単位のトラックの継続時間。それが最善の方法かどうかはわかりませんが、TrackRenderer
をオーバーロードすることでこれを達成しました。
私はvideoPlayer.getBufferedPercentage()
を使用していますが、TrackRenderer
のgetBufferedPositionUs()
と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);
}
}
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()
_
これは少なくとも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);
}
}
明確にするために、進捗イベント用の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 のソースを参照してください。
それが正しいアプローチかどうかはわかりませんが、 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);
}
現在のプレーヤークラス(例: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();
で更新を開始します
これを達成したい場合は、onPositionDiscontinuity()
を聞いてください。 seekbar
がスクラブされている場合、情報を提供します
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;
}
});