ExoPlayer 库的核心是 Player
接口。Player
提供了传统的高级媒体播放器功能,例如
缓冲媒体、播放、暂停和跳转。默认实现 ExoPlayer
为
能够尽量少做出假设(因此对
正在播放的媒体的类型、存储方式和位置,以及
。您无需直接实现媒体的加载和渲染,
ExoPlayer
实现将此工作委托给注入的组件
当创建播放器或向播放器传递新的媒体来源时触发。
所有 ExoPlayer
实现通用的组件包括:
MediaSource
实例,用于定义要播放的媒体、加载媒体以及 可以从中读取已加载媒体创建了一个MediaSource
实例 通过播放器内的MediaSource.Factory
从MediaItem
中获取。他们还可以 使用基于媒体来源的播放列表 API 直接传递给播放器。- 可将
MediaItem
转换为MediaSource
的MediaSource.Factory
实例。通过MediaSource.Factory
是在创建播放器时注入的。 - 渲染媒体各个组件的
Renderer
实例。这些是 会在创建播放器时注入 - 一个
TrackSelector
,用于选择MediaSource
提供的音轨作为 每个可用的Renderer
流量消耗。注入TrackSelector
。 - 一个
LoadControl
,用于控制MediaSource
何时缓冲更多媒体;以及 缓冲多少媒体内容当播放器执行上述操作时,LoadControl
创建。 - 一个
LivePlaybackSpeedControl
,用于控制直播期间的播放速度 播放,使播放器能够靠近配置的实时偏移量。答LivePlaybackSpeedControl
是在创建播放器时注入的。
注入用于实现播放器组件的组件的概念 功能。默认情况下, 有些组件会将工作委托给进一步注入的组件。这样,许多 子组件需要单独替换为 以自定义方式进行配置
玩家自定义
以下是通过注入组件来自定义播放器的一些常见示例: 如下所述。
配置网络堆栈
我们有一个页面介绍了如何自定义 ExoPlayer 使用的网络堆栈。
缓存从网络加载的数据
自定义服务器交互
某些应用可能想要拦截 HTTP 请求和响应。您可能需要 注入自定义请求标头、读取服务器的响应标头、修改 请求的”URI 等。例如,您的应用可以通过注入 令牌作为标头来请求媒体片段。
以下示例演示了如何通过
将自定义 DataSource.Factory
注入 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();
在上面的代码段中,注入的 HttpDataSource
包含标头
"Header: Value"
。对于每
与 HTTP 来源交互
作为更精细的方法,您可以使用
ResolvingDataSource
。以下代码段展示了如何注入
请求标头:
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)));
您还可以使用 ResolvingDataSource
执行
对 URI 进行即时修改,如以下代码段所示:
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)));
自定义错误处理
实现自定义 LoadErrorHandlingPolicy
可让应用自定义
ExoPlayer 对加载错误的响应方式。例如,应用可能需要快速失败
而无需重试多次
控制玩家在每次重试之间等待的时间。以下代码段
展示了如何实现自定义退避逻辑:
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();
LoadErrorInfo
参数包含有关失败的加载
根据错误类型或失败的请求来自定义逻辑。
自定义提取器标志
提取器标志可用于自定义各种格式的提取方式
来自渐进式媒体可以在名为 DefaultExtractorsFactory
提供给 DefaultMediaSourceFactory
。以下示例将一个标志
为 MP3 流实现基于索引的搜索。
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();
启用恒定比特率搜寻
对于 MP3、ADS 和 AMR 流,您可以使用
采用 FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
标志的恒定比特率假设。
可以使用
DefaultExtractorsFactory.setXyzExtractorFlags
方法。接收者
为支持它的所有提取器启用恒定比特率搜寻,使用
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
。
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
然后,可以通过 DefaultMediaSourceFactory
注入 ExtractorsFactory
,如下所示:
自定义提取器标志的说明。
启用异步缓冲区队列
异步缓冲区队列是 ExoPlayer 渲染的一项增强功能
流水线,该流水线以异步模式运行 MediaCodec
实例,
它使用额外的线程来安排数据的解码和渲染。启用
可以减少丢帧和音频欠载
在搭载 Android 12 的设备上,异步缓冲区队列默认处于启用状态 (API 级别 31)及更高版本,并且从 Android 6.0(API 级别 23)开始可手动启用。 请考虑在您观察到设备掉落的特定设备上启用此功能 帧或音频欠载,尤其是在播放受 DRM 保护或高帧速率的视频时 内容。
在最简单的情况下,您需要将 DefaultRenderersFactory
注入
播放器,如下所示:
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();
如果您要直接实例化渲染程序,请将
AsynchronousMediaCodecAdapter.Factory
向MediaCodecVideoRenderer
和
MediaCodecAudioRenderer
构造函数。
使用 ForwardingPlayer
拦截方法调用
您可以自定义 Player
实例的某些行为,只需将其封装在内即可
ForwardingPlayer
的子类和替换方法,以执行
以下:
- 先访问参数,然后再将参数传递给委托
Player
。 - 在返回之前,访问委托
Player
的返回值。 - 完全重新实现该方法。
替换 ForwardingPlayer
方法时,请务必确保
实施流程会保持一致并符合Player
尤其是在处理
相同或相关的行为。例如:
- 如果您想覆盖每一个“play”操作,您需要同时覆盖
ForwardingPlayer.play
和ForwardingPlayer.setPlayWhenReady
,因为 调用者会期望这些方法的行为playWhenReady = true
。 - 如果您想更改快进增量,则需要同时替换
ForwardingPlayer.seekForward
,以通过您的自定义ForwardingPlayer.getSeekForwardIncrement
,以便报告 将正确的自定义增量传回给调用方。 - 如果您想控制玩家通告哪些
Player.Commands
因此您必须同时替换Player.getAvailableCommands()
和Player.isCommandAvailable()
,并监听Player.Listener.onAvailableCommandsChanged()
回调,用于接收通知 来自底层播放器的更改。
MediaSource 自定义
上面的示例注入了自定义组件,以便在
传递到播放器的 MediaItem
对象。精细自定义的适用场景
也可以将自定义组件注入个别
MediaSource
实例,此类实例可以直接传递给播放器。示例
下面展示了如何自定义 ProgressiveMediaSource
以使用自定义
DataSource.Factory
、ExtractorsFactory
和 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));
创建自定义组件
该库会提供顶部列出的组件的默认实现。
了解常见用例。ExoPlayer
可以使用这些组件,但
如果非标准行为出现以下情况,也可以构建为使用自定义实现
必填字段。自定义实现的一些用例包括:
Renderer
- 您可能需要实现自定义Renderer
来处理 默认实现不支持的媒体类型 库。TrackSelector
- 实现自定义TrackSelector
可让应用 开发者更改由MediaSource
公开的轨道的显示方式 供每个可用的Renderer
使用。LoadControl
- 实现自定义LoadControl
可让应用 更改播放器的缓冲政策。Extractor
- 如果您需要支持目前不支持的容器格式 请考虑实现自定义Extractor
类。MediaSource
- 实现自定义MediaSource
类可能需要 如果您希望获取媒体样本,以提供给 自定义方式,或者如果您希望实现自定义MediaSource
合成 行为MediaSource.Factory
- 实现自定义MediaSource.Factory
允许应用自定义MediaSource
的创建方式 从MediaItem
开始。DataSource
- ExoPlayer 的上游软件包已包含许多 不同用例的DataSource
实现。您可能需要 实现您自己的DataSource
类,以以其他方式加载数据,例如通过 使用自定义 HTTP 堆栈或从自定义 缓存。
构建自定义组件时,我们建议采用以下做法:
- 如果自定义组件需要将事件报告回应用,我们建议
使用与现有 ExoPlayer 组件相同的模型执行此操作,
使用
EventDispatcher
类或将Handler
与 组件构造函数的监听器。 - 我们建议自定义组件使用与现有 ExoPlayer 相同的模型
组件以允许应用在播放期间重新配置。为此,
自定义组件应实现
PlayerMessage.Target
并接收 在handleMessage
方法中更改配置。应用代码应 通过调用 ExoPlayer 的createMessage
方法来传递配置更改, 配置消息,并使用PlayerMessage.send
。发送要在播放线程中传送的消息 确保它们按顺序执行 播放器。