Skip to content

Commit 7b098fe

Browse files
committed
Porting PlayReady content from UWP->WinUI
1 parent 9fd9445 commit 7b098fe

4 files changed

Lines changed: 849 additions & 4 deletions

File tree

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
---
2+
description: This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a WinUI app.
3+
title: Adaptive Streaming with PlayReady
4+
ms.date: 02/08/2026
5+
ms.topic: article
6+
keywords: windows 10, winui
7+
ms.localizationpriority: medium
8+
---
9+
# Adaptive streaming with PlayReady
10+
11+
12+
This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a WinUI app.
13+
14+
This feature currently supports playback of Dynamic streaming over HTTP (DASH) content.
15+
16+
HLS (Apple's HTTP Live Streaming) is not supported with PlayReady.
17+
18+
Smooth streaming is also currently not supported natively; however, PlayReady is extensible and by using additional code or libraries, PlayReady-protected Smooth streaming can be supported, leveraging software or even hardware DRM (digital rights management).
19+
20+
This article only deals with the aspects of adaptive streaming specific to PlayReady. For information about implementing adaptive streaming in general, see [Adaptive streaming](adaptive-streaming.md).
21+
22+
This article uses code from the [Adaptive streaming sample](https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/AdaptiveStreaming) in Microsoft's **Windows-universal-samples** repository on GitHub. Scenario 4 deals with using adaptive streaming with PlayReady. You can download the repo in a ZIP file by navigating to the root level of the repository and selecting the **Download ZIP** button.
23+
24+
You will need the following **using** statements:
25+
26+
```csharp
27+
using LicenseRequest;
28+
using System;
29+
using System.Net.Http;
30+
using System.Net.Http.Headers;
31+
using System.Runtime.InteropServices;
32+
using System.Threading.Tasks;
33+
using Windows.Foundation.Collections;
34+
using Windows.Media.Protection;
35+
using Windows.Media.Protection.PlayReady;
36+
using Windows.Media.Streaming.Adaptive;
37+
using Windows.UI.Xaml.Controls;
38+
```
39+
40+
The **LicenseRequest** namespace is from **CommonLicenseRequest.cs**, a PlayReady file provided by Microsoft to licensees.
41+
42+
You will need to declare a few global variables:
43+
44+
```csharp
45+
private AdaptiveMediaSource ams = null;
46+
private MediaProtectionManager protectionManager = null;
47+
private string playReadyLicenseUrl = "";
48+
private string playReadyChallengeCustomData = "";
49+
```
50+
51+
You will also want to declare the following constant:
52+
53+
```csharp
54+
private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;
55+
```
56+
57+
## Setting up the MediaProtectionManager
58+
59+
To add PlayReady content protection to your UWP app, you will need to set up a [MediaProtectionManager](/uwp/api/Windows.Media.Protection.MediaProtectionManager) object. You do this when initializing your [**AdaptiveMediaSource**](/uwp/api/Windows.Media.Streaming.Adaptive.AdaptiveMediaSource) object.
60+
61+
The following code sets up a [MediaProtectionManager](/uwp/api/Windows.Media.Protection.MediaProtectionManager):
62+
63+
```csharp
64+
private void SetUpProtectionManager(ref MediaElement mediaElement)
65+
{
66+
protectionManager = new MediaProtectionManager();
67+
68+
protectionManager.ComponentLoadFailed +=
69+
new ComponentLoadFailedEventHandler(ProtectionManager_ComponentLoadFailed);
70+
71+
protectionManager.ServiceRequested +=
72+
new ServiceRequestedEventHandler(ProtectionManager_ServiceRequested);
73+
74+
PropertySet cpSystems = new PropertySet();
75+
76+
cpSystems.Add(
77+
"{F4637010-03C3-42CD-B932-B48ADF3A6A54}",
78+
"Windows.Media.Protection.PlayReady.PlayReadyWinRTTrustedInput");
79+
80+
protectionManager.Properties.Add("Windows.Media.Protection.MediaProtectionSystemIdMapping", cpSystems);
81+
82+
protectionManager.Properties.Add(
83+
"Windows.Media.Protection.MediaProtectionSystemId",
84+
"{F4637010-03C3-42CD-B932-B48ADF3A6A54}");
85+
86+
protectionManager.Properties.Add(
87+
"Windows.Media.Protection.MediaProtectionContainerGuid",
88+
"{9A04F079-9840-4286-AB92-E65BE0885F95}");
89+
90+
mediaElement.ProtectionManager = protectionManager;
91+
}
92+
```
93+
94+
This code can simply be copied to your app, since it is mandatory for setting up content protection.
95+
96+
The [ComponentLoadFailed](/uwp/api/windows.media.protection.mediaprotectionmanager.componentloadfailed) event is fired when the load of binary data fails. We need to add an event handler to handle this, signaling that the load did not complete:
97+
98+
```csharp
99+
private void ProtectionManager_ComponentLoadFailed(
100+
MediaProtectionManager sender,
101+
ComponentLoadFailedEventArgs e)
102+
{
103+
e.Completion.Complete(false);
104+
}
105+
```
106+
107+
Similarly, we need to add an event handler for the [ServiceRequested](/uwp/api/windows.media.protection.mediaprotectionmanager.servicerequested) event, which fires when a service is requested. This code checks what kind of request it is, and responds appropriately:
108+
109+
```csharp
110+
private async void ProtectionManager_ServiceRequested(
111+
MediaProtectionManager sender,
112+
ServiceRequestedEventArgs e)
113+
{
114+
if (e.Request is PlayReadyIndividualizationServiceRequest)
115+
{
116+
PlayReadyIndividualizationServiceRequest IndivRequest =
117+
e.Request as PlayReadyIndividualizationServiceRequest;
118+
119+
bool bResultIndiv = await ReactiveIndivRequest(IndivRequest, e.Completion);
120+
}
121+
else if (e.Request is PlayReadyLicenseAcquisitionServiceRequest)
122+
{
123+
PlayReadyLicenseAcquisitionServiceRequest licenseRequest =
124+
e.Request as PlayReadyLicenseAcquisitionServiceRequest;
125+
126+
LicenseAcquisitionRequest(
127+
licenseRequest,
128+
e.Completion,
129+
playReadyLicenseUrl,
130+
playReadyChallengeCustomData);
131+
}
132+
}
133+
```
134+
135+
## Individualization service requests
136+
137+
The following code reactively makes a PlayReady individualization service request. We pass in the request as a parameter to the function. We surround the call in a try/catch block, and if there are no exceptions, we say the request completed successfully:
138+
139+
```csharp
140+
async Task<bool> ReactiveIndivRequest(
141+
PlayReadyIndividualizationServiceRequest IndivRequest,
142+
MediaProtectionServiceCompletion CompletionNotifier)
143+
{
144+
bool bResult = false;
145+
Exception exception = null;
146+
147+
try
148+
{
149+
await IndivRequest.BeginServiceRequest();
150+
}
151+
catch (Exception ex)
152+
{
153+
exception = ex;
154+
}
155+
finally
156+
{
157+
if (exception == null)
158+
{
159+
bResult = true;
160+
}
161+
else
162+
{
163+
COMException comException = exception as COMException;
164+
if (comException != null && comException.HResult == MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED)
165+
{
166+
IndivRequest.NextServiceRequest();
167+
}
168+
}
169+
}
170+
171+
if (CompletionNotifier != null) CompletionNotifier.Complete(bResult);
172+
return bResult;
173+
}
174+
```
175+
176+
Alternatively, we may want to proactively make an individualization service request, in which case we call the following function in place of the code calling `ReactiveIndivRequest` in `ProtectionManager_ServiceRequested`:
177+
178+
```csharp
179+
async void ProActiveIndivRequest()
180+
{
181+
PlayReadyIndividualizationServiceRequest indivRequest = new PlayReadyIndividualizationServiceRequest();
182+
bool bResultIndiv = await ReactiveIndivRequest(indivRequest, null);
183+
}
184+
```
185+
186+
## License acquisition service requests
187+
188+
If instead the request was a [PlayReadyLicenseAcquisitionServiceRequest](/uwp/api/Windows.Media.Protection.PlayReady.PlayReadyLicenseAcquisitionServiceRequest), we call the following function to request and acquire the PlayReady license. We tell the **MediaProtectionServiceCompletion** object that we passed in whether the request was successful or not, and we complete the request:
189+
190+
```csharp
191+
async void LicenseAcquisitionRequest(
192+
PlayReadyLicenseAcquisitionServiceRequest licenseRequest,
193+
MediaProtectionServiceCompletion CompletionNotifier,
194+
string Url,
195+
string ChallengeCustomData)
196+
{
197+
bool bResult = false;
198+
string ExceptionMessage = string.Empty;
199+
200+
try
201+
{
202+
if (!string.IsNullOrEmpty(Url))
203+
{
204+
if (!string.IsNullOrEmpty(ChallengeCustomData))
205+
{
206+
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
207+
byte[] b = encoding.GetBytes(ChallengeCustomData);
208+
licenseRequest.ChallengeCustomData = Convert.ToBase64String(b, 0, b.Length);
209+
}
210+
211+
PlayReadySoapMessage soapMessage = licenseRequest.GenerateManualEnablingChallenge();
212+
213+
byte[] messageBytes = soapMessage.GetMessageBody();
214+
HttpContent httpContent = new ByteArrayContent(messageBytes);
215+
216+
IPropertySet propertySetHeaders = soapMessage.MessageHeaders;
217+
218+
foreach (string strHeaderName in propertySetHeaders.Keys)
219+
{
220+
string strHeaderValue = propertySetHeaders[strHeaderName].ToString();
221+
222+
if (strHeaderName.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
223+
{
224+
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse(strHeaderValue);
225+
}
226+
else
227+
{
228+
httpContent.Headers.Add(strHeaderName.ToString(), strHeaderValue);
229+
}
230+
}
231+
232+
CommonLicenseRequest licenseAcquision = new CommonLicenseRequest();
233+
234+
HttpContent responseHttpContent =
235+
await licenseAcquision.AcquireLicense(new Uri(Url), httpContent);
236+
237+
if (responseHttpContent != null)
238+
{
239+
Exception exResult = licenseRequest.ProcessManualEnablingResponse(
240+
await responseHttpContent.ReadAsByteArrayAsync());
241+
242+
if (exResult != null)
243+
{
244+
throw exResult;
245+
}
246+
bResult = true;
247+
}
248+
else
249+
{
250+
ExceptionMessage = licenseAcquision.GetLastErrorMessage();
251+
}
252+
}
253+
else
254+
{
255+
await licenseRequest.BeginServiceRequest();
256+
bResult = true;
257+
}
258+
}
259+
catch (Exception e)
260+
{
261+
ExceptionMessage = e.Message;
262+
}
263+
264+
CompletionNotifier.Complete(bResult);
265+
}
266+
```
267+
268+
## Initializing the AdaptiveMediaSource
269+
270+
Finally, you will need a function to initialize the [AdaptiveMediaSource](/uwp/api/Windows.Media.Streaming.Adaptive.AdaptiveMediaSource), created from a given [Uri](/dotnet/api/system.uri) and [MediaElement](/uwp/api/Windows.UI.Xaml.Controls.MediaElement). The **Uri** should be the link to the media file (HLS or DASH); the **MediaElement** should be defined in your XAML.
271+
272+
```csharp
273+
async private void InitializeAdaptiveMediaSource(System.Uri uri, MediaElement m)
274+
{
275+
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);
276+
if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
277+
{
278+
ams = result.MediaSource;
279+
SetUpProtectionManager(ref m);
280+
m.SetMediaStreamSource(ams);
281+
}
282+
else
283+
{
284+
// Error handling
285+
}
286+
}
287+
```
288+
289+
You can call this function in whichever event handles the start of adaptive streaming; for example, in a button click event.
290+
291+
## See also
292+
- [PlayReady DRM](playready-client-sdk.md)

hub/apps/develop/media-playback/adaptive-streaming.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ Call [**MediaSource.CreateFromUri**](/uwp/api/windows.media.core.mediasource.cre
4040

4141
If your app requires more advanced adaptive streaming features, such as providing custom HTTP headers, monitoring the current download and playback bitrates, or adjusting the ratios that determine when the system switches bitrates of the adaptive stream, use the **[AdaptiveMediaSource](/uwp/api/Windows.Media.Streaming.Adaptive.AdaptiveMediaSource)** object.
4242

43-
The adaptive streaming APIs are found in the [**Windows.Media.Streaming.Adaptive**](/uwp/api/Windows.Media.Streaming.Adaptive) namespace. The examples in this article use APIs from the following namespaces.
44-
45-
:::code language="csharp" source="~/../snippets-windows/winappsdk/audio-video-camera/adaptive-streaming-winui/cs/AdaptiveStreamingWinUI/MainWindow.xaml.cs" id="SnippetAdaptiveStreamingUsing":::
46-
4743
## Initialize an AdaptiveMediaSource from a URI.
4844

4945
Initialize the **AdaptiveMediaSource** with the URI of an adaptive streaming manifest file by calling [**CreateFromUriAsync**](/uwp/api/windows.media.streaming.adaptive.adaptivemediasource.createfromuriasync). The [**AdaptiveMediaSourceCreationStatus**](/uwp/api/Windows.Media.Streaming.Adaptive.AdaptiveMediaSourceCreationStatus) value returned from this method lets you know if the media source was created successfully. If so, you can set the object as the stream source for your **MediaPlayer** by creating a **MediaSource** object by calling [**MediaSource.CreateFromAdaptiveMediaSource**](/uwp/api/Windows.Media.Core.MediaSource.AdaptiveMediaSource), and then assigning it to the media player's [**Source**](/uwp/api/windows.media.playback.mediaplayer.Source) property. In this example, the [**AvailableBitrates**](/uwp/api/windows.media.streaming.adaptive.adaptivemediasource.availablebitrates) property is queried to determine the maximum supported bitrate for this stream, and then that value is set as the initial bitrate. This example also registers handlers for the several **AdaptiveMediaSource** events that are discussed later in this article.

0 commit comments

Comments
 (0)