什麼是 EME?

Encrypted Media Extensions 提供 API,可讓網頁應用程式與內容保護系統互動,以便播放已加密的音訊和視訊。

EME 的設計目的是讓相同的應用程式和加密檔案可在任何瀏覽器中使用,不受底層保護系統的影響。前者可透過標準化 API 和流程實現,後者則可透過通用加密概念實現。

EME 是 HTMLMediaElement 規格的擴充功能,因此名稱為 EME。由於 EME 屬於「擴充功能」,因此瀏覽器是否支援 EME 並非必要:如果瀏覽器不支援加密媒體,就無法播放加密媒體,但 HTML 規格並未要求必須使用 EME。根據 EME 規格

此提案擴充 HTMLMediaElement,提供 API 來控制受保護內容的播放作業。

這個 API 支援的用途廣泛,從簡單的明碼金鑰解密到高價值影片皆適用 (前提是使用者代理程式已適當實作)。應用程式會控制授權/金鑰交換作業,方便開發人員開發可支援多種內容解密和保護技術的強大播放應用程式。

本規格未定義內容保護或數位版權管理系統。而是定義了常見的 API,可用於探索、選取及與這類系統互動,以及與較簡單的內容加密系統互動。如要符合此規格,不必實作數位版權管理:只需將 Clear Key 系統做為通用基準實作即可。

通用 API 支援一組簡單的內容加密功能,讓頁面作者自行處理驗證和授權等應用程式功能。這項要求的做法是要求網頁仲介內容保護系統專屬訊息,而非假設加密系統與授權或其他伺服器之間的通訊是離線的。

EME 實作項目會使用下列外部元件:

  • 金鑰系統:內容保護 (DRM) 機制。除了 Clear Key 之外,EME 不會定義金鑰系統 (請參閱下文瞭解詳情)。
  • 內容解密模組 (CDM):用戶端軟體或硬體機制,可播放加密媒體。與 Key Systems 一樣,EME 不會定義任何 CDM,而是提供介面,讓應用程式與可用的 CDM 互動。
  • 授權 (金鑰) 伺服器:與 CDM 互動,提供用於解密媒體的金鑰。應用程式負責與授權伺服器進行協商。
  • 封裝服務:為發布/消費目的編碼及加密媒體。

請注意,使用 EME 的應用程式會與授權伺服器互動,以取得可啟用解密功能的金鑰,但使用者身分和驗證並非 EME 的一部分。系統會在驗證使用者身分 (選用) 後,擷取可啟用媒體播放的金鑰。Netflix 等服務必須在其網路應用程式中驗證使用者:當使用者登入應用程式時,應用程式會判斷使用者的身分和權限。

EME 的運作方式為何?

以下是 EME 元件互動的情形,對應於下方程式碼範例

如果有多種格式或編解碼可供選擇,可以使用 MediaSource.isTypeSupported()HTMLMediaElement.canPlayType() 來選取正確的格式。不過,CDM 可能只支援瀏覽器針對未加密內容支援的部分功能。建議您在選取格式和編解碼器前協商 MediaKeys 設定。如果應用程式等待加密事件,但 MediaKeys 顯示無法處理所選格式/編解碼器,則可能已太晚切換,否則會中斷播放。

建議的流程是先協商 MediaKeys,然後使用 MediaKeysSystemAccess.getConfiguration() 找出協商設定。

如果只有一個格式/編解碼可供選擇,就不需要使用 getConfiguration()。不過,建議您還是先設定 MediaKeys。等待加密事件的唯一原因,是如果無法得知內容是否已加密,但實際上這種情況不太可能發生。

  1. 網路應用程式嘗試播放含有一或多個加密串流的音訊或影片。
  2. 瀏覽器會辨識媒體是否已加密 (請參閱下方方塊瞭解如何辨識),並觸發加密事件,其中包含從媒體取得的中繼資料 (initData),說明該媒體是否已加密。
  3. 應用程式會處理已加密的事件:

    1. 如果沒有 MediaKeys 物件與媒體元素建立關聯,請先使用 navigator.requestMediaKeySystemAccess() 選取可用的金鑰系統,以便檢查可用的金鑰系統,然後透過 MediaKeySystemAccess 物件,為可用的金鑰系統建立 MediaKeys 物件。請注意,MediaKeys 物件的初始化作業應在第一次加密事件之前執行。應用程式會獨立於選取可用的金鑰系統,取得授權伺服器網址。MediaKeys 物件代表可用於解密音訊或影片元素媒體的所有金鑰。它代表 CDM 例項,並提供 CDM 存取權,特別是用於建立用來從授權伺服器取得金鑰的金鑰工作階段。

    2. 建立 MediaKeys 物件後,請將其指派給媒體元素:setMediaKeys() 會將 MediaKeys 物件與 HTMLMediaElement 建立關聯,以便在解碼期間使用其鍵。

  4. 應用程式會在 MediaKeys 上呼叫 createSession(),藉此建立 MediaKeySession。這會建立 MediaKeySession,代表授權和其金鑰的生命週期。

  5. 應用程式會在 MediaKeySession 上呼叫 generateRequest(),將在加密處理常式中取得的媒體資料傳遞至 CDM,藉此產生授權要求。

  6. CDM 會觸發訊息事件:要求從授權伺服器取得金鑰。

  7. MediaKeySession 物件會接收訊息事件,而應用程式會將訊息傳送至授權伺服器 (例如透過 XHR)。

  8. 應用程式會接收來自授權伺服器的回應,並使用 MediaKeySession 的 update() 方法,將資料傳遞至 CDM。

  9. CDM 會使用授權中的金鑰解密媒體。您可以使用有效的金鑰,從與媒體元素相關聯的 MediaKeys 中的任何工作階段。CDM 會存取以金鑰 ID 編入索引的金鑰和政策。

媒體播放作業會繼續。

瀏覽器如何得知媒體已加密?

這項資訊位於媒體容器檔案的中繼資料中,格式為 ISO BMFF 或 WebM 等。對於 ISO BMFF,這表示標頭中繼資料,稱為保護配置資訊方塊。WebM 使用 Matroska ContentEncryption 元素,並加入一些 WebM 專屬項目。在 EME 專屬登錄表中,每個容器都會提供相關規範。

請注意,CDM 和授權伺服器之間可能會有多個訊息,且這個程序中的所有通訊對瀏覽器和應用程式而言都是不透明的:只有 CDM 和授權伺服器可以瞭解訊息,但應用程式層級可以查看 CDM 傳送的訊息類型。授權要求包含 CDM 有效性 (和信任關係) 的證明,以及在產生的授權中加密內容金鑰時要使用的金鑰。

不過,CDM 實際上是做什麼的呢?

EME 實作本身並未提供解密媒體的方式:它只是提供 API,讓網路應用程式與內容解密模組互動。

EME 規格並未定義 CDM 實際執行的操作,CDM 可能會處理媒體的解碼 (解壓縮) 和解密作業。從最不完整到最完整,CDM 功能有幾種可能的選項:

  • 僅解密,啟用使用一般媒體管道的播放功能,例如透過 <video> 元素。
  • 解密和解碼,將影片影格傳遞至瀏覽器進行轉譯。
  • 解密和解碼,直接在硬體 (例如 GPU) 中轉譯。

您可以透過多種方式讓 CDM 可供網頁應用程式使用:

  • 將 CDM 與瀏覽器一起封裝。
  • 分別發布 CDM。
  • 在作業系統中建構 CDM。
  • 在韌體中加入 CDM。
  • 在硬體中嵌入 CDM。

EME 規格並未定義 CDM 的提供方式,但在所有情況下,瀏覽器都負責審查及公開 CDM。

EME 並未強制規定特定的金鑰系統;在目前的電腦和行動瀏覽器中,Chrome 支援 Widevine,IE11 則支援 PlayReady。

從授權伺服器取得金鑰

在一般商業用途中,內容會使用封裝服務或工具加密及編碼。加密媒體上線後,網路用戶端就能從授權伺服器取得金鑰 (包含在授權中),並使用金鑰啟用內容的解密和播放功能。

下列程式碼 (取自規格範例) 說明應用程式如何選取適當的金鑰系統,並從授權伺服器取得金鑰。

    var video = document.querySelector('video');

    var config = [{initDataTypes: ['webm'],
      videoCapabilities: [{contentType: 'video/webm; codecs="vp09.00.10.08"'}]}];

    if (!video.mediaKeys) {
      navigator.requestMediaKeySystemAccess('org.w3.clearkey',
          config).then(
        function(keySystemAccess) {
          var promise = keySystemAccess.createMediaKeys();
          promise.catch(
            console.error.bind(console, 'Unable to create MediaKeys')
          );
          promise.then(
            function(createdMediaKeys) {
              return video.setMediaKeys(createdMediaKeys);
            }
          ).catch(
            console.error.bind(console, 'Unable to set MediaKeys')
          );
          promise.then(
            function(createdMediaKeys) {
              var initData = new Uint8Array([...]);
              var keySession = createdMediaKeys.createSession();
              keySession.addEventListener('message', handleMessage,
                  false);
              return keySession.generateRequest('webm', initData);
            }
          ).catch(
            console.error.bind(console,
              'Unable to create or initialize key session')
          );
        }
      );
    }

    function handleMessage(event) {
      var keySession = event.target;
      var license = new Uint8Array([...]);
      keySession.update(license).catch(
        console.error.bind(console, 'update() failed')
      );
    }

常見的加密

有了通用加密解決方案,內容供應商就能針對每個容器/編解碼加密及封裝內容,並搭配各種金鑰系統、CDM 和用戶端使用,也就是任何支援通用加密的 CDM。舉例來說,使用 Playready 封裝的影片可透過瀏覽器播放,並使用 Widevine CDM 從 Widevine 授權伺服器取得金鑰。

這與舊版解決方案不同,後者只能搭配完整的垂直堆疊運作,包括單一用戶端 (通常也包含應用程式執行階段)。

通用加密 (CENC) 是一種 ISO 標準,定義了 ISO BMFF 的保護機制;WebM 也採用類似的概念。

清除金鑰

雖然 EME 並未定義 DRM 功能,但目前規格規定,所有支援 EME 的瀏覽器都必須實作 Clear Key。使用這個系統,您可以使用金鑰加密媒體,然後只要提供該金鑰即可播放。清除鍵可建構在瀏覽器中:不需要使用個別的解密模組。

雖然 Clear Key 不太可能用於多種商業內容,但在所有支援 EME 的瀏覽器中,它都能完全互通。這也非常適合測試 EME 實作項目,以及使用 EME 的應用程式,無須向授權伺服器要求內容金鑰。您可以在 simpl.info/ck 中找到簡單的 Clear Key 範例。以下是程式碼的逐步操作說明,與上方所述步驟類似,但不會與授權伺服器互動。

// Define a key: hardcoded in this example
// – this corresponds to the key used for encryption
var KEY = new Uint8Array([
  0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, 0x68, 0xef, 0x12, 0x2a, 0xfc,
  0xe4, 0xae, 0x3c,
]);

var config = [
  {
    initDataTypes: ['webm'],
    videoCapabilities: [
      {
        contentType: 'video/webm; codecs="vp8"',
      },
    ],
  },
];

var video = document.querySelector('video');
video.addEventListener('encrypted', handleEncrypted, false);

navigator
  .requestMediaKeySystemAccess('org.w3.clearkey', config)
  .then(function (keySystemAccess) {
    return keySystemAccess.createMediaKeys();
  })
  .then(function (createdMediaKeys) {
    return video.setMediaKeys(createdMediaKeys);
  })
  .catch(function (error) {
    console.error('Failed to set up MediaKeys', error);
  });

function handleEncrypted(event) {
  var session = video.mediaKeys.createSession();
  session.addEventListener('message', handleMessage, false);
  session
    .generateRequest(event.initDataType, event.initData)
    .catch(function (error) {
      console.error('Failed to generate a license request', error);
    });
}

function handleMessage(event) {
  // If you had a license server, you would make an asynchronous XMLHttpRequest
  // with event.message as the body.  The response from the server, as a
  // Uint8Array, would then be passed to session.update().
  // Instead, we will generate the license synchronously on the client, using
  // the hard-coded KEY at the top.
  var license = generateLicense(event.message);

  var session = event.target;
  session.update(license).catch(function (error) {
    console.error('Failed to update the session', error);
  });
}

// Convert Uint8Array into base64 using base64url alphabet, without padding.
function toBase64(u8arr) {
  return btoa(String.fromCharCode.apply(null, u8arr))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=*$/, '');
}

// This takes the place of a license server.
// kids is an array of base64-encoded key IDs
// keys is an array of base64-encoded keys
function generateLicense(message) {
  // Parse the clearkey license request.
  var request = JSON.parse(new TextDecoder().decode(message));
  // We only know one key, so there should only be one key ID.
  // A real license server could easily serve multiple keys.
  console.assert(request.kids.length === 1);

  var keyObj = {
    kty: 'oct',
    alg: 'A128KW',
    kid: request.kids[0],
    k: toBase64(KEY),
  };
  return new TextEncoder().encode(
    JSON.stringify({
      keys: [keyObj],
    }),
  );
}

如要測試這段程式碼,您需要播放加密的影片。您可以按照 webm_crypt 指示,為 WebM 加密影片,以便與 Clear Key 搭配使用。我們也提供商業服務 (至少適用於 ISO BMFF/MP4),並正在開發其他解決方案。

HTMLMediaElement 是簡單美麗的產物。

只要提供 src 網址,我們就能載入、解碼及播放媒體:

<video src="foo.webm"></video>

Media Source API 是 HTMLMediaElement 的擴充功能,可讓 JavaScript 建立從影片「區塊」播放的串流,進而更精細地控管媒體來源。進而啟用自適應串流和時間轉移等技術。

為何 MSE 對 EME 很重要?除了發布受保護的內容,商業內容供應商還必須能夠根據網路狀況和其他規定調整內容傳遞方式。舉例來說,Netflix 會根據網路狀況變化動態調整串流位元率。EME 可用於播放 MSE 實作提供的媒體串流,就像透過 src 屬性提供的媒體一樣。

如何分割及播放以不同位元率編碼的媒體?請參閱下方的 DASH 部分

您可以在 simpl.info/mse 中查看 MSE 的運作情形。在本例中,我們使用 File API 將 WebM 影片分割成五個片段。在實際應用程式中,系統會透過 AJAX 擷取影片片段。

首先建立 SourceBuffer:

var sourceBuffer = mediaSource.addSourceBuffer(
  'video/webm; codecs="vorbis,vp8"',
);

接著,使用 appendBuffer() 方法附加每個區塊,將整部電影「串流」至影片元素:

reader.onload = function (e) {
  sourceBuffer.appendBuffer(new Uint8Array(e.target.result));
  if (i === NUM_CHUNKS - 1) {
    mediaSource.endOfStream();
  } else {
    if (video.paused) {
      // start playing after first chunk is appended
      video.play();
    }
    readChunk_(++i);
  }
};

如要進一步瞭解 MSE,請參閱 MSE 入門指南

無論是多裝置、多平台還是行動裝置,無論如何,使用者經常是在變動連線條件下體驗網頁。動態自適應提交功能對於因應多裝置環境中的頻寬限制和變化情形至關重要。

DASH (又稱 MPEG-DASH) 旨在提供最佳的媒體內容傳遞服務,無論是串流還是下載,都能在網路不穩定的情況下正常運作。其他幾種技術也提供類似功能,例如 Apple 的 HTTP 即時串流 (HLS) 和 Microsoft 的 Smooth Streaming,但 DASH 是唯一採用開放標準,透過 HTTP 提供自動調整位元率串流的技術。YouTube 等網站已開始使用 DASH。

這與 EME 和 MSE 有何關聯?以 MSE 為基礎的 DASH 實作項目可剖析資訊清單,以適當的位元率下載影片片段,並在影片元素需要時,使用現有的 HTTP 基礎架構將這些片段提供給影片元素。

換句話說,DASH 可讓商業內容供應商針對受保護的內容進行自適應串流。

DASH 的功能與名稱相符:

  • 動態:可因應變動條件做出回應。
  • 自動調整:自動調整為提供適當的音訊或影片位元率。
  • 串流:可串流播放及下載。
  • HTTP:可透過 HTTP 的優點提供內容,而不會有傳統串流伺服器的缺點。

BBC 已開始使用 DASH 提供測試串流

媒體會以不同位元率進行多次編碼。每個編碼都稱為「表示法」。這些內容會分成多個媒體片段。用戶端會透過 HTTP 要求表示層級區段,依序播放節目。表示法可分組成包含等效內容的表示法適應組合。如果用戶端想要變更比特率,可以從目前的適應組合中挑選替代方案,並開始要求該表示法中的片段。內容會以這種方式編碼,讓用戶端輕鬆進行切換。除了多個媒體區段外,表示通常也包含初始化區段。這可視為標頭,其中包含編碼、影格大小等資訊。在使用該表示法的媒體片段之前,用戶端需要先取得該表示法的這項資訊。

摘要:

  1. 媒體會以不同的位元率編碼。
  2. 不同的位元率檔案會透過 HTTP 伺服器提供。
  3. 用戶端網頁應用程式會選擇要透過 DASH 擷取及播放的位元率。

在影片分割程序中,系統會以程式輔助方式建構 XML 資訊清單,也就是媒體呈現說明 (MPD)。這會說明適應組合和表示法,並提供持續時間和網址。MPD 如下所示:

    <MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" mediaPresentationDuration="PT0H3M1.63S" minBufferTime="PT1.5S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011"
    type="static">
      <Period duration="PT0H3M1.63S" start="PT0S">
        <AdaptationSet>
          <ContentComponent contentType="video" id="1" />
          <Representation bandwidth="4190760" codecs="avc1.640028" height="1080" id="1" mimeType="video/mp4" width="1920">
            <BaseURL>car-20120827-89.mp4</BaseURL>
            <SegmentBase indexRange="674-1149">
              <Initialization range="0-673" />
            </SegmentBase>
          </Representation>
          <Representation bandwidth="2073921" codecs="avc1.4d401f" height="720" id="2" mimeType="video/mp4" width="1280">
            <BaseURL>car-20120827-88.mp4</BaseURL>
            <SegmentBase indexRange="708-1183">
              <Initialization range="0-707" />
            </SegmentBase>
          </Representation>

                  </AdaptationSet>
      </Period>
    </MPD>

(這個 XML 取自 YouTube DASH 示範播放器使用的 .mpd 檔案)。

根據 DASH 規格,理論上 MPD 檔案可做為影片的 src 使用。不過,為了讓網頁開發人員享有更大的彈性,瀏覽器供應商選擇將 DASH 支援交由使用 MSE 的 JavaScript 程式庫 (例如 dash.js) 負責。在 JavaScript 中實作 DASH 可讓適應演算法進行調整,而無須更新瀏覽器。使用 MSE 還可嘗試使用其他資訊清單格式和提交機制,而無須變更瀏覽器。Google 的 Shaka Player 實作了支援 EME 的 DASH 用戶端。

Mozilla Developer Network 提供操作說明,說明如何使用 WebM 工具和 FFmpeg 區隔影片並建立 MPD。

結論

使用網路提供付費影片和音訊的情況正以驚人的速度成長。無論是平板電腦、遊戲主機、連網電視或機上盒,每部新裝置似乎都能透過 HTTP 串流播放主要內容供應商的媒體內容。目前超過 85% 的行動和電腦版瀏覽器都支援 <video> 和 <audio>,思科估計影片將佔全球消費者網際網路流量的 80% 至 90%。在這種情況下,由於瀏覽器供應商 縮減對大多數媒體外掛程式所依賴的 API 支援,瀏覽器支援的受保護內容發布功能可能會越來越重要。

延伸閱讀

規格和標準

EME 規格:最新的編輯者草稿通用加密 (CENC) 媒體來源擴充功能:最新的編輯者草稿DASH 標準 (是的,這是 PDF 檔案) DASH 標準簡介

文章

DTG 網路研討會 (部分已淘汰) 什麼是 EME?,作者:Henri Sivonen 媒體來源擴充功能入門指南 MPEG-DASH 測試串流:BBC 研發部門網誌文章

示範

Clear Key 示範:simpl.info/ck Media Source Extensions (MSE) 示範 Google 的 Shaka Player 實作支援 EME 的 DASH 用戶端

意見回饋