Das Herzstück der ExoPlayer-Bibliothek ist die Player
-Schnittstelle. Ein Player
die traditionelle High-Level-Mediaplayer-Funktionen
Medien zwischenspeichern, wiedergeben, pausieren und suchen. Die Standardimplementierung ExoPlayer
ist
sind darauf ausgelegt, nur wenige Annahmen zu treffen (und folglich nur wenige Einschränkungen)
Art der Medien, die wiedergegeben werden, wie und wo sie gespeichert werden und wie sie
gerendert. Anstatt das Laden und Rendern von Medien
direkt zu implementieren,
ExoPlayer
-Implementierungen delegiert diese Arbeit an injizierte Komponenten.
Ein Player wird erstellt oder neue Medienquellen an den Player übergeben.
Die folgenden Komponenten sind in allen ExoPlayer
-Implementierungen gemeinsam:
MediaSource
-Instanzen, die die wiederzugebenden Medien definieren, die Medien laden und aus dem die geladenen Medien gelesen werden können. EineMediaSource
-Instanz wird erstellt aus einemMediaItem
durch einenMediaSource.Factory
im Player. Sie können auch Mit der auf Medienquellen basierenden Playlist API können sie direkt an den Player übergeben werden.- Eine
MediaSource.Factory
-Instanz, die eineMediaItem
in eineMediaSource
konvertiert. DieMediaSource.Factory
wird beim Erstellen des Players eingefügt. Renderer
-Instanzen, die einzelne Komponenten der Medien rendern. Dies sind bei der Erstellung des Players eingefügt wird.- Ein
TrackSelector
, der vonMediaSource
bereitgestellte Tracks auswählt, von jedem verfügbarenRenderer
verbraucht.TrackSelector
wird eingeschleust wenn der Player erstellt wird. - Ein
LoadControl
, das steuert, wann dieMediaSource
mehr Medien zwischenspeichert, und wie viel Medien zwischengespeichert werden.LoadControl
wird eingeschleust, wenn der Player erstellt. - Ein
LivePlaybackSpeedControl
, das die Wiedergabegeschwindigkeit während des Livestreams steuert damit der Player in der Nähe eines konfigurierten Live-Offsets bleibt. ALivePlaybackSpeedControl
wird beim Erstellen des Players eingefügt.
Konzept der Injektion von Komponenten, die Teile des Players implementieren Funktionalität in der gesamten Bibliothek vorhanden ist. Die Standardimplementierungen von Einige Komponenten delegieren Arbeit an weitere eingeschleuste Komponenten. Dadurch können viele werden einzeln durch Implementierungen ersetzt, die benutzerdefiniert konfiguriert werden.
Player-Anpassung
Einige gängige Beispiele für die Anpassung des Players durch das Einfügen von Komponenten sind: beschrieben.
Netzwerkstack konfigurieren
Auf dieser Seite erfahren Sie, wie Sie den von ExoPlayer verwendeten Netzwerkstapel anpassen.
Aus dem Netzwerk geladene Daten im Cache speichern
In den Leitfäden finden Sie temporäres spontanes Caching und Medien herunterladen.
Serverinteraktionen anpassen
Einige Apps möchten möglicherweise HTTP-Anfragen und -Antworten abfangen. Vielleicht möchten Sie benutzerdefinierte Anfrage-Header einfügen, Antwort-Header des Servers lesen, Anforderungen URIs enthalten. So kann sich Ihre App beispielsweise durch Einschleusung ein Token als Header, wenn die Mediensegmente angefordert werden.
Das folgende Beispiel zeigt, wie Sie diese Verhaltensweisen
durch Einfügen einer benutzerdefinierten DataSource.Factory
in DefaultMediaSourceFactory
:
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
Im obigen Code-Snippet enthält das eingefügte HttpDataSource
den Header
"Header: Value"
in jeder HTTP-Anfrage. Dieses Verhalten ist fest für alle
mit einer HTTP-Quelle interagieren.
Für einen detaillierteren Ansatz können Sie Just-in-Time-Verhalten mit einer
ResolvingDataSource
Das folgende Code-Snippet zeigt,
kurz vor der Interaktion mit einer HTTP-Quelle:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
Sie können auch ein ResolvingDataSource
verwenden, um
Just-in-Time-Änderungen am URI, wie im folgenden Snippet gezeigt:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
Fehlerbehandlung anpassen
Durch die Implementierung eines benutzerdefinierten LoadErrorHandlingPolicy
können Apps die
wie ExoPlayer
auf Ladefehler reagiert. Wenn z. B. eine App schnell ausfallen möchte,
oder die Backoff-Logik anpassen,
legt fest, wie lange der Spieler zwischen den Wiederholungen wartet. Das folgende Snippet
zeigt, wie Sie eine benutzerdefinierte Backoff-Logik implementieren:
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
Das Argument LoadErrorInfo
enthält weitere Informationen zum fehlgeschlagenen Laden in
die Logik basierend auf dem Fehlertyp oder der fehlgeschlagenen Anfrage anpassen.
Extrahierer-Flags anpassen
Mit Extrahierer-Flags kann die Extraktion einzelner Formate angepasst werden.
von Progressive Media. Sie können auf dem DefaultExtractorsFactory
festgelegt werden,
DefaultMediaSourceFactory
bereitgestellt. Im folgenden Beispiel wird ein Flag übergeben:
das eine indexbasierte Suche nach MP3-Streams ermöglicht.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
Konstante Bitratensuche aktivieren
Bei MP3-, ADTS- und AMR-Streams kannst du die ungefähre Suche mit einem
eine konstante Bitratenannahme mit FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-Flags.
Diese Flags können für einzelne Extraktoren mithilfe der jeweiligen
DefaultExtractorsFactory.setXyzExtractorFlags
-Methoden wie oben beschrieben. Bis
eine konstante Bitratesuche für alle Extraktoren aktivieren, die diese Funktion unterstützen, verwenden Sie
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
Das ExtractorsFactory
kann dann über DefaultMediaSourceFactory
als
oben zum Anpassen der Extrahierer-Flags beschrieben.
Asynchrone Pufferwarteschlangen aktivieren
Asynchrone Pufferwarteschlangen sind eine Verbesserung des Renderings von ExoPlayer
Pipeline, die MediaCodec
-Instanzen im asynchronen Modus ausführt und
verwendet zusätzliche Threads, um die Decodierung und Darstellung von Daten zu planen. Aktivieren
dass Frames und Audio-Unterläufe
reduziert werden können.
Asynchrone Zwischenspeicherwarteschlangen sind auf Geräten mit Android 12 standardmäßig aktiviert (API-Level 31) und höher und können ab Android 6.0 (API-Level 23) manuell aktiviert werden. Aktivieren Sie die Funktion gegebenenfalls für bestimmte Geräte, auf denen die Probleme aufgetreten sind. Frames oder Audio-Unterläufe ab, insbesondere bei der Wiedergabe von DRM-geschützten oder hohen Frame-Rates Inhalte.
Im einfachsten Fall müssen Sie ein DefaultRenderersFactory
in den
Player folgendermaßen:
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
Wenn Sie Renderer direkt instanziieren, übergeben Sie
AsynchronousMediaCodecAdapter.Factory
zu MediaCodecVideoRenderer
und
MediaCodecAudioRenderer
-Konstruktoren.
Methodenaufrufe mit ForwardingPlayer
abfangen
Sie können einen Teil des Verhaltens einer Player
-Instanz anpassen, indem Sie sie einschließen
eine abgeleitete Klasse von ForwardingPlayer
und Überschreiben von Methoden,
Folgendes:
- Rufen Sie Parameter auf, bevor Sie sie an den Bevollmächtigten
Player
übergeben. - Rufen Sie den Rückgabewert des Delegats
Player
auf, bevor Sie ihn zurückgeben. - Implementieren Sie die Methode vollständig neu.
Beim Überschreiben von ForwardingPlayer
-Methoden muss darauf geachtet werden,
Implementierung bleibt selbstständig und entspricht den Player
insbesondere wenn es um Methoden geht,
identisches oder verwandtes Verhalten. Beispiel:
- Wenn Sie jede Wiedergabe überschreiben möchten, müssen Sie beide Werte überschreiben,
ForwardingPlayer.play
undForwardingPlayer.setPlayWhenReady
, da ein erwartet, dass das Verhalten dieser Methoden identisch ist,playWhenReady = true
- Wenn Sie das Inkrement der Vorwärtsspulen ändern möchten, müssen Sie beide
ForwardingPlayer.seekForward
, um eine Suche mit deinem benutzerdefinierten undForwardingPlayer.getSeekForwardIncrement
, um richtige angepasste Stufe zurück an den Anrufer. - Wenn du steuern möchtest, welche
Player.Commands
von einem Spieler beworben werden müssen Sie sowohlPlayer.getAvailableCommands()
als auchPlayer.isCommandAvailable()
und hören Sie sich auch diePlayer.Listener.onAvailableCommandsChanged()
-Callback, um benachrichtigt zu werden über die vom zugrunde liegenden Player stammen.
MediaSource-Anpassung
In den Beispielen oben werden benutzerdefinierte Komponenten
für die Wiedergabe aller Elemente eingefügt,
MediaItem
-Objekte, die an den Player übergeben werden. Wo ist eine fein abgestufte Anpassung
ist es auch möglich, benutzerdefinierte Komponenten in einzelne
MediaSource
-Instanzen, die direkt an den Spieler übergeben werden können. Das Beispiel
unten sehen Sie, wie Sie ein ProgressiveMediaSource
anpassen, um ein benutzerdefiniertes
DataSource.Factory
, ExtractorsFactory
und LoadErrorHandlingPolicy
:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
Benutzerdefinierte Komponenten erstellen
Die Bibliothek bietet Standardimplementierungen der oben aufgeführten Komponenten.
auf dieser Seite für häufige Anwendungsfälle. Ein ExoPlayer
kann diese Komponenten verwenden,
können auch für die Verwendung benutzerdefinierter Implementierungen erstellt werden, wenn nicht standardmäßige Verhaltensweisen
erforderlich. Einige Anwendungsfälle für benutzerdefinierte Implementierungen:
Renderer
: Sie können eine benutzerdefinierteRenderer
implementieren, um eine Medientyp wird von den Standardimplementierungen des Bibliothek.TrackSelector
: Durch die Implementierung einer benutzerdefiniertenTrackSelector
wird eine App zugelassen. die Art und Weise zu ändern, in der Tracks, die durch einenMediaSource
dargestellt werden, verwendet werden. für die Nutzung durch jeden der verfügbarenRenderer
s ausgewählt.LoadControl
: Durch die Implementierung einer benutzerdefiniertenLoadControl
wird eine App zugelassen. die Pufferrichtlinie des Players zu ändern.Extractor
– wenn ein Containerformat unterstützt werden muss, das derzeit nicht unterstützt wird von der Bibliothek unterstützt wird, können Sie eine benutzerdefinierteExtractor
-Klasse implementieren.MediaSource
– Die Implementierung einer benutzerdefiniertenMediaSource
-Klasse kann ist angemessen, wenn Sie Medienbeispiele abrufen möchten, die Sie an Renderer in einem oder wenn Sie eine benutzerdefinierteMediaSource
-Zusammensetzung implementieren möchten verhalten.MediaSource.Factory
– BenutzerdefinierteMediaSource.Factory
implementieren ermöglicht einer Anwendung, die Art und Weise anzupassen, in der einMediaSource
erstellt wird. von einemMediaItem
.DataSource
: Das Upstream-Paket von ExoPlayer enthält bereits eine Reihe vonDataSource
-Implementierungen für verschiedene Anwendungsfälle. Vielleicht möchten Sie Implementieren Sie Ihre eigeneDataSource
-Klasse, um Daten auf andere Weise zu laden, z. B. über Ein benutzerdefiniertes Protokoll, unter Verwendung eines benutzerdefinierten HTTP-Stacks oder von einem benutzerdefinierten persistenten Cache gespeichert werden.
Beim Erstellen benutzerdefinierter Komponenten empfehlen wir Folgendes:
- Wenn eine benutzerdefinierte Komponente Ereignisse an die App melden muss, empfehlen wir
dass Sie dabei dasselbe Modell wie bei den
bestehenden ExoPlayer-Komponenten verwenden,
Beispiel für die Verwendung von
EventDispatcher
-Klassen oder die gemeinsame Übergabe vonHandler
mit Listener für den Konstruktor der Komponente - Wir empfehlen, dass benutzerdefinierte Komponenten dasselbe Modell wie der vorhandene ExoPlayer verwenden
Komponenten, um eine Neukonfiguration durch die App während der Wiedergabe zu ermöglichen. Gehen Sie dazu wie folgt vor:
Benutzerdefinierte Komponenten sollten
PlayerMessage.Target
implementieren und Konfigurationsänderungen in der MethodehandleMessage
. Der Anwendungscode sollte Konfigurationsänderungen übergeben, indem Sie die MethodecreateMessage
von ExoPlayer aufrufen konfigurieren und mithilfe vonPlayerMessage.send
. Nachrichten werden im Wiedergabethread gesendet stellt sicher, dass sie in der richtigen Reihenfolge mit allen anderen Vorgängen ausgeführt werden, im Player ausgeführt wird.