播放器界面

播放器是应用中用于促进媒体项播放的组件。Media3 Player 接口概述了通常由播放器处理的功能。其中包括:

  • 影响播放控件,例如播放、暂停和跳转
  • 查询当前正在播放的媒体的属性,例如播放位置
  • 管理媒体项的播放列表/队列
  • 配置播放属性,例如随机播放、重复、速度和音量
  • 将视频渲染到屏幕上

Media3 还提供 Player 接口的实现,名为 ExoPlayer

组件之间的通用接口

Media3 中的多个组件实现了播放器接口,例如:

组件 说明和行为说明
ExoPlayer 媒体播放器 API 和 Player 接口的默认实现。
MediaController MediaSession 交互以发送播放命令。如果您的 PlayerMediaSession 位于与播放器界面所在的 ActivityFragment 不同的 Service 中,那么您可以将 MediaController 指定为 PlayerView 界面的播放器。播放和播放列表方法调用将通过您的 MediaSession 发送到您的 Player
MediaBrowser 除了 MediaController 提供的功能之外,还会与 MediaLibrarySession 交互以浏览可用的媒体内容。
ForwardingPlayer 将方法调用转发给另一个 PlayerPlayer 实现。可以使用此类通过替换相应的方法来抑制或修改特定操作。
SimpleBasePlayer 一种 Player 实现,可将要实现的方法数量减少到最少。在使用您希望连接到 MediaSession 的自定义播放器时很有用。
CastPlayer 与 Cast 接收器应用通信的 Player 实现。行为取决于底层 Cast 会话。

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

Media3 播放架构

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

在后台播放媒体时,您需要将媒体会话和播放器放在作为前台服务运行的 MediaSessionServiceMediaLibraryService 中。如果这样做,您可以将播放器与包含播放控制界面的应用中的 Activity 分开。因此,您可能需要使用媒体控制器。

展示 Media3 播放组件如何适应媒体应用架构的示意图。
图 1Player 接口在 Media3 的架构中起着关键作用。

播放器状态

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

  1. 播放状态
  2. 媒体项播放列表
  3. 播放/暂停属性,例如:
    • playWhenReady:指示用户是希望尽可能播放媒体还是保持暂停状态
    • 禁止播放的原因:指明禁止播放的原因(如果适用),即使 playWhenReadytrue 也是如此
    • isPlaying:指示播放器当前是否正在播放,仅当播放状态为 STATE_READYplayWhenReadytrue 且未禁止播放时,该值将为 true
  4. 播放位置,包括:

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

监听更改

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

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

Kotlin

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

Java

Handler handler = new Handler(Looper.getMainLooper());
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 : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(...) // 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

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

  @Override
  protected State getState() {
    return new State.Builder()
      .setAvailableCommands(...) // 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_ITEMCOMMAND_SEEK_TO_NEXT_MEDIA_ITEM 等操作。