Digital Rights Management (DRM) for Windows Phone 8
[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]
When you integrate Digital Rights Management (DRM) into your Windows Phone apps, you can better protect and more securely deliver content cross-platform for a variety of scenarios, including streaming, progressive download, rentals, and subscriptions.
This topic contains the following sections.
- 1. DRM scenarios
- 2 Windows Phone DRM online scenarios
- 2.1 Windows Phone DRM online conceptual overview
- 2.2 PlayReady vs. WMDRM
- 2.3 Limitations when using DRM
- 2.4 Considerations for using PlayReady encryption vs. WMDRM encryption
- 2.5 Integrating DRM in Windows Phone
- 2.6 Set up server infrastructure
- 2.7 Point to DRM content from a Windows Phone app
- 2.8 Detecting the DRM state
- 2.9 Error handling
- 2.10 Add custom logic
- 3 Windows Phone DRM offline scenarios
- 3.1 Basic offline DRM (one-time purchase)
- 3.2 Validate license before download
- 3.3 Rentals and subscriptions
- 3.4 Root licenses, leaf licenses, and license chains
- 3.5 Enumerating through licenses
- Related Topics
Warning
In Windows Phone 8 the graphics stack will prohibit app processes from getting a texture pointer to a DRM protected video surface. The only way to show a frame is by passing it to the compositor for drawing as an overlay surface. In Windows Phone OS 7.1 and earlier versions, DRM protected video is accessible as a texture for composition. The following problems can occur when porting Windows Phone OS 7.1 and earlier version apps to Windows Phone 8. Non cardinal rotation (degrees other than 0, 90, 180, 270) Partial transparency 3D projections (object in motion containing video) In each of the cases listed, the video cannot be rendered via the overlay. The video is composed as any other texture content in the scene. In Windows Phone 8 an app cannot properly consume DRM video as a texture; the video defaults to a black display. The standard video overlay controls are not affected. The compositor blends those frames correctly.
1. DRM scenarios
You can use DRM to help you deliver audio and video content that is more secure and better protected from unauthorized capture and redistribution. This protection can be integrated into a variety of business scenarios that include:
Online Scenarios: These scenarios require users to be online while they play back the media content:
Live Streaming: Live streaming (also known as "true streaming") sends content directly to the device without saving the file to a hard disk. A live stream is only available while it is being broadcast. Internet television and radio are examples of live streaming.
Progressive Download: Progressive download lets users play back the media while it is downloading. The main difference between progressive download and live streaming—from a user's point of view—is that progressively downloaded content is stored on the user's device, at least temporarily.
Offline Scenarios: These scenarios allow users to be offline while they play the content. They do require the offline Windows Phone runtime to be installed on a user's device, and they do require the user to be online—at least intermittently—in order to initially download the content and renew subscriptions.
Download File Offline (onetime purchase): The user downloads the content from the Internet and later plays it by using an offline player. For example, an online video store charges customers to download a video file, which users can play back in an offline player whenever they want. The DRM software can restrict redistribution of the video file to one or more devices.
Rental: You can specify time limits in your DRM licenses in order to limit playback of content. For example, an online video store might offer their videos for rent. Once the rental is purchased and the license downloaded, the license will expire 30 days after it is issued or 24 hours after content is first played, whichever occurs first.
Subscription: You can enable customers to playback content based on a subscription model. For example, customers of the online video store pay a monthly fee to watch up to 100 hours of television content online and download up to 20 episodes. In order to renew their subscription, they need to pay the monthly fee and connect to the service at least once a month because the subscription license expires every 45 days.
Warning
If the phone that is attempting to play the content already has a license, it might not be necessary to go through the license acquisition process again; rather, the existing license could be used to allow playback.
2 Windows Phone DRM online scenarios
The following sections explain how to enable online DRM scenarios. Many of the concepts introduced here also apply to offline scenarios.
2.1 Windows Phone DRM online conceptual overview
Note
If the phone that is attempting to play the content already has a license, it might not be necessary to go through the license acquisition process again; rather, the existing license could be used to allow playback.
1. Windows Phone client accesses content
The user attempts to play some DRM-protected content in a Windows Phone app that is stored on the distribution server (a distribution server, usually a Web server, is used to distribute your content). The client app downloads the content (or some of the content, in the case of streaming) and the header.
2. Is the user’s phone individualized?
Before requesting the license to decrypt the content, Windows Phone must first determine whether the user’s device has the appropriate DRM software installed. The client component of DRM is required before any protected content can be played. The individualization component software enables the client device to request and use a DRM license. It also helps protect sensitive data that is used in the decryption process.
If the appropriate individualized component software is not already on the client, the client automatically requests the component from the Microsoft Individualization Service. The process of obtaining the individualized component software is called individualization. Windows Phone individualizes the user's phone by sending information to the Microsoft Individualization Service. The user can block this information from being sent. For more information, see the "Error handling" section later in this topic.
After a valid individualization component is installed, the client usually does not need to individualize again. An exception to this is if the PlayReady component is updated on the phone.
You can detect when individualization is happening for the first time on a device by using the MediaElement.CurrentState property. For more information, see the "Detecting the DRM state" section later in this topic.
3. Windows Phone requests license
When valid individualization component software exists on the client, it is ready to play DRM. When the user initiates playback of the protected content for the first time, the Windows Phone client contacts the PlayReady License Server to obtain a license (the License Server is controlled by you or your service provider). If the License Server approves the request, it issues a license that is used by the client to decrypt the particular media file. The content can then be played.
4. Phone needs to join domain? (optional)
Managing devices on which a user can play back DRM-protected content is called "Domain Management." Managing domains is optional, but is mentioned here because it can be useful in both online and offline scenarios. The functionality that is provided by the Microsoft PlayReady Domain feature is intuitive from an end-user experience: the service provider allows the user to designate a group of computers as a domain.
If a computer has a domain-bound license for the content, any computer on the domain can consume Microsoft PlayReady-protected content that is acquired by any other computer in the domain. The user may easily add or remove computers from the domain, as long as the total number of computers in the domain does not exceed the limit defined by the service. Domain management is facilitated through a PlayReady domain server (domain controller server), which you provide.
5. Copy license to persistent license store (optional)
The license server can decide to either issue a license that is "use once" or one that is persisted on the consumer’s phone. Licenses persisted on the phone are stored in a persistent license store, which is created on the client during individualization. Storing a license on the phone is particularly useful in offline scenarios.
6. Play back content
If the license that is used to play back the content is valid, playback commences.
2.2 PlayReady vs. WMDRM
The Windows Phone client can use two forms of DRM protected content: the traditional Windows Media Digital Rights Management 10 (WMDRM 10) and the newer PlayReady. This flexibility enables existing content that is encrypted by using the WMDRM 10 SDK to play back within Windows Media playback software that supports DRM (such as Windows Media Player) to offer a higher degree of content protection.
Note
WMDRM uses different licenses than PlayReady uses. For example, if you want users to play a given DRM-protected video on both a Windows Media Player or on a Windows Phone, you need two licenses, one for each means of playback.
Note
PlayReady is not used only in Windows Phone. For more information about PlayReady, see PlayReady Whitepaper.
Note
Although Windows Phone can play PlayReady and WMDRM-packaged content, it does not support the entire ecosystems of these technologies. In addition, in order to play WMDRM-packaged content in Windows Phone, you need a PlayReady server (see the earlier diagram).
2.3 Limitations when using DRM
Even if you plan to encrypt content by only using WMDRM, it is important to note that you need at least one PlayReady License Server to distribute licenses to the client.
2.4 Considerations for using PlayReady encryption vs. WMDRM encryption
When setting up architecture for using DRM, keep in mind these considerations:
If you are streaming content, are you encoding and streaming in real-time from live sources, or are you streaming pre-encrypted files on demand?
Are your customers playing content using their Windows Phone or are they also using Windows Media Player?
Do you have a large library of WMDRM-packaged content (packaged using the WMDRM SDK) that you want to serve to Windows Phone customers or can you package all of your content using PlayReady?
Do you want to use the PIFF format? PIFF is supported when using PlayReady.
Regardless of these considerations, if you want to serve DRM content to Windows Phone, you need to purchase at least one PlayReady License Server; however, answering these questions can impact whether you can use only PlayReady packaging or whether you also need to handle WMDRM-packaged content in your overall DRM solution.
Streaming Content: If you want to encode streaming content in real-time from live sources using PlayReady, you can use a third party solution. Alternatively, you can use WMDRM in this scenario. If you are using MediaStreamSource you must use PlayReady packaged content.
Client Player: If your customers are only using Windows Phone to playback DRM, then you can package everything with PlayReady; however, WMDRM encryption is required for customers who are also targeting Windows Media Player clients.
Your Content Library: Regardless whether you are only playing content in Windows Phone, if you do not want to package your content using PlayReady, you need to create a DRM solution that takes into account WMDRM-packaged content.
Note
Windows Phone does not support WMDRM content with script stream (script streams provide text with the stream, such as with closed captioning). If you play WMDRM content with script stream, the audio and video play as usual but the script stream is ignored.
2.5 Integrating DRM in Windows Phone
For streaming or progressive download, the following steps are needed to integrate DRM into Windows Phone:
Set up the necessary server infrastructure to serve up DRM-protected content.
Point to this protected content from your Windows Phone app using MediaElement.
Handle expected errors (for example, when the user does not allow DRM content).
If needed, subclass the LicenseAcquirer or DomainAcquirer class to gain custom business logic.
Note
For WMDRM content, a URL override will be needed to point the license acquisition to the correct PlayReady license server. This is due to the fact that WMDRM content most likely will not contain a PlayReady license server in its WRMHeader.
2.6 Set up server infrastructure
Before using DRM, you must first package the content you want to protect and make it available to clients from a distribution server. You do this by using packaging software. There are several options for packaging content. Two of these options include:
WMDRM 10 SDK.
PlayReady Server SDK.
To learn more about these technologies and associated server configurations, see the "PlayReady vs. WMDRM" section earlier in this topic.
When you encrypt and package your content, you specify a License Acquisition URL (LAURL). You have the opportunity in the Windows Phone app to override this value before sending the License Challenge to a License Server. This is particularly useful if you have one library of WMDRM-packaged content and want to serve that content to both Windows Phone and Windows Media Player clients.
2.7 Point to DRM content from a Windows Phone app
If you are using only PlayReady servers to encrypt your encoded content, you can simply point to the content using the Source property of the MediaElement.
<MediaElement x:Name="myMediaElement" Source="myProtectedVideo.wmv" />
You can do this because content that is packaged by using the PlayReady Server SDK includes in its content header the URI location of the licensing server. However, if you are using the WMRM SDK to package your content, the location of the licensing server is not included in the header, and therefore, you need to specify this URI. To do this, use the LicenseServerUriOverride property to specify the URI for the LicenseAcquirer property of the MediaElement to use to find the license:
myMediaElement.LicenseAcquirer.LicenseServerUriOverride =
new Uri("https://contoso.com/myLicenseServer.asmx", UriKind.Absolute);
2.8 Detecting the DRM state
You can use the MediaElementState enumeration to detect what state the MediaElement is in, specifically, whether the MediaElement is currently individualizing the Windows Phone client or acquiring a license. One reason for detecting these states is to allow you to give feedback to the user about what is going on while the MediaElement prepares to play the content. For example, you can inform the user when individualization is taking place or when a license is being requested by using a TextBlock, as in the following example.
if (myMediaElement.CurrentState == MediaElementState.Individualizing)
{
myStateTextBlock.text = "Downloading DRM Client";
}
else if(myMediaElement.CurrentState == MediaElementState.AcquiringLicense)
{
myStateTextBlock.text = "Aquiring License";
}
2.9 Error handling
DRM-related errors
The following table provides a list of errors that are associated with Windows Phone DRM. The error numbers listed below are consecutive from 6000 up. Note that in addition to error handling done through the MediaFailed event of the MediaElement (as listed below), synchronous methods and properties can directly throw an exception and asynchronous methods can return exceptions through their corresponding AsyncCompletedEventArgs.
Error Code |
Notes |
---|---|
6000 |
MediaElement failed to play the DRM-protected content. |
6001 |
The individualization component software failed to download to the user’s phone. This error occurs when the MediaElement is in the IndividualizingMediaElementState. One possible reason for this error is that the device cannot connect to the Microsoft Individualization Server. |
6002 |
The license acquisition failed. This error occurs when the MediaElement is in the AcquiringLicenseMediaElementState. One possible reason for this error is that the device cannot connect to the license server. |
6003 |
The individualization component software failed to install. |
6004 |
The installation of Windows Phone on the client is out of date and needs to be updated. |
6005 |
The processing of the file header failed. For example, the header might be mal-formed. |
6006 |
License processing failed on the client. This error occurs when the MediaElement is in the AcquiringLicense MediaElementState. |
6007 |
The PlayReady License server allows the server developer to return an error that is specific to the service. For example, the server might return one of these errors: "You need to pay your bill," "The service is unavailable," or "You have exhausted your monthly stream count." The SOAP exception that is returned from the license server has additional data in the CustomData field that is added by the server. You need to write app logic in the Windows Phone app to interpret this. These responses are primarily used when you implement a custom license acquisition (see the "Add custom logic" section later in this topic). |
6008 |
The user disabled DRM in the configuration settings of the client. |
6009 |
The maximum for individualization attempts was exceeded. |
6010 |
A DRM protocol error occurred. |
6011 |
A DRM license acquisition URL override is required. |
6012 |
A DRM license-acquisition redirect error occurred. |
6013 |
The user's HW configuration has changed in a significant way and the DRM state is no longer valid. |
6017 |
The user tried to acquire a license by using a stream (calling AcquireLicenseAsync; however, the stream is in unprotected content. |
6021 |
The DRM system does not support the client OS. |
6023 |
IBX service is not reachable. Some malware / spyware will block calls to *.microsoft.com so ensure that the machine is fully patched and protected. |
6030 |
The license contains an unsupported license protection. |
6031 |
The integrity of the secured video output connection has been compromised or lost. |
6032 |
The secured graphics connection requires renegotiation but could not be renegotiated. |
6033 |
A revoked High-Bandwidth Digital Content Protection (HDCP) device is attached to the video output. |
6034 |
The graphics adapter or the driver has been tampered with. |
6035 |
A new graphics connection was added during playback and playback was halted. |
6036 |
Graphics device's driver certificate is invalid. |
6040 |
The domain is full and does not allow the user to add any more clients to this domain. |
6041 |
The client is not a domain member. |
6042 |
The domain account identifier is unknown. |
Note
If you are implementing your own custom license acquisition, you must add the headers msprdrm_server_redirect_compat:false and msprdrm_server_exception_compat:false to your HTTP Web requests. If you do not add these headers, errors and redirect messages do not work correctly. See the code example in the adding custom logic section.
Note
If you start multiple asynchronous operations on the same LicenseAcquirer or DomainAcquirer, an InvalidOperationException is thrown.
2.10 Add custom logic
The LicenseAcquirer class is used by the MediaElement to handle acquiring licenses for DRM-encrypted content from the PlayReady License Server. You can subclass the LicenseAcquirer class and add custom logic, such as adding your own custom authentication scheme to the license request.
The following example shows how to override the LicenseAcquirer class (named "ManualLicenseAcquirer") and have a MediaElement use it to acquire the license.
<StackPanel x:Name="LayoutRoot" Background="Gray" Orientation="Vertical">
<MediaElement x:Name="myME" Height="100"/>
</StackPanel>
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
// Test a full fledged manual acquirer
// Set the LicenseAcquirer of the MediaElement to the custom License Acquirer
// defined in this sample.
myME.LicenseAcquirer = new ManualLicenseAcquirer(myME.Name);
// Set the License URI to proper License Server address.
myME.LicenseAcquirer.LicenseServerUriOverride = new Uri("https://contoso.com/myLicenseServer.asmx", UriKind.Absolute);
myME.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(myME_MediaFailed);
// Set the source of the MediaElement to the URL of the media encrypted with WMDRM.
myME.Source = new Uri("https://contoso.com/wmdrm_url.wmv", UriKind.Absolute);
}
void myME_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
string errorMessage = "";
if (e.ErrorException.ToString().Contains(" 6001 "))
{
errorMessage = "The individualization component software failed to" +
" download to the user’s phone. This error would" +
" come up when the MediaElement is in the Individualizing" +
" MediaElementState. One possible reason for this error is" +
" that the phone cannot connect the Microsoft" +
" Individualization Server.";
}
else if (e.ErrorException.ToString().Contains(" 6004 "))
{
errorMessage = " The installation of Windows Phone on the device is" +
" out of date and needs to be updated.";
}
else
{
errorMessage = "MediaFailed: " + e.ErrorException.Message + ".";
}
System.Windows.Browser.HtmlPage.Window.Alert(errorMessage);
}
// makes license request explicitly
public class ManualLicenseAcquirer : LicenseAcquirer
{
private string challengeString;
string _mediaElementName;
public ManualLicenseAcquirer(string mediaElementName)
{
_mediaElementName = mediaElementName;
}
// The default implementation of OnAcquireLicense calls into the MediaElement to acquire a
// license. It is called when the Media pipeline is building a topology and will be raised
// before MediaOpened is raised.
protected override void OnAcquireLicense(System.IO.Stream licenseChallenge, Uri licenseServerUri)
{
StreamReader sr = new StreamReader(licenseChallenge);
challengeString = sr.ReadToEnd();
// Need to resolve the URI for the License Server -- make sure it is correct
// and store that correct URI as resolvedLicenseServerUri.
Uri resolvedLicenseServerUri;
if (LicenseServerUriOverride == null)
resolvedLicenseServerUri = licenseServerUri;
else
resolvedLicenseServerUri = LicenseServerUriOverride;
// Make a HttpWebRequest to the License Server.
HttpWebRequest request = WebRequest.Create(resolvedLicenseServerUri) as HttpWebRequest;
request.Method = "POST";
// Set ContentType through property
request.ContentType = "application/xml";
// ADD REQUIRED HEADERS.
// The headers below are necessary so that error handling and redirects are handled
// properly via the Windows Phone client.
request.Headers["msprdrm_server_redirect_compat"] = "false";
request.Headers["msprdrm_server_exception_compat"] = "false";
// Initiate getting request stream
IAsyncResult asyncResult = request.BeginGetRequestStream(new AsyncCallback(RequestStreamCallback), request);
}
// This method is called when the asynchronous operation completes.
void RequestStreamCallback(IAsyncResult ar)
{
HttpWebRequest request = ar.AsyncState as HttpWebRequest;
// populate request stream
request.ContentType = "text/xml";
Stream requestStream = request.EndGetRequestStream(ar);
StreamWriter streamWriter = new StreamWriter(requestStream, System.Text.Encoding.UTF8);
streamWriter.Write(challengeString);
streamWriter.Close();
// Make async call for response
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
private void ResponseCallback(IAsyncResult ar)
{
HttpWebRequest request = ar.AsyncState as HttpWebRequest;
WebResponse response = request.EndGetResponse(ar);
SetLicenseResponse(response.GetResponseStream());
}
}
}
Public Class Page
Inherits UserControl
Public Sub New()
MyBase.New
InitializeComponent
AddHandler Loaded, AddressOf Me.Page_Loaded
End Sub
Private Sub Page_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Test a full fledged manual acquirer
' Set the LicenseAcquirer of the MediaElement to the custom License Acquirer
' defined in this sample.
myME.LicenseAcquirer = New ManualLicenseAcquirer(myME.Name)
' Set the License URI to proper License Server address.
myME.LicenseAcquirer.LicenseServerUriOverride = New Uri("https://contoso.com/myLicenseServer.asmx", UriKind.Absolute)
AddHandler myME.MediaFailed, AddressOf Me.myME_MediaFailed
' Set the source of the MediaElement to the URL of the media encrypted with WMDRM.
myME.Source = New Uri("http:, contoso.com/wmdrm_url.wmv", UriKind.Absolute);)
End Sub
Private Sub myME_MediaFailed(ByVal sender As Object, ByVal e As ExceptionRoutedEventArgs)
Dim errorMessage As String = ""
If e.ErrorException.ToString.Contains(" 6001 ") Then
errorMessage = "The individualization component software failed to" & _
" download to the user's phone. This error would" & _
" come up when the MediaElement is in the Individualizing" & _
" MediaElementState. One possible reason for this error is" & _
" that the phone cannot connect the Microsoft" & _
" Individualization Server."
ElseIf e.ErrorException.ToString.Contains(" 6004 ") Then
errorMessage = "The installation of Windows Phone on the device is" & _
" out of date and needs to be updated."
Else
errorMessage = "MediaFailed: " & _
+ e.ErrorException.Message + "."
End If
System.Windows.Browser.HtmlPage.Window.Alert(errorMessage)
End Sub
' makes license request explicitly
Public Class ManualLicenseAcquirer
Inherits LicenseAcquirer
Private challengeString As String
Private _mediaElementName As String
Public Sub New(ByVal mediaElementName As String)
MyBase.New
_mediaElementName = mediaElementName
End Sub
' The default implementation of OnAcquireLicense calls into the MediaElement to acquire a
' license. It is called when the Media pipeline is building a topology and will be raised
' before MediaOpened is raised.
Protected Overrides Sub OnAcquireLicense(ByVal licenseChallenge As System.IO.Stream, ByVal licenseServerUri As Uri)
Dim sr As StreamReader = New StreamReader(licenseChallenge)
challengeString = sr.ReadToEnd
' Need to resolve the URI for the License Server -- make sure it is correct
' and store that correct URI as resolvedLicenseServerUri.
Dim resolvedLicenseServerUri As Uri
If (LicenseServerUriOverride Is Nothing) Then
resolvedLicenseServerUri = licenseServerUri
Else
resolvedLicenseServerUri = LicenseServerUriOverride
End If
' Make a HttpWebRequest to the License Server.
Dim request As HttpWebRequest = CType(WebRequest.Create(resolvedLicenseServerUri),HttpWebRequest)
request.Method = "POST"
' Set ContentType through property
request.ContentType = "application/xml"
' ADD REQUIRED HEADERS.
' The headers below are necessary so that error handling and redirects are handled
' properly via the Windows Phone client.
request.Headers("msprdrm_server_redirect_compat") = "false"
request.Headers("msprdrm_server_exception_compat") = "false"
' Initiate getting request stream
Dim asyncResult As IAsyncResult = request.BeginGetRequestStream(New AsyncCallback(RequestStreamCallback), request)
End Sub
' This method is called when the asynchronous operation completes.
Private Sub RequestStreamCallback(ByVal ar As IAsyncResult)
Dim request As HttpWebRequest = CType(ar.AsyncState,HttpWebRequest)
' populate request stream
request.ContentType = "text/xml"
Dim requestStream As Stream = request.EndGetRequestStream(ar)
Dim streamWriter As StreamWriter = New StreamWriter(requestStream, System.Text.Encoding.UTF8)
streamWriter.Write(challengeString)
streamWriter.Close
' Make async call for response
request.BeginGetResponse(New AsyncCallback(ResponseCallback), request)
End Sub
Private Sub ResponseCallback(ByVal ar As IAsyncResult)
Dim request As HttpWebRequest = CType(ar.AsyncState,HttpWebRequest)
Dim response As WebResponse = request.EndGetResponse(ar)
SetLicenseResponse(response.GetResponseStream)
End Sub
End Class
End Class
3 Windows Phone DRM offline scenarios
Some users today do not have the available bandwidth to watch high-quality video over the Internet. They do have high speed connections that can be used to download content for viewing later. Other users may want to download a movie to watch later when no network is available, such as on an airplane or at a friend's house that does not have Internet connectivity. Being able to deliver Digital Rights Management (DRM) to content that is offline, but still has increased protection, is key to enabling a variety of content-publishing business scenarios, such as media purchase, subscription, and rental.
3.1 Basic offline DRM (one-time purchase)
Perhaps the simplest offline scenario is when users purchase a piece of content and then download it to their phone to play whenever they want to.
Because the user is attempting to play back protected content offline, license validation also needs to occur offline. To do this, licenses needed by downloaded content are stored on what is called the persistent license store, which is created on the user’s phone during initialization. When the user attempts to play back offline content, the content attempts to validate playback by looking for its corresponding license in the persistent license store. You can learn more about enumerating through these licenses in the "Enumerating through licenses" section later in this topic.
3.2 Validate license before download
In offline scenarios, users download a content file before they play it. Because downloading a media file can take time and bandwidth, consider validating the user’s license before allowing the download, instead of validating when the user attempts playback. The following example shows how to do this.
The following application uses a key identifier and an authentication token to send a license acquisition request to the license server. The license server responds with a license and the URL from which to download the content.
// Called when the user is online and wants to download some protected content.
public void GetLicensePreDelivery(string customData,
Guid keyId)
{
Uri licenseServerUrl = new Uri("https://contoso.com/myLicenseServer.asmx");
LicenseAcquirer acquirer = new LicenseAcquirer();
acquirer.ChallengeCustomData = customData;
// Set the License URI to proper License Server address.
acquirer.LicenseServerUriOverride = licenseServerUrl;
acquirer.AcquireLicenseCompleted += new EventHandler<AcquireLicenseCompletedEventArgs>(acquirer_Completed);
acquirer.AcquireLicenseAsync(keyId, ContentKeyType.Aes128Bit, Guid.Empty);
}
The AcquireLicenseAsync call completes after starting the license acquisition but without waiting for the long content download operation to finish. When the license acquisition actually completes, the delegate that is configured on the AcquireLicenseCompleted event is called. In this example, that is the acquirer_Completed method, and it might look something like in the following example.
public void acquirer_Completed(object sender, AcquireLicenseCompletedEventArgs e)
{
if (e.Error != null)
{
// take appropriate action. Might be retrying for instance.
}
else if (e.Cancelled)
{
// take appropriate action. Might be nothing.
}
else
{
//
// We acquired the license successfully, go ahead and download
// the content. Note the service decided to stash the content
// url in the LicenseAcquirer response custom data.
//
string contentAcquisitionUrl = e.ResponseCustomData;
DownloadContent(contentAcquisitionUrl);
}
}
3.3 Rentals and subscriptions
Enabling rental, subscriptions, or both is more complex than simply purchasing content because you need to build in more complex LicenseServer logic for license management. For example, if users download content that they are renting, you need to specify in the license when the license expires and under what conditions. The license might expire 24 hours after it is initially played or in 30 days after it is downloaded, whichever occurs first.
Rental and subscription agreements are stored in the licenses on the user’s persistent license store. Before playing content, the correct license on the persistent license store needs to be located and validated. Before going into how licenses are iterated through, let’s talk about different types of licenses and how they are chained together to enable certain scenarios.
3.4 Root licenses, leaf licenses, and license chains
A license is a data file that holds a decryption key for an asset (another license or a piece of content). It also contains DRM rights and restrictions that define how the asset that it gives access to can be used. Licenses come in three varieties:
Simple License: A PlayReady License that is delivered from an app that is built by using the PlayReady Server SDK that contains rights and restrictions along with a key for the associated piece of content.
Root License: A license that controls one or more leaf licenses. The leaf licenses control playback of specific pieces of content while the root license controls all of them. For example, in a subscription model, the root license may have an expiration date while the leaf licenses do not. When the root license expires, the leaf licenses are unusable until a new root license is acquired.
Leaf License: A simple license that is dependent on a root license.
When you have leaf licenses controlled by a single root license, these licenses are said to be "chained." The following diagram shows how this looks conceptually:
Let’s say that the user has a subscription and has downloaded several videos to play back later offline. For each video, there is a leaf license that specifies the allowable usage for that video. In this case, let’s assume that the leaf license says that the user can play the video anytime. However, PlayReady ensures that there is a valid root license before allowing playback. If the root license has expired (perhaps because users allowed their subscriptions to expire), the leaf license, does not allow playback. In this way, the root license can be used to control the entire subscription (all the leaf licenses on the user’s device).
Note
A leaf license can have more than one root license. In this case, at least one root license needs to be valid for playback to occur. In addition, a leaf license can exist without a root license.
3.5 Enumerating through licenses
You can check whether a license or license chain has expired by iterating through all the licenses (MediaLicense objects) on the persistent license store. If any licenses need to be updated, you can add logic to attempt to connect to the licensing server and make necessary updates, prompt the user for payment, and so on.
The following example shows how to check whether a subscription license needs to be renewed.
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 subscription 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);
}
In this example, you are iterating through all the MediaLicense instances in the persistent license store. A MediaLicense instance could be any of the following:
A license chain (leaf plus root)
Single simple license
Single leaf license (root does not exist)
Single root license (root is queried directly)
The following function could be used to filter the list of content shown to the user in a list of possible playback choices, especially if the user is offline and cannot get new licenses. It could also be used to decide whether to get a license before going offline. For a subscription customer, the CheckSubscriptionRootForRenewal function (from the previous example) should be called first if the user is online.
public bool IsUsableLicenseAvailableForContent(System.IO.Stream contentFile)
{
bool returnValue = false;
// SelectLicenses works only if the user is running the Windows Phone app offline
// *and* elevated trust.
IEnumerable<MediaLicense> myLicenses = LicenseManagement.SelectLicenses(contentFile);
foreach (MediaLicense ML in myLicenses)
{
if (ML.Usable)
{
returnValue = true;
break;
}
}
return returnValue;
}