Spielerereignisse

Auf Wiedergabeereignisse warten

Ereignisse wie Statusänderungen und Wiedergabefehler werden an registrierte Player.Listener-Instanzen gemeldet. So registrieren Sie einen Listener, der solche Ereignisse empfängt:

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 hat leere Standardmethoden. Sie müssen also nur die Methoden implementieren, die Sie interessieren. Eine vollständige Beschreibung der Methoden und der Zeitpunkte, zu denen sie aufgerufen werden, finden Sie im Javadoc. Einige der wichtigsten Methoden werden im Folgenden genauer beschrieben.

Listener haben die Wahl zwischen der Implementierung einzelner Event-Callbacks oder eines generischen onEvents-Callbacks, der nach dem gemeinsamen Auftreten eines oder mehrerer Events aufgerufen wird. Informationen dazu, welche für verschiedene Anwendungsfälle bevorzugt werden sollten, finden Sie unter Individual callbacks vs onEvents.

Änderungen des Wiedergabestatus

Änderungen am Player-Status können empfangen werden, indem Sie onPlaybackStateChanged(@State int state) in einem registrierten Player.Listener implementieren. Der Player kann sich in einem von vier Wiedergabestatus befinden:

  • Player.STATE_IDLE: Dies ist der Ausgangsstatus, der Status, wenn der Player gestoppt ist und wenn die Wiedergabe fehlgeschlagen ist. Der Player hat in diesem Zustand nur begrenzte Ressourcen.
  • Player.STATE_BUFFERING: Der Player kann nicht sofort von der aktuellen Position aus wiedergeben. Das liegt meistens daran, dass mehr Daten geladen werden müssen.
  • Player.STATE_READY: Der Player kann sofort von seiner aktuellen Position aus wiedergeben.
  • Player.STATE_ENDED: Der Player hat die Wiedergabe aller Medien abgeschlossen.

Zusätzlich zu diesen Status hat der Player das Flag playWhenReady, um die Absicht des Nutzers anzugeben, das Video abzuspielen. Änderungen an diesem Flag können durch die Implementierung von onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) empfangen werden.

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

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

Anstatt diese Eigenschaften einzeln prüfen zu müssen, kann Player.isPlaying aufgerufen werden. Änderungen an diesem Status können durch die 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 durch die Implementierung von onPlayerError(PlaybackException error) in einem registrierten Player.Listener empfangen werden. Wenn ein Fehler auftritt, wird diese Methode unmittelbar vor dem Übergang des Wiedergabestatus zu Player.STATE_IDLE aufgerufen. Fehlgeschlagene oder angehaltene Wiedergaben können durch Aufrufen von ExoPlayer.prepare wiederholt werden.

Bei einigen Player-Implementierungen werden Instanzen von Unterklassen von PlaybackException übergeben, um zusätzliche Informationen zum Fehler zu liefern. Beispiel: ExoPlayer wird an ExoPlaybackException übergeben, das type, rendererIndex und andere ExoPlayer-spezifische Felder enthält.

Das folgende Beispiel zeigt, wie Sie erkennen, 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 Media-Element 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, einen Seek (z. B. nach dem Aufrufen von player.next()), eine Wiederholung desselben Elements oder eine Änderung der Playlist handelt (z. B. wenn das aktuell wiedergegebene Element entfernt wird).

Metadaten

Die von player.getCurrentMediaMetadata() zurückgegebenen Metadaten können aus vielen Gründen geändert werden, z. B. durch Playlist-Übergänge, In-Stream-Metadatenaktualisierungen oder durch Aktualisieren der aktuellen MediaItem während der Wiedergabe.

Wenn Sie an Änderungen von Metadaten interessiert sind, z. B. um eine Benutzeroberfläche zu aktualisieren, auf der der aktuelle Titel angezeigt wird, können Sie auf onMediaMetadataChanged warten.

Springen zu Videopositionen aktiviert

Das Aufrufen von Player.seekTo-Methoden führt zu einer Reihe von Rückrufen 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 dem Seek.
  2. onPlaybackStateChanged mit allen sofortigen Statusänderungen im Zusammenhang mit dem Suchvorgang. Möglicherweise gibt es keine solche Änderung.

Einzelne Callbacks im Vergleich zu onEvents

Hörer können zwischen der Implementierung einzelner Callbacks wie onIsPlayingChanged(boolean isPlaying) und dem generischen Callback onEvents(Player player, Events events) wählen. Der generische Callback bietet Zugriff auf das Player-Objekt und gibt die Menge der events an, die zusammen aufgetreten sind. 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 Zuhörer interessiert sich für die Gründe für die Änderungen. Beispiel: Die Gründe für onPlayWhenReadyChanged oder onMediaItemTransition.
  • Der Listener reagiert nur auf die neuen Werte, die über Callback-Parameter bereitgestellt werden, oder löst etwas anderes aus, das nicht von den Callback-Parametern abhängt.
  • Bei der Listener-Implementierung wird eine klare, lesbare Angabe dessen bevorzugt, was das Ereignis ausgelöst hat.
  • Der Listener meldet sich bei einem 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 soll dieselbe Logik für mehrere Ereignisse auslösen. Beispiel: Aktualisieren einer Benutzeroberfläche für onPlaybackStateChanged und onPlayWhenReadyChanged.
  • Der Listener muss auf das Player-Objekt zugreifen, um weitere Ereignisse auszulösen, z. B. das Suchen nach einem Übergang zwischen Media-Elementen.
  • Der Listener möchte mehrere Statuswerte verwenden, die über separate Callbacks gemeldet werden, oder in Kombination mit Player-Getter-Methoden. Die Verwendung von Player.getCurrentWindowIndex() mit dem in onTimelineChanged bereitgestellten Timeline ist beispielsweise nur innerhalb des onEvents-Callbacks sicher.
  • Der Listener ist daran interessiert, ob Ereignisse logisch zusammen aufgetreten sind. Beispiel: onPlaybackStateChanged wird aufgrund eines Übergangs des Media-Elements zu STATE_BUFFERING.

In einigen Fällen müssen Zuhörer die einzelnen Callbacks mit dem generischen onEvents-Callback kombinieren, um beispielsweise Gründe für Änderungen an Medienelementen mit onMediaItemTransition aufzuzeichnen, aber erst zu reagieren, wenn alle Statusänderungen zusammen in onEvents verwendet werden können.

AnalyticsListener verwenden

Wenn Sie ExoPlayer verwenden, kann ein AnalyticsListener beim Player registriert werden, indem Sie addAnalyticsListener aufrufen. AnalyticsListener-Implementierungen können auf detaillierte Ereignisse warten, die für Analyse- und Protokollierungszwecke nützlich sein können. Weitere Informationen finden Sie auf der Analyseseite.

EventLogger verwenden

EventLogger ist ein AnalyticsListener, das direkt von der Bibliothek für Protokollierungszwecke bereitgestellt wird. Fügen Sie EventLogger einem ExoPlayer hinzu, um mit einer einzigen Zeile nützliche zusätzliche Protokollierung zu aktivieren:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

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

Ereignisse an bestimmten Wiedergabepositionen auslösen

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 sie 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 können Sie festlegen, ob die Nachricht jedes Mal ausgeführt wird, wenn die angegebene Wiedergabeposition erreicht wird (dies kann aufgrund von Such- und Wiederholungsmodi mehrmals geschehen), oder nur beim ersten Mal. Nachdem die PlayerMessage konfiguriert wurde, 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();