wake-up-neo.com

Android ExoPlayer onProgressChanged

Wie kann ich Fortschrittsänderungen bei ExoPlayer überwachen?
Ich habe versucht, eine versteckte MediaController-Methode zu implementieren und setOnSeekBarChangeListener-Methoden zu überschreiben, aber vorerst ohne Erfolg. Ich frage mich, ob es einen anderen Weg gibt, den Fortschritt von ExoPlayer zu hören.

23
ascallonisi

Ich weiß, dass diese Frage sehr alt ist. Bei der Implementierung von ExoPlayer bin ich jedoch dabei gelandet. Dies soll den anderen helfen, die später das Gleiche tun :)

Ich habe also die folgenden Methoden verwendet, um den Fortschritt der Wiedergabe zu verfolgen. So wird es in den ExoPlayer Google Text & Tabellen gemacht. Es funktioniert nach Bedarf.

Checkout PlayerControlView.Java in Google ExoPlayer-Repository

updateProgressBar() ist die Funktion zum Aktualisieren des SeekBar-Fortschritts:

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

Wir rufen updateProgressBar() innerhalb von updateProgressAction wiederholt auf, bis die Wiedergabe stoppt. Die Funktion wird bei jeder Zustandsänderung zum ersten Mal aufgerufen. Wir verwenden removeCallbacks(Runnable runnable), so dass immer eine updateProgressAction zur Verfügung steht.

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

Hoffe das hilft!

15
Ankit Aggarwal

Probieren Sie es einfach aus, es funktioniert für mich:

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

Hier, 

  • getCurrentPosition(): return Die aktuelle Wiedergabeposition in Millisekunden.
  • getDuration(): Die Dauer der Spur in Millisekunde.
4
SANAT

Ich bin nicht sicher, ob dies der beste Weg ist, aber ich habe dies durch Überladen von TrackRenderer erreicht.

Ich verwende videoPlayer.getBufferedPercentage(), aber Sie können möglicherweise auch den Prozentsatz selbst berechnen, indem Sie einfach TrackRenderers getBufferedPositionUs() und getDurationUs() verwenden.

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

Dies funktioniert zumindest mit Exoplayer 2. 

Es gibt vier Wiedergabestatus: STATE_IDLE, STATE_BUFFERING, STATE_READY und STATE_ENDED. 

Das Überprüfen des Wiedergabestatus ist einfach durchzuführen. Es gibt mindestens zwei Lösungen: if-Anweisung oder switch-Anweisung.

Unabhängig vom aktuellen Wiedergabestatus können Sie Ihre Methode ausführen oder eine andere Einstellung vornehmen, z. B. eine Fortschrittsleiste.

@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

Um es klar zu machen, gibt es in EventListener keinen Build für das Fortschrittsereignis. Sie können jedoch Handler.postDelayed in Ihrer updateProgress () - Funktion aufrufen, um den aktuellen Fortschritt abzurufen

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

weitere Informationen finden Sie in der Quelle von PlaybackControlView.Java in Exoplayer

2
ufxmeng

Ich bin nicht sicher, ob es der richtige Ansatz ist, aber ich habe EventBus und TimerTask verwendet, um den Fortschritt des abgespielten Audios zu aktualisieren. In meiner MusicController-Klasse stelle ich Folgendes ein:

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

}

Dann habe ich die Methode in der Variablen onPlayerStateChanged aufgerufen, als der Listener meiner SimpleExoPlayer-Instanz hinzugefügt wurde. Der obige Code sendet die verstrichene und die gesamte Dauer des Audiosignals alle 1 Sekunde (1000 ms) über den EventBus. Dann in der Aktivität, die die SeekBar hostet:

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

Erweitern Sie Ihre aktuelle Spielerklasse (z. B. SimpleExoPlayer) und fügen Sie hinzu 

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

Dann implementieren Sie in der Spieleraktivität einfach die Schnittstelle und starten Sie die Updates mit player.startProgressUpdater();.

0
Sough

Wenn Sie dies erreichen möchten, hören Sie einfach onPositionDiscontinuity(). Es gibt Ihnen Informationen, wenn das seekbar gescrubbt wird

0

Ich habe mit RxJava eine ziemlich elegante Lösung gefunden. Dies beinhaltet auch ein Abfragemuster, aber wir stellen sicher, dass alle 1 Sekunde ein Intervall zum Abfragen verwendet wird.

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

Die Logik hier ist, dass wir ein Observable erstellen, das jede Sekunde eine fortlaufende Nummer ausgibt. Dann verwenden wir den Operator map, um die Nummer in die aktuelle Wiedergabeposition zu transformieren.

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

Um dies endgültig zu erreichen, rufen Sie einfach subscribe an, und die Fortschrittsaktualisierungen werden jede Sekunde ausgegeben:

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

Hinweis: Observable.interval wird standardmäßig mit Scheduler von Schedulers.computation() ausgeführt. Daher müssen Sie wahrscheinlich einen observeOn()-Operator hinzufügen, um sicherzustellen, dass die Ergebnisse an den richtigen Thread gesendet werden.

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

Die obige Aussage gibt Ihnen ein Disposable , das entsorgt werden muss, wenn Sie mit dem Beobachten fertig sind ..__ Sie können so etwas tun ->

private var playbackDisposable: Disposable? = null

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

dann die Ressource zu entsorgen -> 

playbackDisposable?.dispose()
0
Felipe Roriz

Verwenden Sie onTouchListener nur mit 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;
                }
            });