播放器界面

播放器是应用中用于播放媒体文件的组件。Media3 Player 接口为通常由播放器处理的功能设置了框架。包括:

  • 影响播放控件,例如播放、暂停和跳转
  • 查询当前播放媒体的属性,例如播放位置
  • 管理媒体文件播放列表/队列
  • 配置播放属性,例如随机播放、重复播放、播放速度和音量
  • 向屏幕呈现视频

Media3 还提供了 Player 接口的实现,称为 ExoPlayer。

组件之间的通用接口

Media3 中的多个组件都实现了 Player 接口,例如:

组件 说明和行为备注
ExoPlayer 一个媒体播放器 API,以及 Player 接口的默认实现。
MediaController 与 MediaSession 互动以发送播放命令。如果您的 Player 和 MediaSession 位于与播放器界面所在的 Activity 或 Fragment 分开的 Service 中,您可以将 MediaController 分配为界面组件(例如 PlayerView 或 Player 可组合项)的播放器。播放和播放列表方法调用会通过 MediaSession 发送到 Player。
MediaBrowser 除了 MediaController 提供的功能之外,还可与 MediaLibrarySession 交互,以浏览可用的媒体内容。
SimpleBasePlayer 一种 Player 实现,可将要实现的方法数量减少到最低限度。当您使用要连接到 MediaSession 的自定义播放器时,此功能非常有用。
ForwardingSimpleBasePlayer SimpleBasePlayer 子类,旨在将播放操作转发到另一个 Player,同时允许与 SimpleBasePlayer 相同的一致行为自定义。使用此类可抑制或修改特定的播放操作。
RemoteCastPlayer 用于控制远程 Cast 接收器应用上的播放的 Player 实现。
CastPlayer 用于控制本地和远程 Cast 播放的 Player 实现。

虽然 MediaSession 未实现 Player 接口,但在创建 时需要 Player。其目的是提供从其他进程或线程对 Player 的访问权限。

Media3 播放架构

如果您有权访问 Player,则应直接调用其方法来发出播放命令。您可以通过实现 MediaSession 来通告播放并向外部来源授予播放控制权。这些外部来源会实现 MediaController,这有助于连接到媒体会话并发出播放命令请求。

在后台播放媒体时,您需要将媒体会话和播放器放置在作为前台服务运行的 MediaSessionService 或 MediaLibraryService 中。这样,您就可以将播放器与应用中包含播放控件界面的 Activity 分离开来。这可能需要您使用媒体控制器。

一张图表,展示了 Media3 播放组件如何融入媒体应用架构。
图 1:Player 接口在 Media3 架构中扮演着关键角色。

玩家状态

实现 Player 接口的媒体播放器的状态主要包含 4 类信息:

  1. 播放状态
  2. 媒体文件播放列表
  3. 播放/暂停属性,例如:
    • playWhenReady:指示用户是希望媒体在可能的情况下播放还是保持暂停状态
    • 播放抑制原因:指明播放被抑制的原因(如果适用),即使 playWhenReady 为 true 也是如此
    • isPlaying:指示播放器当前是否正在播放,仅当播放状态为 STATE_READY、playWhenReady 为 true 且播放未被抑制时,此值才为 true
  4. 播放位置,包括:

此外,Player 接口还允许访问可用曲目、媒体元数据、播放速度、音量以及播放的其他辅助属性。

监听更改

使用 Player.Listener 监听 Player 中的更改。如需详细了解如何创建和使用监听器,请参阅有关播放器事件的 ExoPlayer 文档。

请注意,监听器接口不包含任何用于跟踪正常播放进度的回调。如需持续监控播放进度(例如设置进度条界面),您应以适当的时间间隔查询当前位置。

Kotlin

fun checkPlaybackPosition(delayMs: Long): Boolean =
  handler.postDelayed(
    {
      val currentPosition = player.currentPosition
      // Update UI based on currentPosition
      checkPlaybackPosition(delayMs)
    },
    delayMs,
  )

Java

boolean checkPlaybackPosition(long delayMs) {
  return handler.postDelayed(
      () -> {
        long currentPosition = player.getCurrentPosition();
        // Update UI based on currentPosition
        checkPlaybackPosition(delayMs);
      },
      delayMs);
}

控制播放

Player 接口提供了多种方法来操纵状态和控制播放:

自定义 Player 实现

如需创建自定义播放器,您可以扩展 Media3 中包含的 SimpleBasePlayer。此类提供 Player 接口的基本实现,可将您需要实现的方法数量降至最低。

首先,替换 getState() 方法。调用此方法时,应填充当前玩家状态,包括:

  • 可用命令集
  • 播放属性,例如播放器是否应在播放状态为 STATE_READY 时开始播放、当前播放的媒体文件的索引,以及当前媒体文件中的播放位置

Kotlin

class CustomPlayer(looper: Looper) : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build()
  }
}

Java

private static final class CustomPlayer extends SimpleBasePlayer {
  public CustomPlayer(Looper looper) {
    super(looper);
  }

  @Override
  protected State getState() {
    return new State.Builder()
        .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
        // Configure additional playback properties
        .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
        .setCurrentMediaItemIndex(0)
        .setContentPositionMs(0)
        .build();
  }
}

SimpleBasePlayer 将强制要求使用有效的状态值组合创建 State。它还会处理监听器并向监听器通知状态更改。如果您需要手动触发状态更新,请调用 invalidateState()。

除了 getState() 方法之外,您只需实现用于播放器声明为可用的命令的方法。找到与您要实现的功能相对应的可替换处理程序方法。例如,替换 handleSeek() 方法以支持 COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM 和 COMMAND_SEEK_TO_NEXT_MEDIA_ITEM 等操作。

修改 Player 实现

您可以使用 ForwardingSimpleBasePlayer 修改现有 Player 的状态和行为,而无需创建完全自定义的 Player。如需了解详情,请参阅“自定义”页面上的指南。