上次更新日期:2020 年 9 月 9 日
为视频播放添加 MediaSession 支持有什么好处?
媒体会话是连接 Android 平台和媒体应用的重要桥梁。它不仅会告知 Android 媒体内容正在播放,以便平台将媒体操作转发到正确的会话,还可以告知平台正在播放什么内容以及如何对其进行控制。
在您的应用中提供 MediaSession 可为用户带来诸多好处。以下是一些很好的示例。
Google 助理
用户可以在您的应用中,通过语音指令(例如“暂停”、“继续”和“下一个”)轻松与媒体内容互动。系统还可借助媒体内容的元数据弄清当前正在播放什么内容。
Android TV
在大屏幕上,对于使用支持 HDMI-CEC 的电视的用户,您的 Android TV 应用支持使用传统遥控器。由播放/暂停、停止、下一个和上一个按钮发出的命令将传送至您的应用。
屏幕媒体控件
从 Android 4.0(API 级别 14)开始,系统可以访问媒体会话的播放状态和元数据。此功能可让锁定屏幕显示媒体控件和海报图片。此行为因 Android 版本而异。
背景媒体
即使播放媒体内容的应用在后台运行,也可以在上述任意场景中控制媒体。
环境计算
提供媒体相关数据(即正在播放什么内容、如何对其进行控制)可在各个设备之间架起桥梁,以便用户能够以他们喜欢的方式与媒体互动。
您将构建的内容
在本 Codelab 中,您将扩展现有的 Exoplayer 示例,为其添加媒体会话支持。您的应用将:
- 正确反映媒体会话的活跃状态
- 将媒体控件传送到 ExoPlayer
- 将队列中媒体内容的元数据传递到媒体会话中
学习内容
- 媒体会话为何能够为用户提供更丰富的体验
- 如何创建媒体会话并管理其状态
- 如何将媒体会话连接到 ExoPlayer
- 如何在媒体会话的播放队列中添加媒体内容的元数据
- 如何添加其他(自定义)操作
本 Codelab 重点介绍 MediaSession SDK,这里不讨论无关的概念和代码块(包括与 ExoPlayer 实现相关的详细信息),但提供这些内容供您简单复制和粘贴。
所需条件
- 最新版 Android Studio(3.5 或更高版本)
- 开发 Android 应用的基础知识
我们从何处入手?
我们着手点是 ExoPlayer 中的主演示。此演示包含带有屏幕播放控件的视频,但不在一开始就使用媒体会话,非常适合我们将其作为起点并添加媒体会话!
获取 ExoPlayer 示例
我们先从 ExoPlayer 示例开始。通过以下链接从 GitHub 代码库中克隆该示例。
打开演示
在 Android Studio 中,打开位于 demos/main
下的主演示项目。
Android Studio 将提示您设置 SDK 路径。如果遇到任何问题,可以按照更新 IDE 和 SDK 工具的建议进行操作。
如果系统要求您使用最新版 Gradle,请进行更新。
请花点时间大致了解应用的设计方式。请注意,涉及的 Activity 有两个:SampleChooserActivity 和 PlayerActivity。Codelab 的剩余部分将主要探讨实际播放媒体内容的类 PlayerActivity,因此,请打开这个类,然后转到下一部分。
创建媒体会话
打开 PlayerActivity.java
。这个类可创建 ExoPlayer 并管理其函数,例如将视频呈现到屏幕上。在本 Activity 中,我们将把 ExoPlayer 连接到媒体会话。
在类的顶部声明以下两个字段。本部分会用到这些字段。
private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;
您将需要把“extension-mediasession”项目依赖项添加到“Module: demo”的模块级 build.gradle
中:
implementation project(path: ':extension-mediasession')
请注意,如果您将鼠标悬停在解析 MediaSessionConnector 时发生的错误上,Android Studio 可帮助您自动添加此依赖项:
最后,请通过添加以下内容来解析类导入:
import android.support.v4.media.session.MediaSessionCompat;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
创建 activity 后,我们将创建一个媒体会话和一个媒体会话连接器,后者充当媒体会话和 ExoPlayer 之间的中间层。
插入上述代码的理想位置也是创建 ExoPlayer 的位置。在我们的演示应用中,我们可以将代码附加到 initializePlayer()
的末尾。请务必在播放器实例化之后添加此逻辑!
private void initializePlayer() {
if (player == null) {
...
player = ...
...
mediaSession = new MediaSessionCompat(this, "sample");
mediaSessionConnector = new MediaSessionConnector(mediaSession);
mediaSessionConnector.setPlayer(player);
}
...
}
释放媒体会话
不再需要媒体会话时,请将其释放。我们在 releasePlayer()
中释放 ExoPlayer 时,还添加了以下代码:
private void releasePlayer() {
if (mediaSession != null) {
mediaSession.release();
}
...
}
管理媒体会话状态
现在,我们已实例化媒体会话,接下来需要确保在用户与 Activity 交互时正确反映媒体会话的状态。
用户启动 Activity 后,媒体会话应变为活动状态:
@Override
public void onStart() {
...
if (mediaSession != null) {
mediaSession.setActive(true);
}
}
由于我们的应用不在后台播放媒体内容,因此请务必确保用户离开 Activity 后媒体会话变为非活动状态:
@Override
public void onStop() {
super.onStop();
if (mediaSession != null) {
mediaSession.setActive(false);
}
...
}
运行演示
- 连接 Android 设备或启动模拟器。
- 请确保选择“演示”,以便从 Android Studio 工具栏运行演示。
- 点击 Android Studio 工具栏中的 。
- 应用在您的设备上启动后,请选择要播放的视频串流。
- 开始播放后,尝试使用以下
adb
命令来控制媒体会话:adb shell media dispatch pauseadb shell media dispatch playadb shell media dispatch play-pauseadb shell media dispatch fast-forwardadb shell media dispatch rewind
- 此外,您还可以通过执行以下命令了解 Android 如何访问您的媒体会话:
adb shell dumpsys media_session
- 如果您使用的是带有麦克风的实体设备,请尝试调用 Google 助理并发出语音指令,例如:“暂停”、“继续”、“快进 1 分钟”。
在 Android TV 上运行的 ExoPlayer 示例。
现在,我们可以在刚才在 initializePlayer()
中创建 MediaSessionConnector 的位置,扩展媒体会话支持的功能。
添加 TimelineQueueNavigator
ExoPlayer 以时间轴的形式表示媒体内容的结构。如需详细了解相关工作原理,请花点时间了解 ExoPlayer 的 Timeline 对象。通过了解此结构,我们可以在内容变化时收到通知,并在收到请求时显示当前正在播放的内容的元数据。
为此,我们将指定一个 TimelineQueueNavigator。在 initializePlayer()
中找到 MediaSessionConnector 的实例化代码段,并在初始化 mediaSession
之后添加 TimelineQueueNavigator 的实现。
mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
@Override
public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
return new MediaDescriptionCompat.Builder()
.setTitle("MediaDescription title")
.setDescription("MediaDescription description for " + windowIndex)
.setSubtitle("MediaDescription subtitle")
.build();
}
});
通过添加以下内容解析类导入:
import android.support.v4.media.MediaDescriptionCompat;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;
注意观察 windowIndex
参数是否与播放队列中该索引的媒体内容相对应。
现在,您已经添加了一些元数据,接下来可以测试 Google 助理是否理解当前正在播放什么内容。在 Android TV 上播放视频时,调用 Google 助理并询问“正在播放的是什么?”
您的播放器可能不支持某些操作,或者您希望添加对更多操作的支持?现在,我们来深入了解一下刚才我们在 initializePlayer()
中创建 MediaSessionConnector 所在的媒体会话。
声明支持的操作
尝试使用 mediaSessionConnector.setEnabledPlaybackActions()
自定义您希望媒体会话支持哪些操作。
请注意,完整的集合如下所示:
mediaSessionConnector.setEnabledPlaybackActions(
PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_SEEK_TO
| PlaybackStateCompat.ACTION_FAST_FORWARD
| PlaybackStateCompat.ACTION_REWIND
| PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
);
我们再次看一下此数据在平台上的显示方式:
- 和之前一样,播放视频。
- 通过执行以下命令,了解 Android 如何从您的媒体会话访问元数据:
adb shell dumpsys media_session
- 找到包含元数据的行,并注意观察其中包含的与
com.google.android.exoplayer2.demo/sample
关联的标题和说明。
添加其他操作
我们扩展我们的视频会话,使其支持更多操作。在本部分中,我们仅添加字幕支持。
字幕支持
为媒体会话添加字幕支持可让用户通过语音打开/关闭字幕。找到初始化媒体会话连接器的位置,添加以下内容:
mediaSessionConnector.setCaptionCallback(new MediaSessionConnector.CaptionCallback() {
@Override
public void onSetCaptioningEnabled(Player player, boolean enabled) {
Log.d("MediaSession", "onSetCaptioningEnabled: enabled=" + enabled);
}
@Override
public boolean hasCaptions(Player player) {
return true;
}
@Override
public boolean onCommand(Player player, ControlDispatcher controlDispatcher, String command, Bundle extras, ResultReceiver cb) {
return false;
}
}
);
最后,解析缺失的任意导入。
如需测试此功能,您可以调用 Android TV 上的 Google 助理,然后说“启用字幕”。检查 Logcat 中的消息,查看此功能如何调用您的代码。
恭喜,您已在示例中成功添加了媒体会话!
您可以通过以下方式实现大量功能:
- 添加媒体会话;
- 将媒体会话连接到 ExoPlayer 的实例;
- 添加元数据和其他操作。
现在您已了解丰富媒体应用所需的关键步骤,快试着为用户提供更丰富多样的体验!
结语
本 Codelab 是基于 ExoPlayer 源代码中的示例构建的。您无需通过源代码使用 ExoPlayer,建议您提取 ExoPlayer 和 MediaSessionConnector 的依赖项,以便轻松获取最新版本。
为此,只需替换项目依赖项即可,例如:
implementation project(modulePrefix + 'library-core')
implementation project(path: ':extension-mediasession')
并从 Maven 代码库中提取数据,例如:
implementation 'com.google.android.exoplayer:exoplayer-core:2.+'
implementation 'com.google.android.exoplayer:extension-mediasession:2.+'