Livestreaming

ExoPlayer spielt die meisten adaptiven Livestreams ohne spezielle Konfiguration ab. Weitere Informationen finden Sie auf der Seite Unterstützte Formate.

Adaptive Livestreams bieten ein Fenster mit verfügbaren Media, 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 in der Nähe der aktuellen Echtzeit, in der der Stream produziert wird. Die Differenz zwischen der aktuellen Echtzeit und der Wiedergabeposition wird als Live-Offset bezeichnet.

Livewiedergaben erkennen und überwachen

Jedes Mal, wenn ein Live-Fenster aktualisiert wird, erhalten registrierte Player.Listener-Instanzen ein onTimelineChanged-Ereignis. Sie können Details zur aktuellen Livewiedergabe abrufen, indem Sie verschiedene Player- und Timeline.Window-Methoden abfragen, wie unten aufgeführt und in der folgenden Abbildung dargestellt.

Live-Fenster

  • Player.isCurrentWindowLive gibt an, ob es sich bei dem aktuell wiedergegebenen Media-Element um einen Livestream handelt. Dieser Wert ist auch dann noch „true“, wenn der Livestream beendet wurde.
  • Player.isCurrentWindowDynamic gibt an, ob das aktuell wiedergegebene Media-Element noch aktualisiert wird. Das ist in der Regel bei Livestreams der Fall, die noch nicht beendet wurden. Dieses Flag ist in einigen Fällen auch für Nicht-Livestreams „true“.
  • Player.getCurrentLiveOffset gibt den Offset zwischen der aktuellen Echtzeit und der Wiedergabeposition (falls verfügbar) zurück.
  • Player.getDuration gibt die Länge des aktuellen Live-Fensters zurück.
  • Player.getCurrentPosition gibt die Wiedergabeposition relativ zum Beginn des Live-Fensters zurück.
  • Player.getCurrentMediaItem gibt das aktuelle Medienelement zurück, wobei MediaItem.liveConfiguration von der App bereitgestellte Überschreibungen für die Parameter für das Ziel-Live-Offset und die Live-Offset-Anpassung enthält.
  • Player.getCurrentTimeline gibt die aktuelle Media-Struktur in einem Timeline zurück. Der aktuelle Timeline.Window kann mit Player.getCurrentMediaItemIndex und Timeline.getWindow aus dem Timeline abgerufen werden. Im Window:
    • Window.liveConfiguration enthält die Parameter für das Ziel-Live-Offset und die Anpassung des Live-Offsets. Diese Werte basieren auf Informationen in den Media und allen von der App bereitgestellten Überschreibungen, die in MediaItem.liveConfiguration festgelegt sind.
    • Window.windowStartTimeMs ist die Zeit seit der Unix-Epoche, zu der das Live-Fenster beginnt.
    • Window.getCurrentUnixTimeMs ist 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.getDefaultPositionMs ist die Position im Live-Fenster, an der die Wiedergabe im Player standardmäßig beginnt.

In Livestreams vorspulen

Mit Player.seekTo kannst du zu einer beliebigen Stelle im Live-Fenster springen. Die übergebene Suchposition ist relativ zum Beginn des Live-Fensters. Beispiel: seekTo(0) springt zum Anfang des Live-Fensters. Der Player versucht, nach einem Seek-Vorgang denselben Live-Offset wie die Position beizubehalten, zu der gesucht wurde.

Das Live-Fenster hat auch eine Standardposition, an der die Wiedergabe beginnen soll. Diese Position befindet sich normalerweise in der Nähe des Live-Rands. Mit Player.seekToDefaultPosition können Sie zur Standardposition springen.

Benutzeroberfläche für die Livewiedergabe

In den Standard-UI-Komponenten von ExoPlayer werden die Dauer des Live-Fensters und die aktuelle Wiedergabeposition darin angezeigt. Das bedeutet, dass die Position bei jeder Aktualisierung des Live-Fensters zurückspringt. Wenn Sie ein anderes Verhalten benötigen, z. B. die Unix-Zeit oder den aktuellen Live-Offset, können Sie PlayerControlView forken und an Ihre Anforderungen anpassen.

Parameter für die Live-Wiedergabe konfigurieren

ExoPlayer verwendet einige Parameter, um den Offset der Wiedergabeposition vom Live-Edge und den Bereich der Wiedergabegeschwindigkeiten zu steuern, die zum Anpassen dieses Offsets verwendet werden können.

ExoPlayer ruft Werte für diese Parameter an drei Stellen ab, in absteigender Prioritätsreihenfolge (der erste gefundene Wert wird verwendet):

  • Pro MediaItem-Werten, die an MediaItem.Builder.setLiveConfiguration übergeben werden.
  • Globale Standardwerte für DefaultMediaSourceFactory festgelegt.
  • Werte, die direkt aus den Media 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:

  • targetOffsetMs: Der Ziel-Live-Offset. Der Player versucht, diesen Live-Offset während der Wiedergabe nach Möglichkeit zu erreichen.
  • minOffsetMs: Der minimal zulässige Live-Offset. Auch wenn der Offset an die aktuellen Netzwerkbedingungen angepasst wird, versucht der Player während der Wiedergabe nicht, diesen Offset zu unterschreiten.
  • maxOffsetMs: Das maximal zulässige Live-Offset. Auch wenn der Offset an die aktuellen Netzwerkbedingungen angepasst wird, versucht der Player während der Wiedergabe nicht, diesen Offset zu überschreiten.
  • minPlaybackSpeed: Die Mindestabspielgeschwindigkeit, die der Player verwenden kann, 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 vom Medium oder der App bereitgestellten Live-Offset abzugleichen, reagiert aber auch auf sich ändernde Netzwerkbedingungen. Wenn beispielsweise während der Wiedergabe Pufferungen auftreten, verlangsamt der Player die Wiedergabe leicht, 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, wird die Wiedergabe im Player beschleunigt, um wieder in Richtung des Ziel-Live-Offsets zu gelangen.

Wenn die automatische Anpassung der Wiedergabegeschwindigkeit nicht gewünscht ist, kann sie deaktiviert werden, indem die Eigenschaften minPlaybackSpeed und maxPlaybackSpeed auf 1.0f gesetzt werden. Ebenso kann es für Live-Streams ohne geringe Latenz aktiviert werden, indem diese explizit auf andere Werte als 1.0f festgelegt werden. Weitere Informationen zum Festlegen dieser Attribute finden Sie oben im Konfigurationsabschnitt.

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:

  • fallbackMinPlaybackSpeed und fallbackMaxPlaybackSpeed: Die minimalen und maximalen Wiedergabegeschwindigkeiten, die für die Anpassung verwendet werden können, wenn weder die Medien noch die von der App bereitgestellte MediaItem Grenzwerte definieren.
  • proportionalControlFactor: Steuert, wie gleichmäßig die Geschwindigkeitsanpassung erfolgt. Bei einem hohen Wert sind die Anpassungen plötzlicher und reaktiver, aber auch wahrscheinlicher hörbar. Ein kleinerer Wert führt zu einem sanfteren Übergang zwischen den Geschwindigkeiten, allerdings auf Kosten der Geschwindigkeit.
  • targetLiveOffsetIncrementOnRebufferMs: Dieser Wert wird dem Live-Ziel-Offset immer dann hinzugefügt, wenn ein Rebuffer auftritt, um vorsichtiger vorzugehen. Diese Funktion kann deaktiviert werden, indem Sie den Wert auf 0 setzen.
  • minPossibleLiveOffsetSmoothingFactor: Ein Faktor für die exponentielle Glättung, der verwendet wird, um den minimal möglichen Live-Offset basierend auf den aktuell gepufferten Media zu verfolgen. Ein Wert, der sehr nahe an 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 Rebuffering ist höher.

BehindLiveWindowException und ERROR_CODE_BEHIND_LIVE_WINDOW

Die Wiedergabeposition kann hinter dem Live-Fenster zurückbleiben, z. B. wenn der Player lange genug pausiert oder gepuffert wird. In diesem Fall schlägt die Wiedergabe fehl und über Player.Listener.onPlayerError wird eine Ausnahme mit dem Fehlercode ERROR_CODE_BEHIND_LIVE_WINDOW gemeldet. Im Anwendungscode können solche Fehler behoben werden, indem die Wiedergabe an der Standardposition fortgesetzt wird. 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
  }
}