Simple C# PlayReady App Walkthrough - Part 1
Before you begin creating the simple PlayReady protected Windows Store app, you must first install the appropriate software as described in Prerequisites. Once you have installed the software, you can begin to set up the simple PlayReady protected app.
In this section you will create the basic PlayReady protected Windows Store app. Then you will modify the main page to contain a Play button, a MediaElement control, and a text box that displays messages, warnings, and errors. Finally, you will add playback capabilities to the app's main page.
To set up the simple PlayReady example in C#
- In Visual Studio, from the FILE menu, choose New, Project.
- In the New Project dialog box, under Visual C#, choose Windows Store.
- In the Templates pane, choose Blank App (XAML), and change the name of the project to PlayReady CS RTM Windows Store app.
- Enter the location for the project in the Location text box or use the default location.
- Choose the OK button.
Referencing PlayReady Client SDK for Windows Store Apps
The next procedure describes how to set up a reference in your Windows Store app to the PlayReady Client SDK for Windows Store Apps extension.
To add PlayReady Client SDK for Windows Store Apps extension
From the PROJECT menu, choose Add Reference.
In the Reference Manager dialog, expand the Windows tab, and then choose Extensions.
Select the check box next to Microsoft PlayReady Client SDK.
Choose the OK button.
From the PROJECT menu, choose PlayReady CS RTM Windows Store app Properties.
Choose the Build tab.
Under the General section, choose the Platform target.
Note
You must choose a specific target platform (x86, x64, or ARM) - do not use Any CPU.
- Make sure the Prefer 32-bit option is not selected.
Modifying the Main Page
The main page of the Windows Store app consists of the following three elements:
- A Play button that launches the playback of the media.
- A MediaElement control that plays the video and audio.
- A multiline text box in which program execution trace messages are displayed.
To add the elements to the main page
In Solution Explorer, open the shortcut menu for MainPage.xaml and choose View Designer.
In the XAML page, replace the text between <Grid> and </Grid> with the following code:
<Button Content="Play" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Name="btnPlay" Click="btnPlay_Click_1"/> <MediaElement Name="myME" HorizontalAlignment="Left" Height="379" Margin="10,53,0,0" VerticalAlignment="Top" Width="641"/> <TextBox FontFamily="Consolas" HorizontalAlignment="Left" TextWrapping="Wrap" Name="txtMessages" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Auto" Margin="656,53,0,0" Height="705" Width="700"/>
This code adds a Play button, a MediaElement control, and a text box with a vertical scroll bar on the main page of the app.
In Solution Explorer, open the shortcut menu for MainPage.xaml and choose View Code.
In MainPage.xaml.cs add the following lines after
using Windows.UI.Xaml.Navigation
:using PlayReady_CS_RTM_Windows_Store_app; using Microsoft.Media.PlayReadyClient; using Windows.Media.Protection; using Windows.UI.Core;
The first line provides access to the PlayReady Windows Store app. The second line simplifies adding PlayReady API calls to the code. The next line allows you to call the media protection manager. The fourth line provides a means of interacting with any XAML that needs to be done on a separate thread.
Add the following global variables just below the MainPage class declaration:
static public string LAURL = "http://playready.directtaps.net/win/rightsmanager.asmx"; static public string MEDIA = "http://playready.directtaps.net/win/media/TallShip_with_Credits_folk_rock_soundtrack_encrypted.wmv"; public static CoreDispatcher _dispatcher;
These lines contain the URLs for the license acquisition manager and the media to be played.
Add the following lines at the end of the MainPage constructor:
_dispatcher = Window.Current.Dispatcher; GlobalData.g_mainPage = this;
These lines create the dispatcher to be used when updating the XAML text box.
Add the following lines after the MainPage constructor:
private void log(string msg) { TestLogger.LogMessage(msg); } public async void LogMessage(String msg) { await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { txtMessages.Text += msg + Environment.NewLine; }); } public async void LogError(String msg) { await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { txtMessages.Text += msg + Environment.NewLine; }); }
These methods are used to add log messages and log errors to the dispatcher to wait for asynchronous display on the main page of the app.
Add the following lines after the OnNavigatedTo method:
private Playback playMedia = null; private void btnPlay_Click_1(object sender, RoutedEventArgs e) { playMedia = new Playback(); playMedia.Play(myME, MEDIA); }
This method starts playback of the media content when you click the Play button on the main page of the app.
Add the following lines after the MainPage class:
public class GlobalData { public static MainPage g_mainPage = null; } public class TestLogger { public static void LogMessage(String message) { if (GlobalData.g_mainPage != null) { GlobalData.g_mainPage.LogMessage(message); } } public static void LogError(String message) { if (GlobalData.g_mainPage != null) { GlobalData.g_mainPage.LogError(message); } } }
Adding Playback Capabilities
The Playback.Play method of the Windows Store app renders the video and audio to a MediaElement control when the user chooses the Play button. In addition, any message or error is displayed in the text box on the right side of the page.
To add playback capabilities to the code
In Solution Explorer, open the shortcut menu for the project and choose Add, New Item.
In the Add New Item dialog box, under Visual C#, choose Code.
In the Templates pane, choose Code File.
Change the name to Playback.cs, and then choose the Add button.
Add the following directives to the beginning of the file:
using System; using Windows.UI; using Windows.UI.Core; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml; using Windows.Foundation; using Windows.Media.Protection; using Microsoft.Media.PlayReadyClient;
Add the PlayReady_CS_RTM_Windows_Store_app namespace and the Playback class after the directives:
namespace PlayReady_CS_RTM_Windows_Store_app { public class Playback { } }
Add the following code to the beginning of the Playback class:
protected MediaElement _mediaElement = null; protected string _strMediaPath = null; protected MediaProtectionManager _protectionManager = new MediaProtectionManager(); const string _prGUID = "{F4637010-03C3-42CD-B932-B48ADF3A6A54}"; MediaProtectionServiceCompletion _serviceCompletionNotifier = null; RequestChain _requestChain = null; ServiceRequestConfigData _requestConfigData = null; public ServiceRequestConfigData RequestConfigData { set { this._requestConfigData= value; } get { return this._requestConfigData; } } void HookEventHandlers() { _mediaElement.CurrentStateChanged += new RoutedEventHandler( CurrentStateChanged ); _mediaElement.MediaEnded += new RoutedEventHandler( MediaEnded ); _mediaElement.MediaFailed += new ExceptionRoutedEventHandler( MediaFailed ); _mediaElement.MediaOpened += new RoutedEventHandler( MediaOpened ); } void UnhookEventHandlers() { _mediaElement.CurrentStateChanged -= new RoutedEventHandler( CurrentStateChanged ); _mediaElement.MediaEnded -= new RoutedEventHandler( MediaEnded ); _mediaElement.MediaFailed -= new ExceptionRoutedEventHandler( MediaFailed ); _mediaElement.MediaOpened -= new RoutedEventHandler( MediaOpened ); }
These lines set the values of several variables, define the RequestConfigData property, and set up the hooks to the event handlers for the main page.
After the event handlers, add the following Play method:
public void Play(MediaElement mediaElement, string strMediaPath) { TestLogger.LogMessage("Enter Playback.Play()" ); _mediaElement = mediaElement; _strMediaPath = strMediaPath; _protectionManager.ComponentLoadFailed += new ComponentLoadFailedEventHandler( ProtectionManager_ComponentLoadFailed ); _protectionManager.ServiceRequested += new ServiceRequestedEventHandler( ProtectionManager_ServiceRequested ); TestLogger.LogMessage("Creating protection system mappings..." ); Windows.Foundation.Collections.PropertySet cpSystems = new Windows.Foundation.Collections.PropertySet(); cpSystems.Add("{F4637010-03C3-42CD-B932-B48ADF3A6A54}", "Microsoft.Media.PlayReadyClient.PlayReadyWinRTTrustedInput"); //Playready _protectionManager.Properties.Add("Windows.Media.Protection.MediaProtectionSystemIdMapping", cpSystems); _protectionManager.Properties.Add("Windows.Media.Protection.MediaProtectionSystemId", "{F4637010-03C3-42CD-B932-B48ADF3A6A54}"); _mediaElement.ProtectionManager = _protectionManager; TestLogger.LogMessage("Creating media extension manager..." ); var extensions = new Windows.Media.MediaExtensionManager(); extensions.RegisterByteStreamHandler("Microsoft.Media.PlayReadyClient.PlayReadyByteStreamHandler", ".wma", "PRaudio"); extensions.RegisterByteStreamHandler("Microsoft.Media.PlayReadyClient.PlayReadyByteStreamHandler", ".wmv", "PRvideo"); HookEventHandlers(); _mediaElement.Source = new Uri( _strMediaPath ); TestLogger.LogMessage("Leave Playback.Play()" ); }
The Playback.Play method:
- Establishes handlers for the Windows.Media.Protection.MediaProtectionManager object (which was instantiated in the form class initialization) for handling the ComponentLoadFailed and ServiceRequested events.
- Creates and adds protection system mappings to the protection manager object.
- Attaches the protection manager object to the MediaElement control.
- Registers the PlayReady client codec with the media extension manager. This allows the PlayReady client to intercept the media data stream.
- Establishes event handlers that are used for monitoring the state of the MediaElement playback.
- Starts the playback by setting the MediaElement.Source property to the URL of the example DRM-protected video clip.
Add the following event handler methods to the end of the Playback class:
protected void CurrentStateChanged( object sender, RoutedEventArgs e ) { TestLogger.LogMessage("CurrentState:" + ((MediaElement)sender).CurrentState); } virtual protected void MediaFailed(object sender, ExceptionRoutedEventArgs e) { UnhookEventHandlers(); TestLogger.LogMessage("MediaFailed Source: " + ((MediaElement)sender).Source ); TestLogger.LogMessage("*****************************************Playback Failed************************************"); TestLogger.LogMessage("MediaFailed: " + e.ErrorMessage ); } virtual protected void MediaEnded( object sender, RoutedEventArgs e ) { UnhookEventHandlers(); TestLogger.LogMessage("MediaEnded: " + ((MediaElement)sender).Source ); TestLogger.LogMessage("*****************************************Playback succeeded**********************************"); } virtual protected void MediaOpened( object sender, RoutedEventArgs e ) { TestLogger.LogMessage("MediaOpened: " + ((MediaElement)sender).Source ); } void ProtectionManager_ComponentLoadFailed( MediaProtectionManager sender, ComponentLoadFailedEventArgs e ) { TestLogger.LogMessage("Enter Playback.ProtectionManager_ComponentLoadFailed()" ); TestLogger.LogMessage( e.Information.ToString() ); // List the failing components - RevocationAndRenewalInformation for ( int i = 0; i < e.Information.Items.Count; i++ ) { TestLogger.LogMessage(e.Information.Items[i].Name + "\nReasons=0x" + e.Information.Items[i].Reasons + "\n" + "Renewal Id=" + e.Information.Items[i].RenewalId ); } e.Completion.Complete( false ); TestLogger.LogMessage("Leave Playback.ProtectionManager_ComponentLoadFailed()" ); }
Most of the events in this section just print a log message, the MediaFailed and MediaEnded methods indicate that playback has stopped, and unhook the playback event handlers. In addition, the ProtectionManager_ComponentLoadFailed method sets the ComponentLoadFailedEventArgs.Completion property to false.
Add the following service request methods to the end of the Playback class:
void ProtectionManager_ServiceRequested( MediaProtectionManager sender, ServiceRequestedEventArgs srEvent ) { TestLogger.LogMessage("Enter Playback.ProtectionManager_ServiceRequested()" ); _serviceCompletionNotifier = srEvent.Completion; IPlayReadyServiceRequest serviceRequest = ( IPlayReadyServiceRequest )srEvent.Request; TestLogger.LogMessage("Service request type = " + serviceRequest.GetType()); _requestChain = new RequestChain( serviceRequest ); _requestChain.RequestConfigData = this.RequestConfigData; _requestChain.FinishAndReportResult( new ReportResultDelegate(HandleServiceRequest_Finished)); TestLogger.LogMessage("Leave Playback.ProtectionManager_ServiceRequested()" ); } void HandleServiceRequest_Finished(bool bResult) { TestLogger.LogMessage("Enter Playback.HandleServiceRequest_Finished()" ); TestLogger.LogMessage("MediaProtectionServiceCompletion.Complete = " + bResult.ToString() ); _serviceCompletionNotifier.Complete( bResult ); TestLogger.LogMessage("Leave Playback.HandleServiceRequest_Finished()" ); }
The ProtectionManager_ServiceRequest method sets up the protection manager service requests and completion notifications. The HandleServiceRequest_Finished method furnishes the results of the service request completion.
Add the following class after the end of the Playback class:
public class PlaybackAndReportResult : Playback { ReportResultDelegate _reportResult = null; string _strExpectedError = null; public PlaybackAndReportResult( ReportResultDelegate callback, string strExpectedError = null ) { _reportResult = callback; _strExpectedError = strExpectedError; } override protected void MediaEnded( object sender, RoutedEventArgs e ) { TestLogger.LogMessage("Enter PlaybackAndReportResult.MediaEnded()" ); base.MediaEnded(sender, e); _reportResult( true ); TestLogger.LogMessage("Leave PlaybackAndReportResult.MediaEnded()" ); } override protected void MediaFailed(object sender, ExceptionRoutedEventArgs e) { TestLogger.LogMessage("Enter PlaybackAndReportResult.MediaFailed()" ); base.MediaFailed(sender, e); bool bHandled = false; if( _strExpectedError != null ) { if ( e.ErrorMessage.ToLower().Contains( _strExpectedError.ToLower() ) ) { TestLogger.LogMessage( "'" + e.ErrorMessage + "' Contains " + _strExpectedError + " as expected" ); bHandled = true; } } _reportResult( bHandled ); TestLogger.LogMessage("Leave PlaybackAndReportResult.MediaFailed()" ); } }
The methods in the PlaybackAndReportResult class report the results of the media playback, that is, whether the media has finished playing back or whether the a failure occurred while the media was being played.
From the FILE menu, choose Save All to save your work so far.
This walkthrough continues in Simple C# PlayReady App Walkthrough - Part 2.