PlayReady 加密媒体扩展

本部分介绍如何修改 PlayReady Web 应用,以支持从以前的 Windows 8.1 版本到 Windows 10 版本的更改。

使用 Internet Explorer 中的 PlayReady 媒体元素,开发人员可以在强制实施内容提供程序定义的访问规则的同时,创建能够向用户提供 PlayReady 内容的 Web 应用。 本部分介绍如何仅使用 HTML5 和 JavaScript 将 PlayReady 媒体元素添加到现有 Web 应用。

PlayReady 加密媒体扩展中的新增功能

本部分提供对 PlayReady 加密媒体扩展(EME)所做的更改列表,用于在 Windows 10 上启用 PlayReady 内容保护。

以下列表描述了对适用于 Windows 10 的 PlayReady 加密媒体扩展所做的新功能和更改:

  • 添加了硬件数字权限管理(DRM)。

    基于硬件的内容保护支持允许在多个设备平台上安全播放高清(HD)和超高清(UHD)内容。 利用硬件安全保护密钥材料(包括私钥、内容密钥和任何其他用于派生或解锁上述密钥的密钥材料)以及解密的压缩和未压缩视频示例。

  • 提供对非永久性许可证的主动获取。

  • 提供在一条消息中获取多个许可证。

    可以在 Windows 8.1 中使用具有多个密钥标识符(KeyID)的 PlayReady 对象,或者将内容解密模型数据(CDMData)与多个 KeyID 配合使用

    注意

    在 Windows 10 中,CDMData 中的 KeyID> 下<支持多个密钥标识符。

  • 添加了实时过期支持或有限的持续时间许可证(LDL)。

    提供在许可证上设置实时过期的功能。

  • 添加了 HDCP 类型 1(版本 2.2)策略支持。

  • Miracast 现在隐式作为输出。

  • 添加了安全停止。

    安全停止为 PlayReady 设备提供了一种自信地向媒体流式处理服务断言的方法,即媒体播放已针对任何给定内容停止。

  • 添加了音频和视频许可证分离。

    单独的曲目阻止视频解码为音频;启用更可靠的内容保护。 新兴标准要求为音频和视频曲目提供单独的密钥。

  • 添加了 MaxResDecode。

    添加了此功能,以将内容播放限制为最大分辨率,即使拥有功能更强大的密钥(但不是许可证)。 它支持使用单个键对多个流大小进行编码的情况。

PlayReady 中的加密媒体扩展支持

本部分介绍 PlayReady 支持的 W3C 加密媒体扩展的版本。

playReady for Web 应用 目前绑定到 2013 年 5 月 10 日 W3C 加密媒体扩展 (EME) 草稿。 将来版本的 Windows 中,此支持将更改为更新的 EME 规范。

使用硬件 DRM

本部分介绍 Web 应用如何使用 PlayReady 硬件 DRM,以及如何在受保护的内容不支持硬件 DRM 的情况下禁用硬件 DRM。

若要使用 PlayReady 硬件 DRM,JavaScript Web 应用应使用 isTypeSupported EME 方法,其关键系统标识符用于 com.microsoft.playready.hardware 从浏览器查询 PlayReady 硬件 DRM 支持。

有时,硬件 DRM 不支持某些内容。 硬件 DRM 中从未支持鸡尾酒内容;如果要播放鸡尾酒内容,则必须选择退出硬件 DRM。 某些硬件 DRM 将支持 HEVC,有些硬件不支持;如果想要播放 HEVC 内容和硬件 DRM 不支持它,则还需要选择退出。

注意

若要确定 HEVC 内容是否受支持,请在实例化 com.microsoft.playready后使用 PlayReadyStatics.CheckSupportedHardware 方法。

向 Web 应用添加安全停止

本部分介绍如何向 Web 应用添加安全停止。

安全停止为 PlayReady 设备提供了一种自信地向媒体流式处理服务断言的方法,即媒体播放已针对任何给定内容停止。 此功能可确保媒体流式处理服务针对给定帐户在不同设备上提供准确的使用限制和报告。

发送安全停止质询有两个主要方案:

  • 当媒体演示文稿因内容结束而停止或用户停止中间某个位置的媒体演示文稿时。
  • 当上一个会话意外结束(例如,由于系统或应用崩溃)。 应用需要在启动时或关闭时查询任何未完成的安全停止会话,并将质询与任何其他媒体播放分开发送。

以下过程介绍如何为各种方案设置安全停止。

若要为演示文稿的正常结束设置安全停止,请执行:

  1. 播放开始之前注册 onEnded 事件。
  2. onEnded 事件处理程序需要从视频/音频元素对象调用removeAttribute("src"),将源设置为 NULL,这会触发媒体基础拆解拓扑、销毁解密器(s),并设置停止状态。
  3. 可以在处理程序中启动安全停止 CDM 会话,以向服务器发送安全停止质询,以通知播放目前已停止,但稍后也可以执行此操作。

若要设置安全停止(如果用户离开页面或关闭选项卡或浏览器):

  • 无需应用操作即可记录停止状态;系统会为你记录它。

若要为自定义页面控件或用户操作设置安全停止(例如自定义导航按钮或在当前演示文稿完成之前启动新演示文稿):

  • 发生自定义用户操作时,应用需要将源设置为 NULL ,这会触发媒体基础来拆毁拓扑、销毁解密器(s),并设置停止状态。

以下示例演示如何在 Web 应用中使用安全停止:

// JavaScript source code

var g_prkey = null;
var g_keySession = null;
var g_fUseSpecificSecureStopSessionID = false;
var g_encodedMeteringCert = 'Base64 encoded of your metering cert (aka publisher cert)';

// Note: g_encodedLASessionId is the CDM session ID of the proactive or reactive license acquisition 
//       that we want to initiate the secure stop process.
var g_encodedLASessionId = null;

function main()
{
    ...

    g_prkey = new MSMediaKeys("com.microsoft.playready");

    ...

    // add 'onended' event handler to the video element
    // Assume 'myvideo' is the ID of the video element
    var videoElement = document.getElementById("myvideo");
    videoElement.onended = function (e) { 

        //
        // Calling removeAttribute("src") will set the source to null
        // which will trigger the MF to tear down the topology, destroy the
        // decryptor(s) and set the stop state.  This is required in order
        // to set the stop state.
        //
        videoElement.removeAttribute("src");
        videoElement.load();

        onEndOfStream();
    };
}

function onEndOfStream()
{
    ...

    createSecureStopCDMSession();

    ...    
}

function createSecureStopCDMSession()
{
    try{    
        var targetMediaCodec = "video/mp4";
        var customData = "my custom data";

        var encodedSessionId = g_encodedLASessionId;
        if( !g_fUseSpecificSecureStopSessionID )
        {
            // Use "*" (wildcard) as the session ID to include all secure stop sessions
            // TODO: base64 encode "*" and place encoded result to encodedSessionId
        }

        var int8ArrayCDMdata = formatSecureStopCDMData( encodedSessionId, customData,  g_encodedMeteringCert );
        var emptyArrayofInitData = new Uint8Array();

        g_keySession = g_prkey.createSession(targetMediaCodec, emptyArrayofInitData, int8ArrayCDMdata);

        addPlayreadyKeyEventHandler();

    } catch( e )
    {
        // TODO: Handle exception
    }
}

function addPlayreadyKeyEventHandler()
{
    // add 'keymessage' eventhandler   
    g_keySession.addEventListener('mskeymessage', function (event) {

        // TODO: Get the keyMessage from event.message.buffer which contains the secure stop challenge
        //       The keyMessage format for the secure stop is similar to LA as below:
        //
        //            <PlayReadyKeyMessage type="SecureStop" >
        //              <SecureStop version="1.0" >
        //                <Challenge encoding="base64encoded">
        //                    secure stop challenge
        //                </Challenge>
        //                <HttpHeaders>
        //                    <HttpHeader>
        //                      <name>Content-Type</name>
        //                         <value>"content type data"</value>
        //                    </HttpHeader>
        //                    <HttpHeader>
        //                         <name>SOAPAction</name>
        //                         <value>soap action</value>
        //                     </HttpHeader>
        //                    ....
        //                </HttpHeaders>
        //              </SecureStop>
        //            </PlayReadyKeyMessage>
                
        // TODO: send the secure stop challenge to a server that handles the secure stop challenge

        // TODO: Receive and response and call event.target.Update() to process the response
    });
    
    // add 'keyerror' eventhandler
    g_keySession.addEventListener('mskeyerror', function (event) {
        var session = event.target;
        
        ...

        session.close();
    });
    
    // add 'keyadded' eventhandler
    g_keySession.addEventListener('mskeyadded', function (event) {
        
        var session = event.target;

        ...

        session.close();             
    });
}

/**
* desc@ formatSecureStopCDMData
*   generate playready CDMData
*   CDMData is in xml format:
*   <PlayReadyCDMData type="SecureStop">
*     <SecureStop version="1.0">
*       <SessionID>B64 encoded session ID</SessionID>
*       <CustomData>B64 encoded custom data</CustomData>
*       <ServerCert>B64 encoded server cert</ServerCert>
*     </SecureCert>
* </PlayReadyCDMData>        
*/
function formatSecureStopCDMData(encodedSessionId, customData, encodedPublisherCert) 
{
    var encodedCustomData = null;

    // TODO: base64 encode the custom data and place the encoded result to encodedCustomData

    var CDMDataStr = "<PlayReadyCDMData type=\"SecureStop\">" +
                     "<SecureStop version=\"1.0\" >" +
                     "<SessionID>" + encodedSessionId + "</SessionID>" +
                     "<CustomData>" + encodedCustomData + "</CustomData>" +
                     "<ServerCert>" + encodedPublisherCert + "</ServerCert>" +
                     "</SecureStop></PlayReadyCDMData>";
    
    var int8ArrayCDMdata = null

    // TODO: Convert CDMDataStr to Uint8 byte array and palce the converted result to int8ArrayCDMdata

    return int8ArrayCDMdata;
}

注意

以上示例中安全停止数据的 <SessionID>B64 encoded session ID</SessionID> 可以是星号 (*),它是所有记录的安全停止会话的通配符。 即,SessionID 标记既可以是特定会话,也可以是选择所有安全停止会话的通配符 (*)。

加密媒体扩展的编程注意事项

本部分列出了在为 Windows 10 创建启用了 PlayReady 的 Web 应用时应考虑的编程注意事项。

应用创建的 MSMediaKeysMSMediaKeySession 对象必须保持活动状态,直到应用关闭。 确保这些对象保持活动状态的一种方法是将它们指定为全局变量(如果声明为函数内的局部变量,变量将脱离范围并受到垃圾回收的约束)。 例如,以下示例将变量 g_msMediaKeys 和 g_mediaKeySession 分配为全局变量,然后将这些变量分配到函数中的 MSMediaKeys 和 MSMediaKeySession 对象

var g_msMediaKeys;
var g_mediaKeySession;

function foo() {
  ...
  g_msMediaKeys = new MSMediaKeys("com.microsoft.playready");
  ...
  g_mediaKeySession = g_msMediaKeys.createSession("video/mp4", intiData, null);
  g_mediaKeySession.addEventListener(this.KEYMESSAGE_EVENT, function (e) 
  {
    ...
    downloadPlayReadyKey(url, keyMessage, function (data) 
    {
      g_mediaKeySession.update(data);
    });
  });
  g_mediaKeySession.addEventListener(this.KEYADDED_EVENT, function () 
  {
    ...
    g_mediaKeySession.close();
    g_mediaKeySession = null;
  });
}

有关详细信息,请参阅 示例应用程序

另请参阅