Netzwerkstacks

ExoPlayer wird häufig zum Streamen von Medien über das Internet verwendet. Sie unterstützt mehrere Netzwerkstacks für die Erstellung der zugrunde liegenden Netzwerkanfragen. Die Wahl des Netzwerkstacks kann einen erheblichen Einfluss auf die Streamingleistung haben.

Auf dieser Seite wird beschrieben, wie Sie ExoPlayer für die Verwendung Ihres bevorzugten Netzwerk-Stacks konfigurieren, die verfügbaren Optionen auflisten, einen Netzwerk-Stack für Ihre Anwendung auswählen und das Caching für gestreamte Medien aktivieren.

ExoPlayer für die Verwendung eines bestimmten Netzwerkstacks konfigurieren

ExoPlayer lädt Daten über DataSource-Komponenten, die aus aus Anwendungscode eingeschleusten DataSource.Factory-Instanzen abgerufen werden.

Wenn Ihre Anwendung nur HTTP(s)-Inhalte wiedergeben muss, ist die Auswahl eines Netzwerkstacks so einfach wie die Aktualisierung aller DataSource.Factory-Instanzen, die Ihre App als Instanzen der HttpDataSource.Factory einfügt, die dem gewünschten Netzwerkstack entsprechen. Wenn Ihre App außerdem Inhalte wiedergeben muss, die nicht unter http(s) enthalten sind, z. B. lokale Dateien, verwenden Sie DefaultDataSource.Factory:

Kotlin

DefaultDataSource.Factory(
  ...
  /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(...))

Java

new DefaultDataSource.Factory(
    ...
    /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...));

In diesem Beispiel ist PreferredHttpDataSource.Factory die Factory, die Ihrem bevorzugten Netzwerk-Stack entspricht. Die DefaultDataSource.Factory-Ebene unterstützt auch Nicht-HTTP-Quellen wie lokale Dateien.

Das folgende Beispiel zeigt, wie ein ExoPlayer erstellt wird, der den Cronet-Netzwerkstack verwendet und auch die Wiedergabe von Nicht-HTTP(s)-Inhalten unterstützt.

Kotlin

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
val cronetDataSourceFactory = CronetDataSource.Factory(cronetEngine, executor)

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
val dataSourceFactory =
  DefaultDataSource.Factory(context, /* baseDataSourceFactory= */ cronetDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
CronetDataSource.Factory cronetDataSourceFactory =
    new CronetDataSource.Factory(cronetEngine, executor);

// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (such as files, resources,
// etc).
DefaultDataSource.Factory dataSourceFactory =
    new DefaultDataSource.Factory(
        context, /* baseDataSourceFactory= */ cronetDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

Unterstützte Netzwerkstacks

ExoPlayer bietet direkte Unterstützung für Cronet, OkHttp und den integrierten Netzwerk-Stack von Android. ExoPlayer kann auch zur Unterstützung anderer Netzwerk-Stacks erweitert werden, die unter Android funktionieren.

Cronet

Cronet ist der Chromium-Netzwerk-Stack, der Android-Apps als Bibliothek zur Verfügung gestellt wird. Cronet nutzt mehrere Technologien, die die Latenz reduzieren und den Durchsatz der Netzwerkanfragen erhöhen, die Ihre Anwendung ausführen muss, einschließlich der von ExoPlayer. Es unterstützt nativ die Protokolle HTTP, HTTP/2 und HTTP/3 over QUIC. Cronet wird von einigen der weltweit größten Streaming-Apps wie YouTube verwendet.

ExoPlayer unterstützt Cronet über die Cronet-Bibliothek. Eine ausführliche Anleitung zur Verwendung findest du in der README.md der Bibliothek. Die Cronet-Bibliothek kann drei zugrunde liegende Cronet-Implementierungen verwenden:

  1. Google Play-Dienste:Wir empfehlen, in den meisten Fällen diese Implementierung zu verwenden und auf den in Android integrierten Netzwerkstack (DefaultHttpDataSource) zurückzugreifen, wenn Google Play-Dienste nicht verfügbar sind.
  2. Cronet eingebettet:Kann eine gute Wahl sein, wenn ein großer Prozentsatz Ihrer Nutzer in Märkten lebt, in denen Google Play-Dienste nicht allgemein verfügbar sind, oder wenn Sie die genaue Version der verwendeten Cronet-Implementierung steuern möchten. Der größte Nachteil von Cronet Embed besteht darin, dass Ihre App um ca. 8 MB erweitert wird.
  3. Cronet-Fallback: Bei der Fallback-Implementierung von Cronet wird die Cronet-API als Wrapper um den integrierten Netzwerk-Stack von Android implementiert. Es sollte nicht mit ExoPlayer verwendet werden, da es effizienter ist, den integrierten Netzwerkstack von Android direkt (über DefaultHttpDataSource) zu verwenden.

OkHttp

OkHttp ist ein weiterer moderner Netzwerk-Stack, der von vielen beliebten Android-Apps weit verbreitet ist. Sie unterstützt HTTP und HTTP/2, aber noch nicht HTTP/3 über QUIC.

ExoPlayer unterstützt OkHttp über die OkHttp-Bibliothek. Eine ausführliche Anleitung zur Verwendung findest du in der README.md der Bibliothek. Bei Verwendung der OkHttp-Bibliothek ist der Netzwerkstack in die Anwendung eingebettet. Dies ähnelt der Methode „Cronet Embed“, aber OkHttp ist deutlich kleiner und kostet Ihre Anwendung weniger als 1 MB.

Der integrierte Android-Netzwerk-Stack

ExoPlayer unterstützt die Verwendung des integrierten Android-Netzwerkstacks mit DefaultHttpDataSource und DefaultHttpDataSource.Factory, die Teil der ExoPlayer-Kernbibliothek sind.

Die genaue Implementierung des Netzwerkstacks hängt von der Software ab, die auf dem zugrunde liegenden Gerät ausgeführt wird. Auf den meisten Geräten (seit 2021) wird nur HTTP unterstützt. Das heißt, HTTP/2 und HTTP/3 über QUIC werden nicht unterstützt.

Andere Netzwerkstacks

In Apps können auch andere Netzwerkstacks mit ExoPlayer integriert werden. Implementieren Sie dazu einen HttpDataSource, der den Netzwerkstack zusammen mit einer entsprechenden HttpDataSource.Factory umschließt. Die ExoPlayer-Bibliotheken Cronet und OkHttp sind gute Beispiele dafür.

Bei der Einbindung in einen reinen Java-Netzwerkstack empfiehlt es sich, einen DataSourceContractTest zu implementieren, um zu prüfen, ob die HttpDataSource-Implementierung korrekt funktioniert. OkHttpDataSourceContractTest in der OkHttp-Bibliothek ist ein gutes Beispiel dafür.

Netzwerkstack auswählen

In der folgenden Tabelle sind die Vor- und Nachteile der von ExoPlayer unterstützten Netzwerkstacks aufgeführt.

Netzwerkstack Protokolle Auswirkung der APK-Größe Hinweise
Cronet (Google Play-Dienste) HTTP
HTTP/2
HTTP/3 over QUIC
Klein
(<100 KB)
Google Play-Dienste erforderlich. Cronet-Version wird automatisch aktualisiert
Cronet (eingebettet) HTTP
HTTP/2
HTTP/3 over QUIC
Groß
(~8 MB)
Cronet-Version wird vom App-Entwickler gesteuert
Cronet (Fallback) HTTP
(je nach Gerät)
Klein
(<100 KB)
Nicht empfohlen für ExoPlayer
OkHttp HTTP
HTTP/2
Klein
(<1 MB)
Kotlin-Laufzeit erforderlich
Integrierter Netzwerkstack HTTP
(je nach Gerät)
Keine Die Implementierung variiert je nach Gerät

Mit den Protokollen HTTP/2 und HTTP/3 über QUIC kann die Leistung des Medienstreamings erheblich verbessert werden. Insbesondere beim Streaming adaptiver Medien, die über ein Content Distribution Network (CDN) verteilt werden, gibt es Fälle, in denen die Verwendung dieser Protokolle CDNs wesentlich effizienter machen kann. Aus diesem Grund ist die Unterstützung von Cronet für HTTP/2 und HTTP/3 über QUIC (und die Unterstützung von OkHttp für HTTP/2) gegenüber der Verwendung des integrierten Netzwerkstacks von Android ein großer Vorteil, vorausgesetzt, die Server, auf denen die Inhalte gehostet werden, diese Protokolle ebenfalls unterstützen.

Wenn Sie Media-Streaming isoliert in Betracht ziehen, empfehlen wir die Verwendung von Cronet, das von den Google Play-Diensten bereitgestellt wird, und auf DefaultHttpDataSource zurückzugreifen, wenn die Google Play-Dienste nicht verfügbar sind. Mit dieser Empfehlung lässt sich ein ausgewogenes Verhältnis zwischen der Aktivierung von HTTP/2 und HTTP/3 gegenüber QUIC auf den meisten Geräten und einer deutlichen Erhöhung der APK-Größe erzielen. Es gibt Ausnahmen von dieser Empfehlung. Für Fälle, in denen Google Play-Dienste wahrscheinlich auf einem erheblichen Teil der Geräte nicht verfügbar sind, auf denen Ihre App ausgeführt wird, ist die Verwendung von Cronet Integrate oder OkHttp möglicherweise besser geeignet. Die Verwendung des integrierten Netzwerkstacks ist möglicherweise akzeptabel, wenn die APK-Größe ein kritisches Problem ist oder wenn Medienstreaming nur einen geringen Teil der Funktionalität Ihrer App ausmacht.

Neben Medien ist es normalerweise sinnvoll, einen einzelnen Netzwerkstack für das gesamte von Ihrer Anwendung ausgeführte Netzwerk auszuwählen. Dadurch können Ressourcen (z. B. Sockets) effizient zusammengefasst und von ExoPlayer und anderen Anwendungskomponenten gemeinsam genutzt werden.

Da Ihre Anwendung höchstwahrscheinlich Netzwerke ausführen muss, die nichts mit der Medienwiedergabe zu tun haben, sollte Ihre Wahl des Netzwerkstacks letztendlich unsere oben genannten Empfehlungen für das Streaming von Medien, die Anforderungen aller anderen Netzwerkkomponenten und deren relative Bedeutung für Ihre Anwendung berücksichtigen.

Medien im Cache speichern

ExoPlayer unterstützt das Caching geladener Byte auf dem Laufwerk, um zu verhindern, dass dieselben Byte wiederholt aus dem Netzwerk geladen werden. Dies ist nützlich, wenn Sie im aktuellen Medium zurückgehen oder dasselbe Element wiederholen möchten.

Caching erfordert eine SimpleCache-Instanz, die auf ein dediziertes Cache-Verzeichnis verweist, und eine CacheDataSource.Factory:

Kotlin

// Note: This should be a singleton in your app.
val databaseProvider = StandaloneDatabaseProvider(context)

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
val cache =
    SimpleCache(
        downloadDirectory, LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider)

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
val cacheDataSourceFactory =
    CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory)

// Inject the DefaultDataSource.Factory when creating the player.
val player =
    ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build()

Java

// Note: This should be a singleton in your app.
DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context);

// An on-the-fly cache should evict media when reaching a maximum disk space limit.
Cache cache =
    new SimpleCache(
        downloadDirectory, new LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider);

// Configure the DataSource.Factory with the cache and factory for the desired HTTP stack.
DataSource.Factory cacheDataSourceFactory =
    new CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory);

// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory))
        .build();