如何生成 PlayReady 标头

打包程序需要在加密内容中包含 PlayReady 标头。

有关 PlayReady 标头和 PlayReady 对象的详细说明,请参阅 PlayReady 标头规范

PlayReady 标头包含有关正在播放的内容的信息,包括密钥标识符 (KID) 标识用于加密数据的密钥、PlayReady 许可证服务器的默认许可证获取 URL 以及要包括的任何自定义数据。 用于加密内容的密钥和 KID 必须与 PlayReady 许可证服务器共享,该服务器将颁发该特定内容的许可证,通常通过密钥管理系统 (KMS) 。

注意

Microsoft 不提供带 PlayReady 的密钥管理系统。

以下 XML 代码提供了 PlayReady 标头的示例,该标头可能插入到分段 MP4 文件的标头中,通常适用于按需内容。 它包括 KID 列表 (客户端解密内容所需的内容加密密钥的 ID) 。 这是为按需文件或流发出这些 KID 信号的最常用方法。

<WRMHEADER xmlns="http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader" version="4.3.0.0">
  <DATA>
    <PROTECTINFO>
      <KIDS>
        <KID ALGID="AESCTR" VALUE="PV1LM/VEVk+kEOB8qqcWDg=="></KID>
        <KID ALGID="AESCTR" VALUE="tuhDoKUN7EyxDPtMRNmhyA=="></KID>
      </KIDS>
    </PROTECTINFO>
    <LA_URL>http://rm.contoso.com/rightsmanager.asmx</LA_URL>
    <DS_ID>AH+03juKbUGbHl1V/QIwRA==</DS_ID>
  </DATA>
</WRMHEADER>

以下 XML 代码提供了 Live Linear 内容的 PlayReady 标头示例。 它不包括任何 KID,因为内容加密密钥 (及其关联的 KID) 偶尔会更改 (,例如,非常频繁,或在程序边界,或每小时,或每天) 。 用于内容流的 KID 将在段标头中发出信号,并且不需要在流顶级 PlayReady 标头中包含其中任何一个。 属性 DECRYPTORSETUP 设置为 ONDEMAND,这意味着将按需设置 PlayReady 标头和解密器,这意味着当客户端实际需要开始解密段时,此时,客户端将有权访问段标头中的另一个 PlayReady 标头,以确定涉及哪些 KID。

注意

DECRYPTORSETUP = ONDEMAND 并不意味着内容是按需提供的,它实际上是相反的。

<WRMHEADER version="4.2.0.0" xmlns="http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader">
  <DATA>
    <DECRYPTORSETUP>ONDEMAND</DECRYPTORSETUP>
  </DATA>
</WRMHEADER>

在打包程序中创建 PlayReady 标头生成器有多种方法。 以下部分一般介绍了如何生成 PlayReady 标头。

方法 1 - 基于 PlayReady 标头规范生成自己的代码

PlayReady 对象和标头规范是公开的,因此,在生成 PlayReady 对象和 PlayReady 标头的设备或服务中编写代码可以打包内容,并且非常简单。

PlayReady 标头生成器需要具有输入参数,例如:

  • 按需内容或 Live Linear 内容。
  • 用于保护整个资产的 KID 或 KID 列表。
  • (AESCTR 或 AESCBC) 使用的加密类型。
  • Defaut LA URL - PlayReady 许可证服务器的默认 URL,将在打包时发出许可证。
  • 如果服务使用的是域,则默认域服务标识符。

现在可以创建 PlayReady 对象及其关联的 PlayReady 标头。 下面的代码示例演示如何创建包含用于按需内容的 PlayReady 标头的 PlayReady 对象。

// This function gets values from the key management system and your specified encryption mode
// (and optionally the domainID), creates a PlayReady Header, adds the header to a 
// PlayReady Object, and stores them in a file in UTF-16 little endian format

void buildRMObject(string KeyID1, string KeyID2, string encryptMode, string domainID)
{
	// The string parameters include the following:
	// KeyID1, KeyID2 - The key identifiers - these values are returned from the key management system or
	//                                        the KeySeed mechanism
	// encryptMode - the encryption mode used to encrypt content, can be AESCTR or AESCBC
	// domainID - the optional domain service identifier (only used for domains)

	string xmlString = "<WRMHEADER xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" version=\"4.3.0.0\"><DATA><PROTECTINFO><KIDS><KID ALGID=\"";
	xmlString.append(encryptMode);
	xmlString.append("\" VALUE=\"");
	xmlString.append(KeyID1);
	xmlString.append("\"></KID><KID ALGID=\"");
	xmlString.append(encryptMode);
	xmlString.append("\" VALUE=\"");
	xmlString.append(KeyID2);
	xmlString.append("\"></KID></KIDS></PROTECTINFO><LA_URL>http://rm.contoso.com/rightsmanager.asmx</LA_URL><DS_ID>");
	xmlString.append(domainID);
	xmlString.append("</DS_ID></DATA></WRMHEADER>");

	// Convert the PlayReady header to UFT-16 format
	wstring_convert<codecvt_utf8_utf16<wchar_t>> convert;
	wstring utf16XML = convert.from_bytes(xmlString);

	// Calculate the length of the PlayReady Header
	int32_t headerLength = (utf16XML.size() * sizeof(wchar_t));
	// Calculate the length of the PlayReady Object
	int32_t objectLength = headerLength + 10;
	// Set the number of PlayReady object records (in this case, 1)
	int16_t recordCount = 1;
	// Set the record type (in this case, a PlayReady Header)
	// If this was an embedded license store, this value would be 3
	int16_t recordType = 1;

	// Write the PlayReady Object and PlayReady Header to a file
	wofstream wofs("C:\\Temp\\PRObject.txt", ios::binary);
	wofs.imbue(locale(wofs.getloc(),
			  new codecvt_utf16<wchar_t, 0x10ffff, little_endian>));
	wofs.write(reinterpret_cast<const wchar_t *>(&objectLength), 2);
	wofs.write(reinterpret_cast<const wchar_t *>(&recordCount), 1);
	wofs.write(reinterpret_cast<const wchar_t *>(&recordType), 1);
	wofs.write(reinterpret_cast<const wchar_t *>(&headerLength), 1);
	wofs <<  utf16XML;
}

方法 2 - 使用 PlayReady 服务器 API

如果你是 PlayReady Server SDK 许可者,则服务器 SDK 包含可用于构造 PlayReady Header 的 PlayReadyHeader 类。 它包括可以使用的方法和属性,你可以填写 PlayReady 标头所需的信息。

PlayReadyHeader 类详细介绍了使用 PlayReady Server SDK 许可证收到的 PlayReady 文档。 PlayReady Server SDK 还包括一个示例打包程序 (AESPackaging) ,演示如何创建 PlayReady 标头。

与方法 1 中一样,需要密钥管理系统生成的 KeyID () 、加密类型 (AESCTR 或 AESCBC) 、PlayReady 许可证服务器的 URL,以及域服务标识符(可选)。

方法 3 - 使用Windows应用程序

开始之前需要的东西。

  1. 必须具有密钥管理系统生成的 KeyID () 。
  2. 必须知道加密类型 (AESCTR 或 AESCBC) 。
  3. 使用 Windows 10 PlayReadyContentHeader 类在 PlayReady 对象中创建 PlayReady 标头

与前面的方法一样,需要密钥管理系统生成的 KeyID () 、加密类型 (AESCTR 或 AESCBC) 、PlayReady 许可证服务器的 URL 以及域服务标识符(可选)。