使用 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 內容,方法會傳回非空值 MediaPlayer.DrmInfo 值。

如果 MediaPlayer.DrmInfo 存在:

  1. 查看可用 UUID 的地圖,然後選擇一個。
  2. 請呼叫 prepareDrm(),為目前來源準備 DRM 設定。
    • 如果您建立並註冊了 OnDrmConfigHelper 回呼,系統會在 prepareDrm() 執行時呼叫該回呼。這樣一來,您就能在開啟 DRM 工作階段前,執行 DRM 屬性的自訂設定。回呼會在呼叫 prepareDrm() 的執行緒中同步呼叫。如要存取 DRM 屬性,請呼叫 getDrmPropertyString()setDrmPropertyString()。避免執行冗長的作業。
    • 如果裝置尚未佈建,prepareDrm() 也會存取佈建伺服器來佈建裝置。這項作業可能需要一段時間才能完成,具體時間長短視網路連線而定。
  3. 如要取得要傳送至授權伺服器的不透明鍵要求位元組陣列,請呼叫 getKeyRequest()
  4. 如要將從授權伺服器收到的金鑰回應通知 DRM 引擎,請呼叫 provideKeyResponse()。結果取決於索引鍵要求的類型:
    • 如果回應是離線索引鍵要求,結果就是索引鍵組別 ID。您可以將這個鍵組 ID 與 restoreKeys() 搭配使用,將金鑰還原至新工作階段。
    • 如果回應是針對串流或發布要求,結果會為空值。

以非同步方式準備 DRM

根據預設,prepareDrm() 會以同步方式執行,並在準備完成前保持封鎖狀態。不過,新裝置的首次 DRM 準備作業也可能需要進行佈建,而 prepareDrm() 會在內部處理此作業,且由於涉及網路作業,因此可能需要一些時間才能完成。您可以定義並設定 MediaPlayer.OnDrmPreparedListener,避免在 prepareDrm() 上發生封鎖。

設定 OnDrmPreparedListenerprepareDrm() 會在背景執行佈建作業 (如有需要) 和準備作業。佈建和準備程序完成後,系統會呼叫監聽器。請勿對呼叫序列或事件監聽器執行的執行緒做出任何假設 (除非您使用處理常式執行緒註冊事件監聽器)。系統可以在 prepareDrm() 回傳之前或之後呼叫監聽器。

非同步設定 DRM

您可以建立並註冊 MediaPlayer.OnDrmInfoListener 來準備 DRM,並註冊 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 解決方案。請參閱更多資訊

以下頁面將說明錄製、儲存及播放音訊和影片的相關主題: