从 Android 8.0(API 级别 26)开始,MediaPlayer
包含支持播放受 DRM 保护内容的 API。MediaPlayer
DRM API 类似于 MediaDrm
提供的低级 API,但它们在更高层面运行,并且不公开底层提取器、DRM 和加密对象。
虽然 MediaPlayer
DRM API 不提供 MediaDrm
的全部功能,但它支持最常见的用例。当前实现可以处理以下内容类型:
- Widevine 保护的本地媒体文件
- Widevine 保护的远程或流媒体文件
以下代码段演示了如何在同步实现中使用新的 DRM MediaPlayer
方法。
要管理 DRM 控制的媒体,您需要将新方法与 MediaPlayer 的常规调用流程一起包含,如本例所示:
Kotlin
mediaPlayer?.apply {
setDataSource()
setOnDrmConfigHelper() // optional, for custom configuration
prepare()
drmInfo?.also {
prepareDrm()
getKeyRequest()
provideKeyResponse()
}
// MediaPlayer is now ready to use
start()
// ...play/pause/resume...
stop()
releaseDrm()
}
Java
setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
prepareDrm();
getKeyRequest();
provideKeyResponse();
}
// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();
首先,初始化 MediaPlayer
对象并像往常一样使用 setDataSource()
设置其源。然后,要使用 DRM,请执行以下步骤:
- 如果您希望应用执行自定义配置,请定义一个
OnDrmConfigHelper
接口,并使用setOnDrmConfigHelper()
将其附加到播放器。 - 调用
prepare()
。 - 调用
getDrmInfo()
。如果源包含 DRM 内容,该方法将返回一个非 null 的MediaPlayer.DrmInfo
值。
如果 MediaPlayer.DrmInfo
存在:
- 检查可用 UUID 的映射并选择一个。
- 通过调用
prepareDrm()
为当前源准备 DRM 配置。- 如果您已创建并注册
OnDrmConfigHelper
回调,则在prepareDrm()
执行期间会调用该回调。这允许您在打开 DRM 会话之前执行 DRM 属性的自定义配置。回调在调用prepareDrm()
的线程中同步调用。要访问 DRM 属性,请调用getDrmPropertyString()
和setDrmPropertyString()
。避免执行耗时操作。 - 如果设备尚未进行预配,
prepareDrm()
还会访问预配服务器以预配设备。这可能需要不同的时间,具体取决于网络连接。
- 如果您已创建并注册
- 要获取要发送到许可服务器的不透明密钥请求字节数组,请调用
getKeyRequest()
。 - 要将从许可服务器收到的密钥响应通知 DRM 引擎,请调用
provideKeyResponse()
。结果取决于密钥请求的类型:- 如果响应是针对离线密钥请求,则结果是密钥集标识符。您可以将此密钥集标识符与
restoreKeys()
一起使用,以将密钥恢复到新会话。 - 如果响应是针对流式传输或释放请求,则结果为 null。
- 如果响应是针对离线密钥请求,则结果是密钥集标识符。您可以将此密钥集标识符与
异步准备 DRM
默认情况下,prepareDrm()
同步运行,阻塞直到准备完成。但是,新设备上的首次 DRM 准备也可能需要预配,prepareDrm()
会在内部处理此操作,并且由于涉及网络操作,可能需要一些时间才能完成。您可以通过定义和设置 MediaPlayer.OnDrmPreparedListener
来避免阻塞 prepareDrm()
。
设置 OnDrmPreparedListener
。prepareDrm()
在后台执行预配(如果需要)和准备。当预配和准备完成时,系统会调用侦听器。不要对调用序列或侦听器运行的线程做任何假设(除非您使用处理程序线程注册侦听器)。系统可以在 prepareDrm()
返回之前或之后调用侦听器。
异步设置 DRM
您可以通过创建和注册用于 DRM 准备的 MediaPlayer.OnDrmInfoListener
和用于启动播放器的 MediaPlayer.OnDrmPreparedListener
来异步初始化 DRM。它们与 prepareAsync()
协同工作,如本例所示:
Kotlin
setOnPreparedListener()
setOnDrmInfoListener()
setDataSource()
prepareAsync()
// ...
// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
mediaPlayer.apply {
prepareDrm()
getKeyRequest()
provideKeyResponse()
}
}
// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
Java
setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...
// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
prepareDrm();
getKeyRequest();
provideKeyResponse();
}
// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {
start();
}
处理加密媒体
从 Android 8.0(API 级别 26)开始,MediaPlayer
还可以解密基本流类型 H.264 和 AAC 的通用加密方案 (CENC) 和 HLS 样本级加密媒体 (METHOD=SAMPLE-AES)。以前支持全段加密媒体 (METHOD=AES-128)。
了解详情
Jetpack Media3 是您应用中媒体播放的推荐解决方案。详细了解它。
这些页面涵盖了与录制、存储和播放音频和视频相关的主题: