Änderungen am Status des Players (z. B. Start der Wiedergabe, Pufferung oder Fehler)
lösen Ereignisse aus, die an registrierte Player.Listener Instanzen gesendet werden. Diese
Ereignisse werden durch ganzzahlige Konstanten dargestellt und durch Player.Event
und Player.Eventsdefiniert.
Player.Listener registrieren
Player-Ereignisse werden an registrierte Player.Listener-Instanzen gemeldet. So registrieren Sie einen Listener, der solche Ereignisse empfangen soll:
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);
Wenn Sie Kotlin verwenden, können Sie auch die suspendierenden Erweiterungsfunktionen des Moduls media3-common-ktx verwenden, um Ereignisse mit Coroutinen zu beobachten.
In diesem Fall müssen Sie keinen Player.Listener explizit registrieren oder die Registrierung aufheben.
Wiedergabeereignisse mit Player.Listener beobachten
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 in der Javadoc. Einige der wichtigsten Methoden werden unten ausführlicher beschrieben.
Listener können entweder einzelne Ereignis-Callbacks oder einen generischen onEvents-Callback implementieren, der aufgerufen wird, nachdem ein oder mehrere Ereignisse gleichzeitig aufgetreten sind. Siehe Individual callbacks vs onEvents für eine Erläuterung, welche
für verschiedene Anwendungsfälle bevorzugt werden sollte.
Änderungen des Wiedergabestatus
Änderungen des Player-Status können empfangen werden, indem onPlaybackStateChanged(@State int state) in einem registrierten Player.Listener implementiert wird.
Der Player kann sich in einem von vier Wiedergabestatus befinden:
Player.STATE_IDLE: Dies ist der Anfangsstatus, der Status, wenn der Player angehalten wird, und wenn die Wiedergabe fehlgeschlagen ist. In diesem Status hält der Player nur begrenzte Ressourcen.Player.STATE_BUFFERING: Der Player kann nicht sofort von seiner aktuellen Position aus wiedergeben. Das liegt meist daran, dass weitere Daten geladen werden müssen.Player.STATE_READY: Der Player kann sofort von seiner aktuellen Position aus wiedergeben.Player.STATE_ENDED: Der Player hat alle Medien wiedergegeben.
Zusätzlich zu diesen Status hat der Player ein playWhenReady-Flag, um die Wiedergabeabsicht des Nutzers anzugeben. Änderungen an diesem Flag können empfangen werden, indem onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) implementiert wird.
Ein Player gibt wieder (d. h., seine Position wird erhöht und Medien werden dem Nutzer präsentiert), wenn alle drei folgenden Bedingungen erfüllt sind:
- Der Player befindet sich im Status
Player.STATE_READY. playWhenReadyisttrue.- Die Wiedergabe wird nicht aus einem Grund unterdrückt, der von
Player.getPlaybackSuppressionReasonzurückgegeben wird.
Anstatt diese Eigenschaften einzeln zu prüfen, kann Player.isPlaying aufgerufen werden. Änderungen an diesem Status können empfangen werden, indem onIsPlayingChanged(boolean isPlaying) implementiert wird:
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 unmittelbar vor dem Übergang des Wiedergabestatus zu Player.STATE_IDLE aufgerufen. Fehlgeschlagene oder angehaltene Wiedergaben können mit ExoPlayer.prepare wiederholt werden.
Einige Player-Implementierungen übergeben Instanzen von Unterklassen von
PlaybackException, um zusätzliche Informationen zum Fehler zu liefern. For
example, ExoPlayer übergibt ExoPlaybackException mit type,
rendererIndex und anderen ExoPlayer-spezifischen Feldern.
Das folgende Beispiel zeigt, wie Sie erkennen können, 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
onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int
reason) wird für registrierte Player.Listener Objekte aufgerufen. Der Grund gibt an, ob es sich um einen automatischen Übergang, eine Suche (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: Playlist-Übergänge, In‑Stream-Metadaten-Updates oder Aktualisieren des aktuellen MediaItem während der Wiedergabe.
Wenn Sie an Metadatenänderungen interessiert sind, z. B. um eine Benutzeroberfläche zu aktualisieren, auf der der aktuelle Titel angezeigt wird, können Sie onMediaMetadataChanged beobachten.
Springen zu Videopositionen aktiviert
Das Aufrufen von Player.seekTo-Methoden führt zu einer Reihe von Callbacks für registrierte Player.Listener-Instanzen:
onPositionDiscontinuitymitreason=DISCONTINUITY_REASON_SEEK. Dies ist das direkte Ergebnis des Aufrufs vonPlayer.seekTo. Der Callback hatPositionInfo-Felder für die Position vor und nach der Suche.onPlaybackStateChangedmit allen sofortigen Statusänderungen im Zusammenhang mit der Suche. Eine solche Änderung ist nicht immer vorhanden.
Einzelne Callbacks im Vergleich zu onEvents
Listener können zwischen der Implementierung einzelner Callbacks wie
onIsPlayingChanged(boolean isPlaying), und dem generischen onEvents(Player
player, Events events) Callback wählen. Der generische Callback bietet Zugriff auf das Player-Objekt und gibt die Menge der events an, die gleichzeitig 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 Listener ist an den Gründen für Änderungen interessiert. Beispiele sind die Gründe für
onPlayWhenReadyChangedoderonMediaItemTransition. - 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.
- Die Listener-Implementierung bevorzugt eine klare, lesbare Angabe dessen, was das Ereignis ausgelöst hat, im Methodennamen.
- Der Listener meldet sich bei einem Analysesystem, das über alle einzelnen Ereignisse und Statusänderungen informiert werden muss.
Der generische onEvents(Player player, Events events) sollte in den folgenden Fällen bevorzugt werden:
- Der Listener möchte für mehrere Ereignisse dieselbe Logik auslösen. Beispiel: Aktualisieren einer Benutzeroberfläche für
onPlaybackStateChangedundonPlayWhenReadyChanged. - Der Listener benötigt Zugriff auf das
Player-Objekt, um weitere Ereignisse auszulösen, z. B. die Suche nach einem Übergang des Medienelements. - Der Listener beabsichtigt, mehrere Statuswerte zu verwenden, die über separate Callbacks gemeldet werden, oder in Kombination mit
Player-Getter-Methoden. Beispiel: Verwendung vonPlayer.getCurrentWindowIndex()mit dem inonTimelineChangedbereitgestelltenTimelineist nur innerhalb desonEventsCallbacks sicher. - Der Listener ist daran interessiert, ob Ereignisse logisch zusammen aufgetreten sind.
Beispiel:
onPlaybackStateChangedzuSTATE_BUFFERINGaufgrund eines Übergangs des Medienelements.
In einigen Fällen müssen Listener die einzelnen Callbacks mit dem generischen onEvents-Callback kombinieren, z. B. um Gründe für Änderungen an Medienelementen mit onMediaItemTransition aufzuzeichnen, aber erst zu reagieren, wenn alle Statusänderungen in onEvents zusammen verwendet werden können.
Wiedergabeereignisse mit Coroutinen beobachten
Alternativ können Sie mit Player.listenTo eine Kotlin-Coroutine starten und
das relevante Player.Event angeben:
Beachten Sie, dass Player.listen und Player.listenTo von jedem
Thread aus aufgerufen werden können, während das Callback-Lambda immer im Thread aufgerufen wird, der mit Player.getApplicationLooper verknüpft ist. Daher können Sie auch dann sicher auf Player-Methoden und Statuseigenschaften innerhalb des Callback-Lambdas zugreifen, wenn die Coroutine in einem anderen Thread gestartet wurde.
Änderungen des Wiedergabestatus
coroutineScope.launch { player.listenTo(Player.EVENT_IS_PLAYING_CHANGED) { // `Player` is a receiver scope for this trailing lambda if (isPlaying) { // Active playback. } else { // Not playing. } } }
Wiedergabefehler
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYER_ERROR) { val error = playerError ?: return@listenTo val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. if (cause is InvalidResponseCodeException) { // Retrieve the response code, message and headers } else { // Try calling cause.cause to retrieve the underlying cause } } } }
Einzelne Callbacks im Vergleich zu onEvents
Wenn Sie Player-Ereignisse in einer Coroutine beobachten, stellen Sie immer
die Implementierung für den onEvents-Callback bereit, im Gegensatz zu einem
einzelnen Callback. Sie können zwischen Player.listen und Player.listenTo wählen, je nachdem, welche Ereignisse
einen Aufruf Ihres Lambdas auslösen sollen. Die Funktionen sind ansonsten gleichwertig:
anhören
coroutineScope.launch { player.listen { events -> // `Player` is a receiver scope for this trailing lambda if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) { // Access the player state directly from the receiver updateUi(playbackState) } if (events.contains(Player.EVENT_PLAYER_ERROR)) { // Access the error directly from the player handleError(playerError) } } }
anhören
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events -> // `Player` is a receiver scope for this trailing lambda if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) { // Access the player state directly from the receiver updateUi(playbackState) } if (events.contains(Player.EVENT_PLAYER_ERROR)) { // Access the error directly from the player handleError(playerError) } } }
Wenn Sie an mehreren Ereignistypen interessiert sind, können Sie eine Liste von Ereignissen an
Player.listenTo übergeben. Ihr Lambda wird immer aufgerufen, wenn eines dieser Ereignisse eintritt. Sie können den Parameter Events prüfen, um zu sehen, welche Ereignisse tatsächlich ausgelöst wurden:
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events -> // Unclear which event got triggered without querying `events` parameter // The following function will fire whenever either one is caught updateUiAndHandleError(playbackState, playerError) } }
Da diese Funktionen mit onEvents arbeiten, bieten sie keinen Zugriff auf die transienten Argumente, die an einzelne Callbacks übergeben werden, z. B. den Grund in onMediaItemTransition(..., int reason) oder die oldPosition in onPositionDiscontinuity(...). Wenn Ihre Logik von diesen spezifischen Argumenten abhängt
(und sie nicht als Statuseigenschaften für den Player verfügbar sind), sollten Sie
stattdessen die Standardschnittstelle Player.Listener verwenden.
AnalyticsListener verwenden
Wenn Sie ExoPlayer verwenden, kann ein AnalyticsListener mit dem Player
registriert werden, indem Sie addAnalyticsListener aufrufen. AnalyticsListener -Implementierungen können detaillierte Ereignisse beobachten, die für Analyse- und Loggingzwecke nützlich sein können. Weitere Informationen finden Sie auf der Analyseseite.
EventLogger verwenden
EventLogger ist ein AnalyticsListener, der direkt von der Bibliothek für Loggingzwecke bereitgestellt wird. Fügen Sie EventLogger einem ExoPlayer hinzu, um mit einer einzigen Zeile nützliches zusätzliches Logging 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. Eine 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 werden soll, wenn die angegebene Wiedergabeposition erreicht wird (dies kann aufgrund von Such- und Wiederholungsmodi mehrmals vorkommen), 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();