Spielerereignisse

Wiedergabeereignisse überwachen

Ereignisse wie Statusänderungen und Wiedergabefehler werden an registrierte Player.Listener-Instanzen gemeldet. So registrieren Sie einen Listener für den Empfang solcher Ereignisse:

Kotlin

// Add a listener to receive events from the player.
player.addListener(listener)

Java

// Add a listener to receive events from the player.
player.addListener(listener);

Player.Listener enthält leere Standardmethoden, sodass Sie nur die gewünschten Methoden implementieren müssen. Eine vollständige Beschreibung der Methoden und des Zeitpunkts ihres Aufrufs finden Sie in Javadoc. Einige der wichtigsten Methoden werden unten ausführlicher beschrieben.

Listener können entweder einzelne Ereignis-Callbacks implementieren oder einen generischen onEvents-Callback implementieren, der aufgerufen wird, nachdem ein oder mehrere Ereignisse zusammen aufgetreten sind. Unter Individual callbacks vs onEvents finden Sie eine Erläuterung, welche für verschiedene Anwendungsfälle bevorzugt werden sollte.

Änderungen des Wiedergabestatus

Änderungen am Player-Status können durch Implementierung von onPlaybackStateChanged(@State int state) in einem registrierten Player.Listener empfangen werden. Der Player kann einen von vier Wiedergabestatus haben:

  • Player.STATE_IDLE: Dies ist der Anfangszustand sowie der Status, an dem der Player angehalten wird und bei dem die Wiedergabe fehlgeschlagen ist. In diesem Status verfügt der Player nur über begrenzte Ressourcen.
  • Player.STATE_BUFFERING: Der Spieler kann nicht sofort von der aktuellen Position aus wiedergeben. Dies liegt vor allem daran, dass mehr Daten geladen werden müssen.
  • Player.STATE_READY: Der Spieler kann von seiner aktuellen Position aus sofort abspielen.
  • Player.STATE_ENDED: Der Player hat die Wiedergabe aller Medien beendet.

Zusätzlich zu diesen Status hat der Spieler ein playWhenReady-Flag, das die Nutzerabsicht angibt, zu spielen. Änderungen in diesem Flag können durch Implementierung von onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) empfangen werden.

Ein Player wird wiedergegeben (d. h. seine Position geht weiter und dem Nutzer werden Medien präsentiert), wenn alle drei folgenden Bedingungen erfüllt sind:

  • Der Player befindet sich im Status Player.STATE_READY
  • playWhenReady ist true
  • Die Wiedergabe wird aus einem von Player.getPlaybackSuppressionReason zurückgegebenen Grund nicht unterdrückt

Anstatt diese Attribute einzeln zu prüfen, kann Player.isPlaying aufgerufen werden. Änderungen an diesem Status können durch Implementierung von onIsPlayingChanged(boolean isPlaying) empfangen werden:

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

Wiedergabefehler

Fehler, die dazu führen, dass die Wiedergabe fehlschlägt, können empfangen werden, indem onPlayerError(PlaybackException error) in einem registrierten Player.Listener implementiert wird. Wenn ein Fehler auftritt, wird diese Methode aufgerufen, kurz bevor der Wiedergabestatus in Player.STATE_IDLE übergeht. Fehlgeschlagene oder angehaltene Wiedergaben können durch Aufrufen von ExoPlayer.prepare wiederholt werden.

Beachten Sie, dass einige Player-Implementierungen Instanzen von abgeleiteten Klassen von PlaybackException übergeben, um zusätzliche Informationen über den Fehler bereitzustellen. Beispiel: ExoPlayer übergibt ExoPlaybackException, das type, rendererIndex und andere ExoPlayer-spezifische Felder enthält.

Das folgende Beispiel zeigt, wie du erkennen kannst, wenn eine Wiedergabe aufgrund eines HTTP-Netzwerkproblems fehlgeschlagen ist:

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

Playlist-Übergänge

Immer wenn der Player zu einem neuen Medienelement in der Playlist wechselt, wird onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) für registrierte Player.Listener-Objekte aufgerufen. Der Grund gibt an, ob es sich um einen automatischen Übergang, eine Suche (z. B. nach dem Aufruf von player.next()), eine Wiederholung desselben Elements oder eine Playlist-Änderung handelte (z. B. wenn das aktuell wiedergegebene Element entfernt wird).

Metadaten

Von player.getCurrentMediaMetadata() zurückgegebene Metadaten können sich aus verschiedenen Gründen ändern: Playlistübergänge, Aktualisierungen von In-Stream-Metadaten oder Aktualisierung des aktuellen MediaItem während der Wiedergabe.

Wenn Sie an Metadatenänderungen interessiert sind und beispielsweise eine UI aktualisieren möchten, in der der aktuelle Titel angezeigt wird, können Sie auf onMediaMetadataChanged warten.

Springen zu Videopositionen aktiviert

Der Aufruf von Player.seekTo-Methoden führt zu einer Reihe von Callbacks an registrierte Player.Listener-Instanzen:

  1. onPositionDiscontinuity mit reason=DISCONTINUITY_REASON_SEEK. Dies ist das direkte Ergebnis des Aufrufs von Player.seekTo. Der Callback hat PositionInfo-Felder für die Position vor und nach der Suche.
  2. onPlaybackStateChanged durch jede unmittelbare Statusänderung im Zusammenhang mit der Suche. Möglicherweise gibt es keine solche Änderung.

Einzelne Rückrufe im Vergleich zu onEvents

Listener können wählen, ob sie einzelne Callbacks wie onIsPlayingChanged(boolean isPlaying) oder den generischen onEvents(Player player, Events events)-Callback implementieren. Der generische Callback bietet Zugriff auf das Player-Objekt und gibt die Gruppe von events an, die zusammen aufgetreten ist. Dieser Callback wird immer nach den Callbacks aufgerufen, die den einzelnen Ereignissen entsprechen.

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

Java

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

Einzelne Ereignisse sollten in den folgenden Fällen bevorzugt werden:

  • Der Listener interessiert sich für die Gründe für Änderungen. z. B. die für onPlayWhenReadyChanged oder onMediaItemTransition angegebenen Gründe.
  • Der Listener verarbeitet nur die neuen Werte, die durch Callback-Parameter bereitgestellt werden, oder löst etwas anderes aus, das nicht von den Callback-Parametern abhängig ist.
  • Die Listener-Implementierung bevorzugt im Methodennamen eine deutlich lesbare Angabe darüber, was das Ereignis ausgelöst hat.
  • Der Listener meldet ein Analysesystem, das über alle einzelnen Ereignisse und Statusänderungen informiert werden muss.

Die generische onEvents(Player player, Events events) sollte in den folgenden Fällen bevorzugt werden:

  • Der Listener möchte dieselbe Logik für mehrere Ereignisse auslösen. Beispiel: Eine UI für onPlaybackStateChanged und onPlayWhenReadyChanged aktualisieren.
  • Der Listener benötigt Zugriff auf das Objekt Player, um weitere Ereignisse auszulösen, z. B. die Suche nach einem Medienelementübergang.
  • Der Listener beabsichtigt, mehrere Statuswerte zu verwenden, die über separate Callbacks gemeinsam oder in Kombination mit Player-Getter-Methoden gemeldet werden. Beispielsweise ist die Verwendung von Player.getCurrentWindowIndex() mit dem Timeline in onTimelineChanged nur innerhalb des onEvents-Callbacks sicher.
  • Der Listener möchte wissen, ob Ereignisse logisch zusammen aufgetreten sind. Beispiel: onPlaybackStateChanged zu STATE_BUFFERING aufgrund einer Umstellung eines Medienelements.

In einigen Fällen müssen Listener möglicherweise die einzelnen Callbacks mit dem generischen onEvents-Callback kombinieren, z. B. um Gründe für Änderungen von Medienelementen mit onMediaItemTransition aufzuzeichnen. Sie werden aber erst dann aktiv, wenn alle Statusänderungen gemeinsam in onEvents verwendet werden können.

AnalyticsListener werden verwendet

Bei Verwendung von ExoPlayer kann durch Aufrufen von addAnalyticsListener ein AnalyticsListener mit dem Spieler registriert werden. AnalyticsListener-Implementierungen können auf detaillierte Ereignisse warten, die für Analyse- und Logging-Zwecke nützlich sein können. Weitere Informationen finden Sie auf der Analytics-Seite.

EventLogger werden verwendet

EventLogger ist ein AnalyticsListener, der für Logging-Zwecke direkt von der Bibliothek bereitgestellt wird. Fügen Sie EventLogger zu einem ExoPlayer hinzu, um nützliches zusätzliches Logging mit einer einzigen Zeile zu ermöglichen:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Weitere Informationen finden Sie auf der Seite zum Debug-Logging.

Ereignisse an bestimmten Wiedergabepositionen werden ausgelöst

In einigen Anwendungsfällen müssen Ereignisse an bestimmten Wiedergabepositionen ausgelöst werden. Dies wird mit PlayerMessage unterstützt. Ein PlayerMessage kann mit ExoPlayer.createMessage erstellt werden. Die Wiedergabeposition, an der es ausgeführt werden soll, kann mit PlayerMessage.setPosition festgelegt werden. Nachrichten werden standardmäßig im Wiedergabethread ausgeführt. Dies kann jedoch mit PlayerMessage.setLooper angepasst werden. Mit PlayerMessage.setDeleteAfterDelivery kann gesteuert werden, ob die Nachricht jedes Mal ausgeführt wird, wenn die angegebene Wiedergabeposition erreicht wird (dies kann beim Such- und Wiederholungsmodus mehrere Male passieren) oder nur beim ersten Mal. Sobald die PlayerMessage konfiguriert ist, kann sie mit PlayerMessage.send geplant werden.

Kotlin

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

Java

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();