什么是 EME?

Encrypted Media Extensions 提供了一个 API,可让 Web 应用与内容保护系统进行交互,从而允许播放经过加密的音频和视频。

EME 旨在让用户能够在任何浏览器中使用相同的应用和加密文件,无论底层保护系统如何。前者由标准化 API 和流程实现,而后者则由通用加密概念实现。

EME 是 HTMLMediaElement 规范的扩展,因此得名。作为“扩展程序”,这意味着浏览器对 EME 的支持并非强制性要求:如果浏览器不支持加密媒体,则无法播放加密媒体,但实现 HTML 规范的兼容性并不要求必须支持 EME。EME 规范

此提案扩展了 HTMLMediaElement,提供了用于控制受保护内容播放的 API。

该 API 支持各种用例,从简单的明文密钥解密到高价值视频(前提是实现了适当的用户代理)。许可/密钥交换由应用控制,有助于开发支持一系列内容解密和保护技术的强大播放应用。

本规范未定义内容保护或数字版权管理系统。而是定义了一个通用 API,可用于发现、选择和与此类系统以及更简单的内容加密系统进行交互。无需实现数字版权管理即可符合此规范:只需将 Clear Key 系统作为常见基准来实现即可。

通用 API 支持一组简单的内容加密功能,将身份验证和授权等应用功能留给页面作者完成。这需要通过页面中介内容保护系统特定消息传递来实现,而不是假设加密系统和许可或其他服务器之间的带外通信。

EME 实现使用以下外部组件:

  • 密钥系统:内容保护 (DRM) 机制。除了 Clear Key 之外,EME 本身不定义密钥系统(详见下文)。
  • 内容解密模块 (CDM):支持播放加密媒体的客户端软件或硬件机制。与密钥系统一样,EME 不定义任何 CDM,而是为应用提供一个界面,供其与可用的 CDM 交互。
  • 许可 (Key) 服务器:与 CDM 交互,以提供用于解密媒体的密钥。与许可服务器的协商工作由应用负责。
  • 打包服务:对媒体进行编码和加密,以便分发/使用。

请注意,使用 EME 的应用会与许可服务器交互以获取密钥以启用解密,但用户身份和身份验证不属于 EME 的一部分。在(可选)对用户进行身份验证后,系统会检索用于启用媒体播放的密钥。Netflix 等服务必须在其 Web 应用中对用户进行身份验证:当用户登录应用时,应用会确定用户的身份和权限。

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,供 Web 应用与内容解密模块进行交互。

EME 规范未定义 CDM 的实际用途,CDM 可以处理媒体解码(解压缩)和解密。CDM 功能有几种可能的实现方式,从最不健全到最健全依次为:

  • 仅解密,使用常规媒体流水线(例如通过 <video> 元素)启用播放。
  • 解密和解码,将视频帧传递给浏览器进行渲染。
  • 解密和解码,直接在硬件(例如 GPU)中渲染。

您可以通过多种方式使 CDM 可供 Web 应用使用:

  • 将 CDM 与浏览器捆绑在一起。
  • 单独分发 CDM。
  • 将 CDM 构建到操作系统中。
  • 在固件中添加 CDM。
  • 在硬件中嵌入 CDM。

EME 规范未定义如何提供 CDM,但在所有情况下,浏览器都负责审核和公开 CDM。

EME 不强制要求使用特定的密钥系统;在当前的桌面版和移动版浏览器中,Chrome 支持 Widevine,IE11 支持 PlayReady。

从许可服务器获取密钥

在典型的商业用途中,内容会使用打包服务或工具进行加密和编码。加密媒体在线上发布后,Web 客户端可以从许可服务器获取密钥(包含在许可中),并使用该密钥解密内容并进行播放。

以下代码(改编自规范示例)展示了应用如何选择适当的密钥系统并从许可服务器获取密钥。

    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 许可服务器获取密钥的 Widevine CDM 在浏览器中播放。

这与旧版解决方案形成鲜明对比,后者只能与完整的垂直堆栈搭配使用,包括一个通常还包含应用运行时的单个客户端。

通用加密 (CENC) 是一种 ISO 标准,用于定义 ISO BMFF 的保护方案;类似的概念也适用于 WebM。

清除键

虽然 EME 未定义 DRM 功能,但规范目前规定,所有支持 EME 的浏览器都必须实现 Clear Key。借助此系统,您可以使用密钥加密媒体,然后只需提供该密钥即可播放媒体。Clear Key 可以内置到浏览器中,无需使用单独的解密模块。

虽然不太可能用于许多类型的商业内容,但 Clear Key 在所有支持 EME 的浏览器中都完全可互操作。它还非常适合测试 EME 实现和使用 EME 的应用,而无需从许可服务器请求内容密钥。您可以在 simpl.info/ck 中找到一个简单的清除键示例。下面将逐步介绍该代码,该代码与上文中介绍的步骤类似,但无需与许可服务器交互。

// 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 视频进行加密以便与 Clear Key 搭配使用,请按照 webm_crypt 说明操作。还提供商业服务(至少适用于 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 的实际效果;在本示例中,WebM 视频使用 File API 拆分为五个区块。在生产应用中,视频块可通过 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 Live Streaming (HLS) 和 Microsoft 的 Smooth Streaming,但 DASH 是唯一基于开放标准通过 HTTP 进行自适应码率流式传输的方法。YouTube 等网站已经在使用 DASH。

这与 EME 和 MSE 有什么关系?基于 MSE 的 DASH 实现可以使用现有的 HTTP 基础架构解析清单、以适当的码率下载视频片段,并在视频元素需要时将这些片段馈送给该元素。

换句话说,借助 DASH,商业内容提供商可以对受保护的内容进行自适应流式传输。

DASH 的功能与其名称相符:

  • 动态:根据不断变化的条件做出响应。
  • 自适应:可自行调整以提供适当的音频或视频比特率。
  • 流式:允许流式和下载。
  • HTTP:利用 HTTP 的优势实现内容传送,而无需传统流式传输服务器的缺点。

BBC 已开始使用 DASH 提供测试视频流

系统会以不同的比特率对媒体进行多次编码。每种编码都称为“表示法”。这些媒体内容会被拆分为多个媒体片段。客户端通过通过 HTTP 从表示法中按顺序请求片段来播放节目。表示法可以分组为包含等效内容的表示法适配集。如果客户端希望更改比特率,则可以从当前自适应集合中选择替代方案,并开始请求该表示中的片段。内容的编码方式使客户能够轻松进行切换。除了多个媒体片段之外,表示法通常还包含一个初始化片段。这可以被视为一个包含编码、帧大小等信息的标头。客户端需要先为给定表示法获取此标头,然后才能使用该表示法中的媒体片段。

总结:

  1. 媒体以不同的比特率进行编码。
  2. 您可以从 HTTP 服务器获取不同的比特率文件。
  3. 客户端 Web 应用选择要检索和使用 DASH 播放的码率。

在视频分割过程中,以编程方式构建称为“媒体呈现说明 (MPD)”的 XML 清单。它描述了适配集和表示法,以及时长和网址。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。不过,为了给 Web 开发者提供更大的灵活性,浏览器供应商已经选择让 DASH 支持仅支持使用 MSE 的 JavaScript 库(例如 dash.js)。在 JavaScript 中实现 DASH 可让自适应算法不断演变,而无需更新浏览器。通过使用 MSE,您还可以试验其他清单格式和传送机制,而无需更改浏览器。Google 的 Shaka Player 实现了支持 EEE 的 DASH 客户端。

Mozilla 开发者网络提供了相关说明,介绍了如何使用 WebM 工具和 FFmpeg 对视频进行分段并构建 MPD。

总结

通过网络提供付费视频和音频的做法正在以惊人的速度增长。似乎每种新设备(无论是平板电脑、游戏机、联网电视还是机顶盒)都能通过 HTTP 从主要内容提供商流式传输媒体。超过 85% 的移动浏览器和桌面浏览器现在支持 <video> 和 <audio>。Cisco 估计,到 2017 年,视频将占全球消费者互联网流量的 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 客户端

反馈