使用 MediaPlayer 和数字版权管理 (DRM)

从 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,请执行以下步骤:

  1. 如果您希望应用执行自定义配置,请定义 OnDrmConfigHelper 接口,并使用 setOnDrmConfigHelper() 将其附加到播放器。
  2. 调用 prepare()
  3. 调用 getDrmInfo()。如果来源具有 DRM 内容,则该方法会返回一个非 null MediaPlayer.DrmInfo 值。

如果 MediaPlayer.DrmInfo 存在:

  1. 检查可用 UUID 的映射,然后选择一个。
  2. 通过调用 prepareDrm() 为当前来源准备 DRM 配置。
    • 如果您创建并注册了 OnDrmConfigHelper 回调,则系统会在执行 prepareDrm() 时调用该回调。这样一来,您就能够在打开 DRM 会话之前执行 DRM 属性的自定义配置。该回调会在调用 prepareDrm() 的线程中同步调用。如需访问 DRM 属性,请调用 getDrmPropertyString()setDrmPropertyString()。避免执行冗长的操作。
    • 如果尚未配置设备,prepareDrm() 还会访问配置服务器来配置该设备。所需的时间因网络连接而有所不同。
  3. 如需获取不透明的密钥请求字节数组以发送到许可服务器,请调用 getKeyRequest()
  4. 如需向 DRM 引擎告知从许可服务器接收到的密钥响应,请调用 provideKeyResponse()。结果取决于密钥请求的类型:
    • 如果响应针对的是离线密钥请求,则结果为密钥组标识符。您可以将此密钥组标识符与 restoreKeys() 结合使用,以将这些密钥恢复到新的会话中。
    • 如果响应针对的是流式传输或发布请求,则结果为 null。

异步准备 DRM

默认情况下,prepareDrm() 会同步运行,阻塞会持续到准备工作完成。不过,在新设备上进行的首次 DRM 准备可能还需要进行配置,该问题由 prepareDrm() 在内部进行处理,并且由于涉及网络操作,可能需要一些时间才能完成。您可以通过定义和设置 MediaPlayer.OnDrmPreparedListener 来避免阻塞 prepareDrm()

设置 OnDrmPreparedListenerprepareDrm() 会在后台执行配置(如果需要)和准备工作。配置和准备工作完成后,系统会调用监听器。请勿对调用顺序或运行监听器的线程做任何假设(除非您将监听器注册到处理程序线程)。系统可以在 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 是应用中播放媒体的推荐解决方案。了解详情

以下页面介绍了有关录制、存储以及播放音频和视频的主题: