Microsoft PlayReady 域特性提供的这一功能对于最终用户体验而言很直观:服务提供商允许用户将一组计算机指定为一个域。 如果域中的任何计算机对于内容具有域绑定的许可证,则其中的任何计算机都可以使用域中任何其他计算机获取的、受 Microsoft PlayReady 保护的内容。 用户可以轻松地在域中添加或删除计算机,只要域中的计算机总数不超过此服务定义的限制即可。
下图显示了域管理在概念上如何适合总体 DRM 过程:
![显示 Silverlight DRM 如何进行概念化工作。 显示 Silverlight DRM 如何进行概念化工作。](http://i.msdn.microsoft.com/dynimg/IC409753.png)
PlayReady 域服务器(域控制器)是用于确定域所表示的内容(例如,用户、家庭或一组用户)且具有与该域关联的实体列表的服务器。 域服务器还实行用于定义域中可以容纳多少台计算机的策略。 自始至终,域控制、分发和授予许可等都可以在单独的服务器上或同一台服务器上完成,具体取决于性能需要。 为了清楚起见,上图将它们显示为单独的服务器。
使用域绑定许可证的许多媒体应用程序希望在第一次运行应用程序时将客户端加入用户的域。 这使得服务可以控制每个用户有权访问媒体内容的客户端数目。 您不能使用 Silverlight API 枚举这些域。 因此,您将希望让应用程序自行保留状态数据,以了解客户端是否已加入到域。
下面的简单示例演示如何使用 DomainAcquirer 类在第一次启动应用程序时实现加入域的请求。
Guid c_ServiceId = new Guid("{deb47f00-8a3b-416d-9b1e-5d55fd023044}"); Uri c_DomainServerUrl = new Uri("http://domainserver.contoso.com/rmsdk/rightsmanager.asmx"); Uri c_LicenseServerUrl = new Uri("http://licenseserver.contaso.com/rmsdk/rightsmanager.asmx"); public void acquirer_JoinDomainCompleted(object sender, DomainOperationCompletedEventArgs e) { if (e.Error != null) { // Standard error handling may include logging the error, // retrying, or reporting failure to the user // e.CustomData may have additional information depending on // the type of error is reported in e.Message } else if (e.Cancelled) { // Standard cancellation handling } else { // Update the ISO store that we are joined to the domain // The application may want to store e.AccountId } } public void JoinDomainOnFirstUse() { if (false == IsJoinedToDomain()) { string friendlyName = PromptUserForClientFriendlyName(); DomainAcquirer acquirer = new DomainAcquirer(); acquirer.JoinDomainCompleted +=new EventHandler<JoinDomainCompletedEventArgs>( acquirer_JoinDomainCompleted); acquirer.JoinDomainAsync(c_ServiceId, Guid.Empty, c_DomainServerUrl, friendlyName); } }
您可能希望通过允许用户转到帐户的属性页来提供注销客户端的功能。 例如,您可以为当前在域中的每台设备提供一个“注销此客户端”按钮,并提供一个“添加新客户端”按钮以将其他设备加到域中。 如果用户注销当前客户端,则执行“保留域”操作以便从客户端的持久性许可证存储区中删除域键。 通过这种方式,您可以控制可以播放受控制内容的客户端数,并且用户可以控制它们是哪些客户端。
但是,如果当用户从某个客户端(客户端 y)访问在线帐户属性页时,从域中删除了另一个客户端(客户端 x),将发生什么呢? 由于从域中删除客户端 x 时该客户端从 Internet 断开连接,但客户端 x 的永久性许可证存储区中的域许可证并没有获得删除通知,因此,仍然可以在客户端 x 上播放内容。
许多服务具有严格的业务规则来控制此类注销操作。 例如,它们可能限制可通过这种方式注销的设备数,要求调用客户服务来执行注销,或应用欺诈性检测逻辑。 因此,执行“保留域”操作是从域中删除客户端的首选方式。 下面示例中的逻辑说明可能实现“注销此客户端”按钮的方式。
public void RemoveClientFromAccountButton_OnClick(object sender, RoutedEventArgs e) { // // The service may want to prompt the user with a "Are you sure?" // prompt before removing the client as it will disable media // playback of any domain bound content. // // The service will want to look up the currently logged in user’s // account identifier from the ISO store. The accountId parameter // is strictly required, and it is the responsibility of the // service to ensure that is is available at this point. // Guid m_AccountId = accountId; DomainAcquirer acquirer = new myDomainAcquirer(); acquirer.LeaveDomainCompleted += new EventHandler<LeaveDomainCompletedEventArgs>( acquirer_LeaveDomainCompleted); acquirer.LeaveDomainAsync(c_ServiceId, m_AccountId, c_DomainServerUrl); } public void acquirer_LeaveDomainCompleted(object sender, DomainOperationCompletedEventArgs e) { if (e.Error != null) { // Error handling may include logging the error, retrying, or // reporting failure to the user e.CustomData may have // additional information depending on the type of error is // reported in e.Message } else if (e.Cancelled) { // Handle cancel } else { // Update the ISO store that we left the domain. Likely means // cleaning up the domain object for the given AccountId. } }
注意 LeaveDomainAsync 可能会失败(可能是客户端未连接到 Internet),但客户端仍将从域中删除。 在这种情况下,客户端认为它们不是域的一部分,但服务仍可能会认为客户端是域的一部分。 该服务必须提供一种方法来协调这种不匹配。
许可证获取代码(无论是通过 MediaElement 获取,还是使用 LicenseAcquirer 直接获取)理解从许可证服务器发送回的 RenewDomainException 和 DomainRequiredException 错误(请参见本文前面的错误处理)。 如果客户端收到以下错误之一,它将尝试使用 DomainAcquirer(无论是默认实例还是用户配置的实例),以使用在域错误消息中提供的参数来执行“加入域”操作。 下面示例中的逻辑说明要利用此功能几乎不需要在 API 级别执行操作:
public void RenewDomain() { LicenseAcquirer myLicenseAcquirer = new LicenseAcquirer(); myLicenseAcquirer.DomainAcquirer = new myDomainAcquirer(); mediaElement.LicenseAcquirer = myLicenseAcquirer; mediaElement.LicenseAcquirer.LicenseServerUriOverride = c_LicenseServerUrl; mediaElement.Source = new Uri("http://contoso.com/content.wmv"); // // If the license server decides that the client does not have a // domain membership, the domain membership is out of date, // then a DomainRequiredException or RenewDomainException respectively // will be thrown and the client will send back a response containing // the necessary information (url, serviceid, accountid, etc) to // send to the domain server. The license acquisition pipeline // would treat this somewhat like an Indiv message in that // it would do the join domain and then restart the license // acquisition. // // Note that a customized DomainAcquirer was provided so the // OnJoinDomain method will be called to allow // authentication information to be provided with the Join Domain // request. // mediaElement.Play(); }
许可证是保留资产(另一个许可证或一段内容)的解密密钥的数据文件。 它还包含 DRM 权限和限制,这些内容定义如何可以使用它赋予访问权限的资产。 许可证有三种不同类型:
-
简单许可证:从通过使用 PlayReady Server SDK 构建的应用程序中提供的 PlayReady 许可证,其中包含权限和限制以及用于关联内容段的密钥。
-
根许可证:控制一个或更多叶许可证的许可证。 叶许可证控制着特定内容段的播放,而根许可证控制所有内容段的播放。 例如,在订阅模型中,根许可证可能具有到期日期,而叶许可证则没有。 当根许可证到期时,叶许可证将不可用,直至获得新的根许可证。
-
叶许可证:依赖于根许可证的简单许可证。
当您具有受单一根许可证控制的叶许可证时,就认为这些许可证构成了许可证“链”。下面的关系图显示这概念上的外观:
![演示根和叶许可证。 演示根和叶许可证。](http://i.msdn.microsoft.com/dynimg/IC409754.png)
现在讨论用户具有订阅并下载了若干视频供以后脱机播放的情况。 对于每个视频,都有一个叶许可证指定允许使用该视频。 在此情况下,我们假设叶节点规定用户可以随时播放此视频。 但是,PlayReady 确保在允许播放之前具有有效的根许可证。 如果根许可证过期(或许因为用户允许其订阅过期),则叶许可证不允许播放。 这样,就可以使用根许可证来控制整个订阅(用户设备上的所有叶许可证)。
![]() |
---|
叶许可证可以具有多个根许可证。 在这种情况下,至少有一个根许可证有效,才能进行播放。 此外,叶许可证可以脱离根许可证而存在。 |
您可以通过迭代永久性许可证存储区中的所有许可证(MediaLicense 对象)来检查许可证或许可证链是否过期。 如果任何许可证需要更新,您可以添加逻辑以试图连接到授权服务器,进行必要的更新,并提示用户付款等等。
下面的示例演示如何检查是否需要续订订阅许可证。 请注意,用户需要在浏览器之外运行 Silverlight,许可证枚举的提升的信任才有可能。
private void CheckSubscriptionRootForRenewal(Guid parentKeyId, Uri licenseServerUrl) { DateTimeOffset renewalDate = DateTimeOffset.Now.AddDays(5); // Query the licensemanager to see if we have a usable root license IEnumerable<MediaLicense> myLicenses = LicenseManagement.SelectLicenses(parentKeyId); bool renewRoot = true; foreach (MediaLicense ML in myLicenses) { // If the license expires within the next 5 days, // renew the subscribtion by requesting a new root license. if ((ML.Usable) && (ML.ExpirationDate > renewalDate)) { renewRoot = false; break; } } if (renewRoot) { LicenseAcquirer acquirer = new LicenseAcquirer(); acquirer.LicenseServerUriOverride = licenseServerUrl; acquirer.AcquireLicenseCompleted += new EventHandler<AcquireLicenseCompletedEventArgs>(acquirer_Completed); acquirer.AcquireLicenseAsync(parentKeyId, ContentKeyType.Aes128Bit, Guid.Empty); }
在此示例中,您将迭代永久性许可证存储区中的所有 MediaLicense 实例。 MediaLicense 实例可以是以下任意一项:
许可证链(叶加根)
一个简单许可证
单个叶许可证(根许可证不存在)
单个根许可证(直接查询根)
下面的函数可用于筛选可能播放选项列表中向用户显示的内容列表,尤其是当用户处于脱机状态而无法获取新许可证时。 它还可用于决定是否在进入脱机状态之前获得许可证。 对于订阅客户,如果用户处于联机状态,则应首先调用 CheckSubscriptionRootForRenewal 函数(在前面的示例中)。
public bool IsUsableLicenseAvailableForContent(System.IO.Stream contentFile) { bool returnValue = false; // SelectLicenses works only if the user is running the Silverlight application offline // *and* elevated trust. IEnumerable<MediaLicense> myLicenses = LicenseManagement.SelectLicenses(contentFile); foreach (MediaLicense ML in myLicenses) { if (ML.Usable) { returnValue = true; break; } } return returnValue; }
输出保护包含五个主要部分:许可证策略数据、策略管理器、安全兼容的驱动程序、输出端口的加密通道和一种兼容输出设备。
策略数据
策略数据是一个许可证内的数据,如 PlayReady DRM 许可证。 许可证可包含策略,该策略显示声明:内容需要特定的程度和使用一系列 Guid 的输出保护格式。 许可证也可包含显示所需保护级别的每次输出中具有单个号码的策略,反过来,其必须映射到不同的形式和输出保护程度。
下表显示可使用基于输出保护机制的隐式保护级别请求的策略示例。
策略 | 描述 |
---|---|
无限制 | 指示内容可能是可以随时随地流动的较低的相对值。 |
尽最大努力 | 指示内容具有更高的价值和应尝试输出保护,但如果其不能进行,则仍允许该内容流动。 |
受限的 | 指示该内容是非常高的价值和不应允许其在不安全信道流上流动。 |
不允许 | 指示不应允许内容流过指定的通道,而无论安全还是不安全。 |
策略管理器
策略管理器将从单个许可证中获取数据并与一个兼容的驱动程序进行通信,以发现驱动程序、视频卡、音频卡、显示器和显示器或扬声器和计算机之间的所有设备的功能。 根据许可证中请求的输出保护级别和硬件支持的内容,策略管理器将决定是否应传输内容。 如果内容不允许进行传输,则返回一个错误。 如果内容允许流动,则策略管理器可确定如何和在什么输出上传输内容。
当 Silverlight 用于播放时,它是策略管理器。
作为策略管理器,Silverlight 从不与声卡驱动程序或扬声器进行通信来确定其通信能力。
安全和兼容的驱动程序
安全合规视频或音频驱动程序是一个符合内容保护栏(例如,Windows 徽标计划的一部分)和实现正确驱动程序接口的程序。 在 Silverlight 输出保护的情况下,所需的驱动程序界面是 COPP 或 OPM。
模拟及数字输出保护协议
认证输出保护协议 (COPP) 是一个驱动程序接口,可使应用程序查询图形卡上和连接至其的驱动器上的保护级别。
模拟及数字输出保护协议使用于 Windows Server 2003 SP1、Windows XP SP2 及更新的操作系统。
OPM
输出保护管理器 (OPM) 和 COPP 相似。 OPM 还是一种驱动程序接口,允许应用程序为其支持的各种输出保护机制级别查询图形卡和连接至其的设备。 OPM 包括 COOP 中不支持的功能,例如,在“克隆”模式中运行多监控器安装程序和将重复器置于计算机和呈现设备之间的功能。
OPM 适用于 Windows Vista、Windows Server 2008(需要 Media Desktop Experience)和更新的操作系统。
安全且加密的通道。
有很多用于通信加密视频帧和音频示例(视频卡或声卡到担保输出)的音频采样。 有些技术是模拟副本保护 (ACP)、内容生成管理系统模拟 (CGMS-A)、高带宽数字内容保护 (HDCP) 和显示端口内容保护协议 (DPCP)(只有 HDCP 和 DPCP 可在其中传输音频)。
以前在许多输出端口上的协议工作,如组件、复合、S 视频、VGA、HDMI、DVI 和显示端口。
如果图形卡和显示间的路径中存在多个设备,则可能有其它设备出现。 每个设备必须支持正在被使用的输出保护形式。 例如,计算机可连接至接收器,接收机可连接至电视,或者计算机可连接至 KVM 交换机。
各种输出保护技术仅适用于某些端口。
下表显示了可用于特定端口类型的输出保护的形式。
端口类型 | 可用输出保护的形式 |
---|---|
DVI-A、MicroDVI、MiniDVI [通过适配器的 VGA](模拟) |
|
DVI-D、MicroDVI、MiniDVI(数字) | HDCP |
DVI-I、MicroDVI、MiniDVI(数字/模拟) | HDCP 如果配置为数字 |
D-SUB [VGA](模拟) |
|
HDMI (数字) | HDCP |
复合(模拟) | CGMS-A、ACP |
组件(模拟) | CGMS-A、ACP |
S-视频(模拟) | CGMS-A、ACP |
DisplayPort/MiniDiaplyPort(数字) | HDCP,DPCP |
TOSLINK - S/PDIF 音频 | 单片机 |
HDMI - 音频 | HDCP |
USB - 音频 |
|
蓝牙 - 音频 |
|
模拟插孔(例如 3.5mm 电缆)-音频 |
|
同轴 - S/PDIF 音频 | 单片机 |
DisplayPort - 音频 |
|
内部视频输出 |
|
大多数图形卡今天没有直接的复合输出和组件输出。 若要获取复合和组件的输出,通常可以使用适配器从 S-视频端口。