ExoPlayer spielt die meisten adaptiven Livestreams ohne besondere Konfiguration ab. Weitere Informationen finden Sie auf der Seite Unterstützte Formate.
Adaptive Livestreams bieten ein Fenster mit verfügbaren Medien, das in regelmäßigen Abständen aktualisiert wird, um mit der aktuellen Echtzeit Schritt zu halten. Das bedeutet, dass sich die Wiedergabeposition immer irgendwo in diesem Fenster befindet, in den meisten Fällen nahe der aktuellen Echtzeit, in der der Stream produziert wird. Die Differenz zwischen der aktuellen Echtzeit und der Wiedergabeposition wird als Live-Versatz bezeichnet.
Live-Wiedergaben erkennen und beobachten
Jedes Mal, wenn ein Live-Fenster aktualisiert wird, erhalten registrierte Player.Listener-Instanzen ein onTimelineChanged-Ereignis. Sie können Details zur aktuellen Live-Wiedergabe abrufen, indem Sie verschiedene Player- und Timeline.Window-Methoden abfragen, wie weiter unten aufgeführt und in der folgenden Abbildung dargestellt.
Player.isCurrentWindowLivegibt an, ob es sich bei der aktuell wiedergegebenen Mediendatei um einen Livestream handelt. Dieser Wert ist auch dann noch „true“, wenn der Livestream beendet wurde.Player.isCurrentWindowDynamicgibt an, ob die aktuell wiedergegebene Mediendatei noch aktualisiert wird. Das ist in der Regel bei Livestreams der Fall, die noch nicht beendet sind. Dieses Flag ist in einigen Fällen auch für Nicht-Livestreams „true“.Player.getCurrentLiveOffsetgibt den Versatz zwischen der aktuellen Echtzeit und der Wiedergabeposition zurück (falls verfügbar).Player.getDurationgibt die Länge des aktuellen Live-Fensters zurück.Player.getCurrentPositiongibt die Wiedergabeposition relativ zum Beginn des Live-Fensters zurück.Player.getCurrentMediaItemgibt die aktuelle Mediendatei zurück, wobeiMediaItem.liveConfigurationvon der App bereitgestellte Überschreibungen für die Parameter für den Ziel-Live-Offset und die Live-Offset-Anpassung enthält.Player.getCurrentTimelinegibt die aktuelle Medienstruktur in einemTimelinezurück. Der aktuelleTimeline.Windowkann mitPlayer.getCurrentMediaItemIndexundTimeline.getWindowaus demTimelineabgerufen werden. ImWindow:Window.liveConfigurationenthält die Parameter für den Ziel-Live-Offset und die Live-Offset-Anpassung. Diese Werte basieren auf Informationen in den Medien und allen von der App bereitgestellten Überschreibungen, die inMediaItem.liveConfigurationfestgelegt sind.Window.windowStartTimeMsist die Zeit seit der Unix-Epoche, zu der das Live-Fenster beginnt.Window.getCurrentUnixTimeMsist die Zeit seit der Unix-Epoche der aktuellen Echtzeit. Dieser Wert kann durch eine bekannte Zeitdifferenz zwischen dem Server und dem Client korrigiert werden.Window.getDefaultPositionMsist die Position im Live-Fenster, an der der Player standardmäßig mit der Wiedergabe beginnt.
In Livestreams vor- und zurückspulen
Mit Player.seekTo kannst du zu einer beliebigen Stelle im Live-Fenster springen. Die übergebene Suchposition bezieht sich auf den Beginn des Live-Fensters. Beispiel: seekTo(0) springt zum Anfang des Livefensters. Der Player versucht, nach dem Suchvorgang denselben Live-Offset wie die gesuchte Position beizubehalten.
Das Livefenster hat auch eine Standardposition, an der die Wiedergabe beginnen soll. Diese Position befindet sich normalerweise in der Nähe des Live-Rands. Sie können die Standardposition durch Aufrufen von Player.seekToDefaultPosition ansteuern.
Benutzeroberfläche für die Live-Wiedergabe
Die Standard-UI-Komponenten von ExoPlayer zeigen die Dauer des Live-Fensters und die aktuelle Wiedergabeposition darin an. Das bedeutet, dass die Position bei jeder Aktualisierung des Live-Fensters rückwärts zu springen scheint. Wenn Sie ein anderes Verhalten benötigen, z. B. die Anzeige der Unix-Epoche oder des aktuellen Live-Offsets, können Sie PlayerControlView forken und an Ihre Anforderungen anpassen.
Parameter für die Live-Wiedergabe konfigurieren
ExoPlayer verwendet einige Parameter, um den Versatz der Wiedergabeposition vom Live-Edge und den Bereich der Wiedergabegeschwindigkeiten zu steuern, mit denen dieser Versatz angepasst werden kann.
ExoPlayer ruft Werte für diese Parameter von drei Stellen ab, in absteigender Reihenfolge der Priorität (der erste gefundene Wert wird verwendet):
- Pro
MediaItem-Wert, der anMediaItem.Builder.setLiveConfigurationübergeben wird. - Globale Standardwerte wurden am
DefaultMediaSourceFactoryfestgelegt. - Werte, die direkt aus den Medien gelesen werden.
Kotlin
// Global settings. val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build() // Per MediaItem settings. val mediaItem = MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build() ) .build() player.setMediaItem(mediaItem)
Java
// Global settings. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build(); // Per MediaItem settings. MediaItem mediaItem = new MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()) .build(); player.setMediaItem(mediaItem);
Verfügbare Konfigurationswerte sind:
targetOffsetMs: Der Ziel-Live-Offset. Der Player versucht, während der Wiedergabe möglichst nah an diesen Live-Versatz heranzukommen.minOffsetMs: Der minimal zulässige Live-Offset. Selbst wenn der Offset an die aktuellen Netzwerkbedingungen angepasst wird, versucht der Player während der Wiedergabe nicht, einen niedrigeren Wert als diesen Offset zu erreichen.maxOffsetMs: Der maximal zulässige Live-Offset. Selbst wenn der Offset an die aktuellen Netzwerkbedingungen angepasst wird, versucht der Player während der Wiedergabe nicht, diesen Offset zu überschreiten.minPlaybackSpeed: Die minimale Wiedergabegeschwindigkeit, die der Player verwenden kann, um zurückzufallen, wenn er versucht, den Ziel-Live-Offset zu erreichen.maxPlaybackSpeed: Die maximale Wiedergabegeschwindigkeit, die der Player verwenden kann, um den Ziel-Live-Offset zu erreichen.
Anpassung der Wiedergabegeschwindigkeit
Bei der Wiedergabe eines Livestreams mit niedriger Latenz passt ExoPlayer den Live-Offset an, indem die Wiedergabegeschwindigkeit leicht geändert wird. Der Player versucht, den von den Medien oder der App bereitgestellten Ziel-Live-Offset zu erreichen, versucht aber auch, auf sich ändernde Netzwerkbedingungen zu reagieren. Wenn beispielsweise während der Wiedergabe Zwischenspeicherungen auftreten, verlangsamt der Player die Wiedergabe geringfügig, um sich weiter vom Live-Edge zu entfernen. Wenn das Netzwerk dann wieder stabil genug ist, um die Wiedergabe näher am Live-Edge zu unterstützen, beschleunigt der Player die Wiedergabe, um sich wieder dem Ziel-Live-Offset anzunähern.
Wenn die automatische Anpassung der Wiedergabegeschwindigkeit nicht erforderlich ist, kann sie deaktiviert werden, indem die Properties minPlaybackSpeed und maxPlaybackSpeed auf 1.0f gesetzt werden.
Ebenso kann sie für Livestreams ohne niedrige Latenz aktiviert werden, indem diese explizit auf andere Werte als 1.0f gesetzt werden. Weitere Informationen zum Festlegen dieser Attribute finden Sie im Abschnitt „Konfiguration“.
Algorithmus zur Anpassung der Wiedergabegeschwindigkeit anpassen
Wenn die Geschwindigkeitsanpassung aktiviert ist, wird durch eine LivePlaybackSpeedControl definiert, welche Anpassungen vorgenommen werden. Sie können einen benutzerdefinierten LivePlaybackSpeedControl implementieren oder die Standardimplementierung DefaultLivePlaybackSpeedControl anpassen. In beiden Fällen kann beim Erstellen des Players eine Instanz festgelegt werden:
Kotlin
val player = ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build() ) .build()
Java
ExoPlayer player = new ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( new DefaultLivePlaybackSpeedControl.Builder() .setFallbackMaxPlaybackSpeed(1.04f) .build()) .build();
Relevante Anpassungsparameter von DefaultLivePlaybackSpeedControl sind:
fallbackMinPlaybackSpeedundfallbackMaxPlaybackSpeed: Die minimale und maximale Wiedergabegeschwindigkeit, die für die Anpassung verwendet werden kann, wenn weder die Medien noch die von der App bereitgestellteMediaItemLimits definieren.proportionalControlFactor: Steuert, wie flüssig die Geschwindigkeitsanpassung ist. Ein hoher Wert führt zu plötzlichen und reaktiven Anpassungen, die aber auch eher hörbar sind. Ein kleinerer Wert führt zu einem flüssigeren Übergang zwischen den Geschwindigkeiten, allerdings auf Kosten der Geschwindigkeit.targetLiveOffsetIncrementOnRebufferMs: Dieser Wert wird dem Ziel-Live-Offset hinzugefügt, wenn ein Rebuffering auftritt, um vorsichtiger vorzugehen. Diese Funktion kann deaktiviert werden, indem der Wert auf 0 gesetzt wird.minPossibleLiveOffsetSmoothingFactor: Ein exponentieller Glättungsfaktor, der verwendet wird, um den minimal möglichen Live-Offset basierend auf den aktuell gepufferten Medien zu verfolgen. Ein Wert, der sehr nahe bei 1 liegt, bedeutet, dass die Schätzung vorsichtiger ist und es länger dauern kann, bis sie sich an verbesserte Netzwerkbedingungen anpasst. Ein niedrigerer Wert bedeutet, dass sich die Schätzung schneller anpasst, aber das Risiko von Zwischenspeicherungen höher ist.
BehindLiveWindowException und ERROR_CODE_BEHIND_LIVE_WINDOW
Die Wiedergabeposition kann hinter dem Live-Stream zurückbleiben, z. B. wenn der Player pausiert oder lange genug gepuffert wird. In diesem Fall schlägt die Wiedergabe fehl und eine Ausnahme mit dem Fehlercode ERROR_CODE_BEHIND_LIVE_WINDOW wird über Player.Listener.onPlayerError gemeldet. Sie können solche Fehler beheben, indem Sie die Wiedergabe an der Standardposition fortsetzen. Die PlayerActivity der Demo-App veranschaulicht diesen Ansatz.
Kotlin
override fun onPlayerError(error: PlaybackException) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition() player.prepare() } else { // Handle other errors } }
Java
@Override public void onPlayerError(PlaybackException error) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition(); player.prepare(); } else { // Handle other errors } }