web-dev-qa-db-fra.com

Android ExoPlayer onProgressChanged

Comment puis-je surveiller les changements de progression sur ExoPlayer?
J'ai essayé d'implémenter une méthode MediaController masquée et surchargée setOnSeekBarChangeListener, mais pour l'instant sans succès. Je me demande s’il existe un autre moyen d’écouter les progrès ExoPlayer.

23
ascallonisi

Je sais que cette question est très ancienne. Mais, j'ai atterri là-dessus tout en implémentant ExoPlayer. C'est pour aider les autres qui font la même chose plus tard :)

J'ai donc suivi les méthodes suivantes pour suivre les progrès de la lecture. C’est comme cela que l’on procède dans la ExoPlayer Google Docs. Cela fonctionne au besoin.

Checkout PlayerControlView.Java in - Référentiel Google ExoPlayer

updateProgressBar() est la fonction permettant de mettre à jour la progression 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();
    }
};

Nous appelons updateProgressBar() dans updateProgressAction à plusieurs reprises jusqu'à ce que la lecture s'arrête . La fonction est appelée pour la première fois à chaque changement d'état. Nous utilisons removeCallbacks(Runnable runnable) pour qu'il y ait toujours une updateProgressAction à prendre en compte.

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

J'espère que cela t'aides!

15
Ankit Aggarwal

Essayez ceci, ça marche pour moi:

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

Ici, 

  • getCurrentPosition(): return La position de lecture actuelle en millisecondes.
  • getDuration(): durée de la piste en millisecondes.
4
SANAT

Je ne suis pas sûr que ce soit la meilleure solution, mais j’y suis parvenu en surchargeant la TrackRenderer.

J'utilise videoPlayer.getBufferedPercentage(), mais vous pouvez également calculer le pourcentage vous-même en utilisant simplement TrackRenderer's getBufferedPositionUs() et 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

Cela fonctionne au moins avec Exoplayer 2. 

Il existe quatre états de lecture: STATE_IDLE, STATE_BUFFERING, STATE_READY et STATE_ENDED. 

Vérifier l'état de la lecture est facile à faire. Il existe au moins deux solutions: instruction if ou instruction switch.

Quel que soit l'état de lecture en cours, vous pouvez exécuter votre méthode ou définir quelque chose d'autre, par exemple 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
user7459244

Pour être clair, il n'y a pas de construction dans EventListener pour l'événement progress, mais vous pouvez appeler Handler.postDelayed à l'intérieur de la fonction updateProgress () pour obtenir la progression actuelle.

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

pour plus de détails, voir la source de PlaybackControlView.Java inside Exoplayer

2
ufxmeng

Je ne sais pas si c'est la bonne approche, mais j'ai utilisé EventBus et TimerTask pour mettre à jour la progression de l'audio en cours de lecture. Dans mon cours MusicController, j'ai mis:

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

}

J'ai ensuite appelé la méthode dans la variable onPlayerStateChanged lors de l'ajout du programme d'écoute à mon instance SimpleExoPlayer. Le code ci-dessus envoie la durée écoulée et la durée totale de l'audio lu toutes les 1 seconde (1000 ms) via EventBus. Ensuite, à l'intérieur de l'activité hébergeant la barre SeekBar:

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

Étendez votre classe de joueurs actuelle (SimpleExoPlayer par exemple) et ajoutez 

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

Ensuite, dans l'activité du lecteur, implémentez l'interface et démarrez les mises à jour avec player.startProgressUpdater();

0
Sough

J'ai trouvé une solution assez élégante avec RxJava. Cela implique également un modèle d’interrogation, mais nous nous assurons d’utiliser un intervalle pour interroger toutes les secondes.

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

La logique ici est de créer un observable qui émettra un numéro séquentiel chaque seconde. Nous utilisons ensuite l'opérateur map pour transformer le nombre en position de lecture actuelle.

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

Pour finalement relier cela ensemble, appelez simplement subscribe, et les mises à jour de progression seront émises toutes les secondes:

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

Remarque: Observable.interval s’exécute sur une Scheduler par défaut de Schedulers.computation(). Par conséquent, vous devrez probablement ajouter un opérateur observeOn() pour vous assurer que les résultats sont envoyés au bon thread.

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

La déclaration ci-dessus vous donnera un Jetable qui doit être éliminé une fois que vous avez terminé d’observer .. Vous pouvez faire quelque chose comme ceci ->

private var playbackDisposable: Disposable? = null

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

puis disposer de la ressource -> 

playbackDisposable?.dispose()
0
Felipe Roriz

Utilisez uniquement onTouchListener avec 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;
                }
            });