ExoPlayer 通常用于通过互联网流式传输媒体。它支持多个网络堆栈来执行其底层的网络请求。您选择的网络堆栈会对流式传输性能产生重大影响。
本页概述了如何配置 ExoPlayer 以使用您选择的网络堆栈,列出了可用的选项,提供了一些关于如何为您的应用选择网络堆栈的指导,并说明了如何为流式传输媒体启用缓存。
配置 ExoPlayer 以使用特定网络堆栈
ExoPlayer 通过 DataSource
组件加载数据,这些组件是从应用代码中注入的 DataSource.Factory
实例获得的。
如果您的应用只需要播放 http(s) 内容,选择网络栈就像更新应用注入的任何 DataSource.Factory
实例一样简单,使其成为与您想要使用的网络栈相对应的 HttpDataSource.Factory
实例。如果您的应用还需要播放非 http(s) 内容,例如本地文件,请使用 DefaultDataSource.Factory
Kotlin
DefaultDataSource.Factory( ... /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(...))
Java
new DefaultDataSource.Factory( ... /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...));
在此示例中,PreferredHttpDataSource.Factory
是与您首选网络栈相对应的工厂。 DefaultDataSource.Factory
层添加了对非 http(s) 源(例如本地文件)的支持。
以下示例展示了如何构建一个将使用 Cronet 网络栈并支持播放非 http(s) 内容的 ExoPlayer
。
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();
支持的网络栈
ExoPlayer 直接支持 HttpEngine、Cronet、OkHttp 和 Android 的内置默认网络栈。ExoPlayer 还可以扩展以支持任何其他在 Android 上工作的网络栈。
HttpEngine
HttpEngine 是 API 34(或 S 扩展 7)上 Android 推荐的默认网络栈。在大多数情况下,它在内部使用 Cronet 网络栈,支持通过 QUIC 协议的 HTTP、HTTP/2 和 HTTP/3。
ExoPlayer 通过其 HttpEngineDataSource.Factory
支持 HttpEngine。您可以像在 配置 ExoPlayer 使用特定网络栈 中描述的那样注入此数据源工厂。
Cronet
Cronet 是 Chromium 网络栈,作为库提供给 Android 应用。Cronet 利用多种技术来减少应用所需网络请求的延迟并提高其吞吐量,包括 ExoPlayer 进行的请求。它原生支持通过 QUIC 协议的 HTTP、HTTP/2 和 HTTP/3。Cronet 被世界上一些最大的流媒体应用使用,包括 YouTube。
ExoPlayer 通过其 Cronet 库 支持 Cronet。有关如何使用它的详细说明,请参阅库的 README.md
。请注意,Cronet 库能够使用三种底层 Cronet 实现
- Google Play 服务:我们建议在大多数情况下使用此实现,如果 Google Play 服务不可用,则回退到 Android 的内置网络栈 (
DefaultHttpDataSource
)。 - Cronet 嵌入式:如果您的用户中有很大一部分位于 Google Play 服务不普遍的市场,或者您想控制所使用的 Cronet 实现的具体版本,那么这可能是一个不错的选择。Cronet 嵌入式的主要缺点是它会为您的应用增加约 8MB 的大小。
- Cronet 回退:Cronet 的回退实现将 Cronet 的 API 实现为围绕 Android 的内置网络栈的包装器。不应将它与 ExoPlayer 一起使用,因为直接使用 Android 的内置网络栈(通过使用
DefaultHttpDataSource
)更有效。
OkHttp
OkHttp 是另一个现代网络栈,被许多流行的 Android 应用广泛使用。它支持 HTTP 和 HTTP/2,但尚不支持通过 QUIC 的 HTTP/3。
ExoPlayer 通过其 OkHttp 库 支持 OkHttp。有关如何使用它的详细说明,请参阅库的 README.md
。使用 OkHttp 库时,网络栈将嵌入到应用中。这类似于 Cronet 嵌入式,但 OkHttp 的体积要小得多,为您的应用增加不到 1MB 的大小。
Android 的内置网络栈
ExoPlayer 支持使用 Android 的内置网络栈,并使用 DefaultHttpDataSource
和 DefaultHttpDataSource.Factory
,它们是核心 ExoPlayer 库的一部分。
确切的网络栈实现取决于底层设备上运行的软件。在大多数设备上,只支持 HTTP(也就是说,不支持通过 QUIC 的 HTTP/2 和 HTTP/3)。
其他网络栈
应用还可以将其他网络栈与 ExoPlayer 集成。为此,请实现一个包装网络栈的 HttpDataSource
,以及一个相应的 HttpDataSource.Factory
。ExoPlayer 的 Cronet 和 OkHttp 库是有关如何执行此操作的很好的示例。
与纯 Java 网络栈集成时,最好实现一个 DataSourceContractTest
来检查您的 HttpDataSource
实现是否按预期工作。OkHttp 库中的 OkHttpDataSourceContractTest
是有关如何执行此操作的很好的示例。
选择网络栈
下表概述了 ExoPlayer 支持的网络栈的优缺点。
网络栈 | 协议 | APK 大小影响 | 备注 |
---|---|---|---|
HttpEngine | HTTP HTTP/2 通过 QUIC 的 HTTP/3 |
无 | 仅在 API 34 或 S 扩展 7 上可用 |
Cronet(Google Play 服务) | HTTP HTTP/2 通过 QUIC 的 HTTP/3 |
小 (<100KB) |
需要 Google Play 服务。Cronet 版本自动更新 |
Cronet(嵌入式) | HTTP HTTP/2 通过 QUIC 的 HTTP/3 |
大 (~8MB) |
Cronet 版本由应用开发者控制 |
Cronet(回退) | HTTP (随设备而异) |
小 (<100KB) |
不推荐用于 ExoPlayer |
OkHttp | HTTP HTTP/2 |
小 (<1MB) |
|
内置网络栈 | HTTP (随设备而异) |
无 | 实现随设备而异 |
通过 QUIC 的 HTTP/2 和 HTTP/3 协议可以显著提高媒体流性能。特别是,在使用内容分发网络 (CDN) 分发自适应媒体时,某些情况下可以使用这些协议让 CDN 更有效地运行。出于这个原因,HttpEngine 和 Cronet 对通过 QUIC 的 HTTP/2 和 HTTP/3(以及 OkHttp 对 HTTP/2 的支持)的支持,与使用 Android 的内置网络栈相比,是一个主要优势,前提是托管内容的服务器也支持这些协议。
仅考虑媒体流时,我们建议使用 HttpEngine 或 Google Play 服务提供的 Cronet,如果 Google Play 服务不可用,则回退到 DefaultHttpDataSource
。此建议在大多数设备上启用 HTTP/2 和通过 QUIC 的 HTTP/3 的使用之间取得了良好的平衡,同时避免了 APK 大小显著增加。此建议也有一些例外。对于 Google Play 服务很可能在运行您的应用的大量设备上不可用的情况,使用 Cronet 嵌入式或 OkHttp 可能更合适。如果 APK 大小是一个关键问题,或者媒体流只是您应用功能中的一小部分,则可以使用内置网络栈。
除了媒体之外,通常最好为应用执行的所有网络操作选择一个网络栈。这样可以有效地将资源(例如套接字)池化并共享给 ExoPlayer 和其他应用组件。
由于您的应用很可能需要执行与媒体播放无关的网络操作,因此您对网络栈的选择最终应考虑我们上面针对孤立媒体流的建议、执行网络操作的任何其他组件的要求以及它们对应用的相对重要性。
缓存媒体
ExoPlayer 支持将加载的字节缓存到磁盘以防止重复从网络加载相同的字节。这在当前媒体中倒退或重复同一项时很有用。
缓存需要一个指向专用缓存目录的 SimpleCache
实例和一个 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();