自定义

ExoPlayer 库的核心是 Player 接口。Player 提供了传统的高级媒体播放器功能,例如 缓冲媒体、播放、暂停和跳转。默认实现 ExoPlayer 为 能够尽量少做出假设(因此对 正在播放的媒体的类型、存储方式和位置,以及 。您无需直接实现媒体的加载和渲染, ExoPlayer 实现将此工作委托给注入的组件 当创建播放器或向播放器传递新的媒体来源时触发。 所有 ExoPlayer 实现通用的组件包括:

  • MediaSource 实例,用于定义要播放的媒体、加载媒体以及 可以从中读取已加载媒体创建了一个 MediaSource 实例 通过播放器内的 MediaSource.FactoryMediaItem 中获取。他们还可以 使用基于媒体来源的播放列表 API 直接传递给播放器。
  • 可将 MediaItem 转换为 MediaSourceMediaSource.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.FactoryMediaCodecVideoRendererMediaCodecAudioRenderer 构造函数。

使用 ForwardingPlayer 拦截方法调用

您可以自定义 Player 实例的某些行为,只需将其封装在内即可 ForwardingPlayer 的子类和替换方法,以执行 以下:

  • 先访问参数,然后再将参数传递给委托 Player
  • 在返回之前,访问委托 Player 的返回值。
  • 完全重新实现该方法。

替换 ForwardingPlayer 方法时,请务必确保 实施流程会保持一致并符合Player 尤其是在处理 相同或相关的行为。例如:

  • 如果您想覆盖每一个“play”操作,您需要同时覆盖 ForwardingPlayer.playForwardingPlayer.setPlayWhenReady,因为 调用者会期望这些方法的行为 playWhenReady = true
  • 如果您想更改快进增量,则需要同时替换 ForwardingPlayer.seekForward,以通过您的自定义 ForwardingPlayer.getSeekForwardIncrement,以便报告 将正确的自定义增量传回给调用方。
  • 如果您想控制玩家通告哪些 Player.Commands 因此您必须同时替换 Player.getAvailableCommands()Player.isCommandAvailable(),并监听 Player.Listener.onAvailableCommandsChanged() 回调,用于接收通知 来自底层播放器的更改。

MediaSource 自定义

上面的示例注入了自定义组件,以便在 传递到播放器的 MediaItem 对象。精细自定义的适用场景 也可以将自定义组件注入个别 MediaSource 实例,此类实例可以直接传递给播放器。示例 下面展示了如何自定义 ProgressiveMediaSource 以使用自定义 DataSource.FactoryExtractorsFactoryLoadErrorHandlingPolicy

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。发送要在播放线程中传送的消息 确保它们按顺序执行 播放器。