播放器是应用中用于协助播放媒体项的组件。Media3 Player
接口为通常由播放器处理的功能设置了轮廓。包括:
- 影响播放控件,例如播放、暂停和跳转
- 查询当前正在播放的媒体的属性,例如播放位置
- 管理媒体内容的播放列表/队列
- 配置播放属性,例如随机播放、重复、速度和音量
- 将视频渲染到屏幕
Media3 还提供了 Player
接口的实现,称为 ExoPlayer
。
组件之间的通用接口
Media3 中的多个组件会实现 Player 接口,例如:
组件 | 说明和行为备注 |
---|---|
ExoPlayer |
媒体播放器 API 和 Player 接口的默认实现。 |
MediaController |
与 MediaSession 互动以发送播放命令。如果您的 Player 和 MediaSession 位于与玩家界面所在的 Activity 或 Fragment 分离的 Service 中,您可以将 MediaController 指定为 PlayerView 界面的玩家。播放和播放列表方法调用会通过 MediaSession 发送到 Player 。
|
MediaBrowser |
除了 MediaController 提供的功能之外,还与 MediaLibrarySession 交互以浏览可用媒体内容。
|
SimpleBasePlayer |
Player 实现,可最大限度地减少要实现的方法数量。在使用要连接到 MediaSession 的自定义播放器时非常有用。
|
ForwardingSimpleBasePlayer |
一个 SimpleBasePlayer 子类,用于将播放操作转发到另一个 Player ,同时允许进行与 SimpleBasePlayer 相同的一致行为自定义。您可以使用此类来抑制或修改特定的播放操作。
|
CastPlayer |
与 Cast 接收器应用通信的 Player 实现。行为取决于底层 Cast 会话。
|
虽然 MediaSession
不会实现 Player
接口,但在创建 MediaSession
时需要 Player
。其目的是提供对其他进程或线程的 Player
访问权限。
Media3 播放架构
如果您有权访问 Player
,则应直接调用其方法来发出播放命令。您可以通过实现 MediaSession
来通告播放内容并向外部来源授予播放控制权。这些外部来源会实现 MediaController
,以便连接到媒体会话并发出播放命令请求。
在后台播放媒体时,您需要将媒体会话和播放器放置在作为前台服务运行的 MediaSessionService
或 MediaLibraryService
中。这样一来,您就可以将播放器与应用中包含用于控制播放的界面的 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 文档中的播放器事件部分。
请注意,监听器接口不包含任何用于跟踪正常播放进度的回调。如需持续监控播放进度(例如设置进度条界面),您应在适当的时间间隔内查询当前位置。
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
等操作。