Android ExoPlayer onProgressChanged
Как отслеживать изменения прогресса на ExoPlayer
?
Я попытался реализовать скрытые MediaController
и переопределяющие методы setOnSeekBarChangeListener
, но пока безуспешно. Мне интересно, есть ли другой способ прослушать прогресс ExoPlayer
.
Ответы
Ответ 1
Я знаю, что этот вопрос очень старый. Но я приземлился на это во время реализации ExoPlayer
. Это поможет другим, кто сделает то же самое позже :)
Итак, я следовал следующим методам, чтобы отслеживать ход воспроизведения. Именно так это и делается в ExoPlayer
Google Docs. Работает по мере необходимости.
Оформить заказ PlayerControlView.java
в хранилище 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();
}
};
Мы вызываем updateProgressBar()
в пределах updateProgressAction
, пока воспроизведение не остановится.
Функция вызывается в первый раз, когда происходит изменение состояния. Мы используем removeCallbacks(Runnable runnable)
, так что всегда есть один updateProgressAction
, о котором нужно заботиться.
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
updateProgressBar();
}
Надеюсь, это поможет!
Ответ 2
Просто попробуй, его работа для меня:
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()
: return Текущая позиция воспроизведения в миллисекундах.
-
getDuration()
: продолжительность дорожки в миллисекундах.
Ответ 3
Не уверен, что это лучший способ, но я достиг этого, перегрузив 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);
}
}
Ответ 4
Я нашел довольно элегантное решение с использованием 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() } );
Чтобы окончательно подключить все это, просто позвоните в службу подписки, и каждую секунду будут появляться обновления прогресса:
playbackProgressObservable.subscribe( { progress -> // Update logic here } )
Примечание. Observable.interval
работает по умолчанию Scheduler
из Schedulers.computation()
. Поэтому вам, вероятно, потребуется добавить оператор observeOn()
, чтобы убедиться, что результаты отправляются в нужный поток.
playbackProgressObservable
.observeOn(AndroidSchedulers.mainThead())
.subscribe(progress -> {}) // Update logic here
Приведенное выше выражение даст вам одноразовый, который необходимо утилизировать, когда вы закончите наблюдение.
Вы можете сделать что-то вроде этого ->
private var playbackDisposable: Disposable? = null
playbackDisposable = playbackProgressObservable
.observeOn(AndroidSchedulers.mainThead())
.subscribe(progress -> {}) // Update logic here
затем распорядиться ресурсом ->
playbackDisposable?.dispose()
Ответ 5
Это работает как минимум с 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);
}
}
Ответ 6
Чтобы было ясно, в событии прогресса не существует сборки EventListener, но вы можете вызвать Handler.postDelayed внутри функции updateProgress(), чтобы получить текущий прогресс.
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();
}
};
для получения дополнительной информации см. источник PlaybackControlView.java внутри Exoplayer
Ответ 7
Я не уверен, что это правильный подход, но я использовал 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();
}
}
}
Затем я вызвал метод внутри onPlayerStateChanged
при добавлении слушателя в мой экземпляр SimpleExoPlayer. Приведенный выше код отправляет прошедшую и общую длительность воспроизводимого звука каждую 1 секунду (1000 мс) через EventBus. Тогда внутри деятельности, принимающей SeekBar:
@Subscribe(threadMode = ThreadMode.MAIN)
public void updateProgress(ProgressNotification pn) {
seekBar.setMax((int) pn.duration);
seekBar.setProgress((int) pn.currentPosition);
}
Ответ 8
Расширьте свой текущий класс игрока (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();
Ответ 9
Используйте только 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;
}
});
Ответ 10
Если вы хотите достичь этого, просто послушайте onPositionDiscontinuity()
. Он даст вам информацию, если seekbar
очищается
Ответ 11
для меня сработала простая тема:
private void startUpdatingProgress(TextView tvProgress, SimpleExoPlayer exoPlayer){
new Thread(() -> {
while (exoPlayer.getCurrentPosition() <= exoPlayer.getDuration()){
SystemClock.sleep(1000);
runOnUiThread(() -> {
Log.e(TAG, "run: posting on ui thread!" + " current:" +exoPlayer.getCurrentPosition()+ "total:" +exoPlayer.getDuration() );
tvProgress.setText(String.format(Locale.ROOT, "%d",(int) ((exoPlayer.getCurrentPosition() * 100) / exoPlayer.getDuration())));
});
}
}).start();
}