播放器是应用中用于促进媒体项播放的组件。Media3 Player
接口为播放器通常处理的功能设置了概要。这包括
- 影响播放控制,例如播放、暂停和搜索
- 查询当前正在播放媒体的属性,例如播放位置
- 管理媒体项目的播放列表/队列
- 配置播放属性,例如随机播放、重复、速度和音量
- 将视频渲染到屏幕上
Media3 还提供了一个 Player
接口的实现,称为 ExoPlayer
.
组件之间的通用接口
Media3 中的几个组件实现了 Player 接口,例如
组件 | 描述和行为说明 |
---|---|
ExoPlayer |
一个媒体播放器 API,也是 Player 接口的默认实现。 |
MediaController |
与 MediaSession 交互以发送播放命令。如果你的 Player 和 MediaSession 位于一个与你的播放器 UI 所在的 Activity 或 Fragment 分开的 Service 中,你可以将你的 MediaController 指定为你的 PlayerView UI 的播放器。播放和播放列表方法调用通过你的 MediaSession 发送到你的 Player 。 |
MediaBrowser |
除了 MediaController 提供的功能外,它还与 MediaLibrarySession 交互以浏览可用的媒体内容。 |
ForwardingPlayer |
一个 Player 实现,它将方法调用转发到另一个 Player 。使用此类来通过覆盖相应方法来抑制或修改特定操作。 |
SimpleBasePlayer |
一个 Player 实现,它将要实现的方法数量减少到最少。当使用你想要连接到 MediaSession 的自定义播放器时很有用。 |
CastPlayer |
一个 Player 实现,它与 Cast 接收器应用程序通信。行为取决于底层的 Cast 会话。 |
虽然 MediaSession
没有实现 Player
接口,但它在创建时需要一个 Player
。它的目的是从其他进程或线程提供对 Player
的访问。
Media3 播放架构
如果你可以访问 Player
,你应该直接调用其方法来发出播放命令。你可以通过实现 MediaSession
来宣传你的播放并授予外部来源播放控制权。这些外部来源实现了一个 MediaController
,它有助于连接到媒体会话并发出播放命令请求。
在后台播放媒体时,你需要将你的媒体会话和播放器放在一个作为前台服务运行的 MediaSessionService
或 MediaLibraryService
中。如果你这样做,你就可以将你的播放器与包含播放控制 UI 的应用程序中的 Activity 分开。这可能需要你使用媒体控制器。
播放器状态
实现 Player
接口的媒体播放器的状态主要包含 4 类信息
- 播放状态
- 使用
getPlaybackState()
获取。 - 接口定义的状态值为
STATE_IDLE
、STATE_BUFFERING
、STATE_READY
和STATE_ENDED
。
- 使用
- 媒体项目的播放列表
- 用于播放的一系列
MediaItem
实例。 - 使用
getCurrentTimeline()
获取 Player
实例可以提供播放列表操作方法,例如 添加 或 删除 一个MediaItem
,以及方便的方法,例如getCurrentMediaItem()
。
- 用于播放的一系列
- 播放/暂停属性,例如
playWhenReady
:指示用户是否希望媒体在可能的情况下播放或保持暂停状态- 播放抑制原因:指示播放被抑制的原因(如果适用),即使
playWhenReady
为true
isPlaying
:指示播放器当前是否正在播放,如果播放状态为STATE_READY
、playWhenReady
为true
且播放未被抑制,则此值仅为true
- 播放位置,包括
- 当前媒体项索引:播放列表中当前
MediaItem
的索引。 isPlayingAd
:指示是否正在播放插入的广告。- 当前播放位置:当前
MediaItem
或插入的广告中的当前播放位置。
- 当前媒体项索引:播放列表中当前
此外,Player
接口允许访问 可用轨道、媒体元数据、播放速度、音量 和播放的其他辅助属性。
监听更改
使用 Player.Listener
监听 Player
中的更改。有关如何创建和使用侦听器的详细信息,请参阅 ExoPlayer 文档中的 播放器事件 部分。
请注意,侦听器接口不包含任何用于跟踪正常播放进度的回调。要持续监视播放进度(例如设置进度条 UI),你应该定期查询当前位置。
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
接口提供了多种方法来操作状态和控制播放
- 基本播放控制,如
play()
、pause()
、prepare()
和stop()
。 - 播放列表操作,如
addMediaItem()
或removeMediaItem()
。 - 搜索 以更改当前项目或位置。
- 设置 重复模式 和 随机播放模式。
- 更新 轨道选择首选项。
- 设置 播放速度。
自定义 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_ITEM
和 COMMAND_SEEK_TO_NEXT_MEDIA_ITEM
等操作。