Livestreaming

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

Bei adaptiven Livestreams wird ein Fenster mit verfügbaren Medien angezeigt, das in regelmäßigen Abständen aktualisiert wird, um die aktuelle Echtzeit widerzuspiegeln. Das bedeutet, dass sich die Wiedergabeposition immer irgendwo in diesem Fenster befindet und in den meisten Fällen in den meisten Fällen annähernd der aktuellen Echtzeit entspricht, in der der Stream produziert wird. Die Differenz zwischen der aktuellen Echtzeit und der Wiedergabeposition wird als Live-Offset bezeichnet.

Erkennen und Überwachen von Livewiedergaben

Jedes Mal, wenn ein Livefenster aktualisiert wird, erhalten registrierte Player.Listener-Instanzen ein onTimelineChanged-Ereignis. Details zur aktuellen Livewiedergabe kannst du mithilfe verschiedener Player- und Timeline.Window-Methoden abrufen, die unten aufgeführt und in der folgenden Abbildung dargestellt sind.

Live-Fenster

  • Player.isCurrentWindowLive gibt an, ob es sich bei dem gerade wiedergegebenen Medienelement um einen Livestream handelt. Dieser Wert gilt auch dann, wenn der Livestream beendet ist.
  • Player.isCurrentWindowDynamic gibt an, ob das aktuell wiedergegebene Medienelement noch aktualisiert wird. Das ist in der Regel bei Livestreams der Fall, die noch nicht beendet sind. Hinweis: Dieses Flag gilt in einigen Fällen auch für nicht live übertragene Streams.
  • Player.getCurrentLiveOffset gibt den Versatz zwischen der aktuellen Echtzeit und der Wiedergabeposition (falls verfügbar) zurück.
  • Player.getDuration gibt die Länge des aktuellen Live-Tracking-Zeitraums zurück.
  • Player.getCurrentPosition gibt die Wiedergabeposition relativ zum Beginn des Live-Zeitfensters zurück.
  • Player.getCurrentMediaItem gibt das aktuelle Medienelement zurück. MediaItem.liveConfiguration enthält von der App bereitgestellte Überschreibungen für die Parameter „Ziel-Live-Offset“ und „Anpassung des Live-Offsets“.
  • Player.getCurrentTimeline gibt die aktuelle Medienstruktur in einer Timeline zurück. Der aktuelle Timeline.Window kann mit Player.getCurrentMediaItemIndex und Timeline.getWindow aus dem Timeline abgerufen werden. Im Bereich Window:
    • Window.liveConfiguration enthält die Parameter für den Ziel-Live-Offset und die Anpassung des Live-Offsets. Diese Werte basieren auf Informationen in den Medien und allen von der App bereitgestellten Überschreibungen, die in MediaItem.liveConfiguration festgelegt wurden.
    • Window.windowStartTimeMs ist der Zeitpunkt seit der Unix-Epoche, zu dem das Live-Fenster beginnt.
    • Window.getCurrentUnixTimeMs ist der Zeitpunkt seit der Unix-Epoche der aktuellen Echtzeit. Dieser Wert kann durch eine bekannte Zeitabweichung zwischen dem Server und dem Client korrigiert werden.
    • Window.getDefaultPositionMs ist die Position im Live-Fenster, an der der Player die Wiedergabe standardmäßig startet.

In Livestreams vor- und zurückspringen

Mit Player.seekTo kannst du dir eine beliebige Stelle im Live-Fenster ansehen. Die übergebene Position für die Suche bezieht sich auf den Beginn des Live-Zeitfensters. Mit seekTo(0) kannst du beispielsweise zum Anfang des Live-Streams springen. Der Spieler versucht, den Live-Versatz wie an der Position, zu der nach einer Suche gesucht wird, beizubehalten.

Das Livefenster hat auch eine Standardposition, an der die Wiedergabe beginnen soll. Diese Position befindet sich normalerweise in der Nähe des aktiven Rands. Sie können Player.seekToDefaultPosition aufrufen, um zur Standardposition zu springen.

Benutzeroberfläche für die Wiedergabe von Livestreams

Die standardmäßigen UI-Komponenten von ExoPlayer zeigen die Dauer des Live-Zeitfensters und die aktuelle Wiedergabeposition darin an. Das bedeutet, dass die Position jedes Mal, wenn das Livefenster aktualisiert wird, scheinbar zurückspringt. Wenn Sie ein anderes Verhalten benötigen, z. B. die Unix-Zeit oder den aktuellen Live-Offset anzeigen lassen möchten, können Sie PlayerControlView forken und nach Ihren Wünschen anpassen.

Parameter für die Livewiedergabe konfigurieren

ExoPlayer verwendet einige Parameter, um den Versatz der Wiedergabeposition vom Live-Rand und den Bereich der Wiedergabegeschwindigkeiten zu steuern, mit dem dieser Versatz angepasst werden kann.

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

  • Pro MediaItem-Wert, der an MediaItem.Builder.setLiveConfiguration übergeben wird.
  • Globale Standardwerte, die unter DefaultMediaSourceFactory festgelegt wurden.
  • 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:

  • targetOffsetMs: Der Ziel-Live-Offset. Der Player versucht nach Möglichkeit, während der Wiedergabe an diesen Live-Offset zu kommen.
  • minOffsetMs: Der zulässige Mindest-Live-Offset. Auch wenn der Offset an die aktuellen Netzwerkbedingungen angepasst wird, versucht der Player während der Wiedergabe nicht, unter diesen Offset zu fallen.
  • maxOffsetMs: Der 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 minimale Wiedergabegeschwindigkeit, auf die der Player zurückgreifen kann, wenn er versucht, den Ziel-Live-Offset zu erreichen.
  • maxPlaybackSpeed: Die maximale Wiedergabegeschwindigkeit, mit der der Player den aktuellen Live-Zeitpunkt erreichen kann.

Anpassung der Wiedergabegeschwindigkeit

Bei der Wiedergabe eines Livestreams mit niedriger Latenz passt ExoPlayer den Live-Offset an, indem er die Wiedergabegeschwindigkeit leicht ändert. Der Player versucht, den vom Medium oder der App bereitgestellten Ziel-Live-Offset anzupassen, reagiert aber auch auf sich ändernde Netzwerkbedingungen. Wenn beispielsweise während der Wiedergabe ein erneutes Puffern auftritt, verlangsamt der Player die Wiedergabe etwas, bis der Live-Rand weiter weg ist. Wenn das Netzwerk dann stabil genug ist, um die Wiedergabe wieder näher am Live-Edge zu unterstützen, beschleunigt der Player die Wiedergabe, um wieder auf den Ziel-Live-Offset zuzusteuern.

Wenn die automatische Anpassung der Wiedergabegeschwindigkeit nicht gewünscht ist, kannst du sie deaktivieren, indem du die Properties minPlaybackSpeed und maxPlaybackSpeed auf 1.0f setzt. Ebenso kann sie für Livestreams ohne niedrige Latenz aktiviert werden, indem du für diese Livestreams explizit andere Werte als 1.0f festlegst. Weitere Informationen zum Festlegen dieser Properties finden Sie oben im Abschnitt „Konfiguration“.

Algorithmus zur Anpassung der Wiedergabegeschwindigkeit anpassen

Wenn die Geschwindigkeitsanpassung aktiviert ist, wird mit einer LivePlaybackSpeedControl festgelegt, welche Anpassungen vorgenommen werden. Sie können einen benutzerdefinierten LivePlaybackSpeedControl implementieren oder die Standardimplementierung anpassen, die DefaultLivePlaybackSpeedControl ist. 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 minimale und maximale Wiedergabegeschwindigkeit, die zur Anpassung verwendet werden kann, wenn weder die Medien noch die von der App bereitgestellte MediaItem Grenzen festlegen.
  • proportionalControlFactor: Bestimmt, wie flüssig die Geschwindigkeit angepasst wird. Ein hoher Wert führt zu abrupteren und schnelleren Anpassungen, die aber auch eher hörbar sind. Ein kleinerer Wert führt zu einem flüssigeren Übergang zwischen den Geschwindigkeiten, aber zu einer geringeren Geschwindigkeit.
  • targetLiveOffsetIncrementOnRebufferMs: Dieser Wert wird dem Ziel-Live-Offset hinzugefügt, wenn ein erneutes Puffern auftritt, um vorsichtiger vorzugehen. Diese Funktion kann deaktiviert werden, indem Sie den Wert auf „0“ setzen.
  • minPossibleLiveOffsetSmoothingFactor: Ein exponentieller Glättefaktor, mit dem der minimal mögliche Live-Offset basierend auf den derzeit gepufferten Medien erfasst wird. Ein Wert, der sehr nahe an 1 liegt, bedeutet, dass die Schätzung vorsichtiger ist und es möglicherweise länger dauert, bis sie sich an verbesserte Netzwerkbedingungen anpasst. Ein niedrigerer Wert bedeutet, dass sich die Schätzung schneller anpasst, aber das Risiko von Pufferungen höher ist.

BehindLiveWindowException und ERROR_CODE_BEHIND_LIVE_WINDOW

Die Wiedergabeposition kann hinter dem Live-Window zurückfallen, z. B. wenn der Player pausiert oder für einen längeren Zeitraum 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. Der Anwendungscode kann solche Fehler behandeln, 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
  }
}