使用 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 是您应用中媒体播放的推荐解决方案。详细了解它。

这些页面涵盖了与录制、存储和播放音频和视频相关的主题: