ExoPlayer 通常用于通过互联网流式传输媒体。它支持多个网络栈来发出底层网络请求。您选择的网络栈可能会对流式传输性能产生重大影响。
本页概述了如何配置 ExoPlayer 以使用您选择的网络栈,列出了可用选项,提供了一些有关如何为应用选择网络栈的指导,并说明了如何为流媒体启用缓存。
将 ExoPlayer 配置为使用特定网络栈
ExoPlayer 通过 DataSource 组件加载数据,这些组件是从应用代码注入的 DataSource.Factory 实例中获取的。
如果您的应用只需要播放 http(s) 内容,那么选择网络栈非常简单,只需将应用注入的所有 DataSource.Factory 实例更新为与您要使用的网络栈对应的 HttpDataSource.Factory 实例即可。如果您的应用还需要播放非 http(s) 内容(例如本地文件),请使用 DefaultDataSource.Factory:
Kotlin
DefaultDataSource.Factory( context, /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(context), )
Java
new DefaultDataSource.Factory( context, /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(context));
在此示例中,PreferredHttpDataSource.Factory 是与您的首选网络栈对应的工厂。DefaultDataSource.Factory 层增加了对非 http(s) 来源(例如本地文件)的支持。
以下示例展示了如何构建一个 ExoPlayer,该 将使用 Cronet
网络栈,并且还支持播放非 HTTP(S) 内容。
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 是 Android 上的推荐默认网络栈,从 API 34(或 S 扩展程序 7)开始提供。在大多数情况下,它在内部使用 Cronet 网络栈,支持基于 QUIC 协议的 HTTP、HTTP/2 和 HTTP/3。
ExoPlayer 通过其 HttpEngineDataSource.Factory 支持 HttpEngine。您可以按照将 ExoPlayer 配置为使用特定网络栈中的说明注入此数据源工厂。
Cronet
Cronet 是以库的形式提供给 Android 应用使用的 Chromium 网络栈。Cronet 利用多种技术来减少延迟和提高网络请求吞吐量,以满足您的应用的运行需要,包括 ExoPlayer 发出的网络请求。它原生支持 HTTP、HTTP/2 和 HTTP/3 over QUIC 协议。Cronet 被全球一些最大的在线影音应用(包括 YouTube)所使用。
ExoPlayer 通过其 Cronet 库支持 Cronet。如需详细了解如何使用该库,请参阅该库的 README.md。请注意,Cronet 库能够使用三种底层 Cronet 实现:
- Google Play 服务:我们建议在大多数情况下使用此实现,并在 Google Play 服务不可用时回退到 Android 的内置网络栈 (
DefaultHttpDataSource)。 - Cronet Embedded:如果您的用户有很大一部分位于 Google Play 服务未广泛普及的市场,或者您希望控制所使用的 Cronet 实现的确切版本,那么这可能是一个不错的选择。Cronet Embedded 的主要缺点是,它会使应用的大小增加大约 8MB。
- Cronet 后备:Cronet 的后备实现将 Cronet 的 API 实现为 Android 内置网络栈的封装容器。它不应与 ExoPlayer 搭配使用,因为直接使用 Android 的内置网络栈(通过使用
DefaultHttpDataSource)更高效。
OkHttp
OkHttp 是另一种现代网络栈,被许多热门 Android 应用广泛使用。它支持 HTTP 和 HTTP/2,但尚不支持 HTTP/3 over QUIC。
ExoPlayer 通过其 OkHttp 库支持 OkHttp。如需详细了解如何使用该库,请参阅该库的 README.md。使用 OkHttp 库时,网络栈会嵌入到应用中。这与 Cronet Embedded 类似,但 OkHttp 的大小要小得多,只会使应用增加不到 1MB。
Android 的内置网络栈
ExoPlayer 支持将 Android 的内置网络栈与 DefaultHttpDataSource 和 DefaultHttpDataSource.Factory(属于 ExoPlayer 核心库)搭配使用。
确切的网络栈实现取决于底层设备上运行的软件。在大多数设备上,仅支持 HTTP(即不支持 HTTP/2 和 HTTP/3 over QUIC)。
其他网络栈
应用还可以将其他网络栈与 ExoPlayer 集成。
为此,请实现一个封装网络栈的 HttpDataSource,以及一个对应的 HttpDataSource.Factory。ExoPlayer 的 Cronet 和 OkHttp 库就是很好的示例。
与纯 Java 网络栈集成时,最好实现 DataSourceContractTest,以检查 HttpDataSource 实现是否正常运行。OkHttp 库中的 OkHttpDataSourceContractTest 就是一个很好的示例,展示了如何实现这一点。
选择网络栈
下表概述了 ExoPlayer 支持的网络栈的优缺点。
| 网络栈 | 协议 | APK 大小影响 | 备注 |
|---|---|---|---|
| HttpEngine | HTTP HTTP/2 HTTP/3 over QUIC |
无 | 仅适用于 API 34 或 S Extensions 7 |
| Cronet(Google Play 服务) | HTTP HTTP/2 HTTP/3 over QUIC |
小 (小于 100KB) |
需要 Google Play 服务。Cronet 版本会自动更新 |
| Cronet(嵌入式) | HTTP HTTP/2 HTTP/3 over QUIC |
大 (约 8MB) |
Cronet 版本由应用开发者控制 |
| Cronet(后备) | HTTP (因设备而异) |
小 (小于 100KB) |
不建议用于 ExoPlayer |
| OkHttp | HTTP HTTP/2 |
小 (小于 1MB) |
|
| 内置网络栈 | HTTP (因设备而异) |
无 | 实现方式因设备而异 |
HTTP/2 和 HTTP/3 over QUIC 协议可以显著提升媒体流式传输性能。特别是,在流式传输使用内容分发网络 (CDN) 分发的自适应媒体时,使用这些协议可以使 CDN 的运行效率大幅提高。 因此,与使用 Android 的内置网络栈相比,HttpEngine 和 Cronet 对 HTTP/2 和基于 QUIC 的 HTTP/3 的支持(以及 OkHttp 对 HTTP/2 的支持)是一项重大优势,前提是托管内容的服务器也支持这些协议。
如果仅考虑媒体串流,我们建议使用 Google Play 服务提供的 HttpEngine 或 Cronet,并在 Google Play 服务不可用时回退到 DefaultHttpDataSource。此建议在以下两方面之间取得了良好的平衡:在大多数设备上支持通过 QUIC 使用 HTTP/2 和 HTTP/3,以及避免 APK 大小显著增加。此建议也有例外情况。如果运行您应用的设备中有很大一部分可能无法使用 Google Play 服务,则使用 Cronet Embedded 或 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();