Share via


Simple C# PlayReady App Walkthrough - Part 2

This section describes how to set up the simple PlayReady protected Windows store app to individualize a device, which will allow licenses to be bound to that device. In addition, this section describes how add license acquisition capabilities to the PlayReady protected app.

Individualizing the Device

Inidividualization must be performed before an app can play back PlayReady protected content. An app must be individualized at least once. However, an app does not need to be individualized every time it is started.

Individualization can be performed in two different ways: proactively or reactively. The simple PlayReady example shows how to perform individualization reactively. That is, the sample waits for a callback from the server that indicates individualization is required before the media can be used. This prevents any round-trip latency to the individualization server if it is not necessary.

To add individualization capabilities to the app

  1. In Solution Explorer, open the shortcut menu for the project and choose Add, New Item.

  2. In the Add New Item dialog box, under Visual C#, choose Code.

  3. In the Templates pane, choose Code File.

  4. Change the name to Indiv.cs, and then choose the Add button.

  5. Add the following directives to the beginning of the file:

    using System;
    using Windows.Foundation;
    using Microsoft.Media.PlayReadyClient;
    
    
  6. Add the PlayReady_CS_RTM_Windows_Store_app namespace with the ReportResultDelegate delegate after the directives:

    namespace PlayReady_CS_RTM_Windows_Store_app
    {
      public delegate void ReportResultDelegate( bool bResult );
    }
    
    
  7. Add the the following Indiv class after the delegate:

    public class Indiv :ServiceRequest
    {
      protected virtual void IndivServiceRequestCompleted( PlayReadyIndividualizationServiceRequest  sender, Exception hrCompletionStatus )
      {
      }
    
      async public void  IndivReactively(PlayReadyIndividualizationServiceRequest indivRequest)
      {
        TestLogger.LogMessage("Enter Indiv.IndivReactively()" );
        Exception exception = null;
    
        try
        {
          _serviceRequest = indivRequest;
    
          TestLogger.LogMessage("Begin indiv service request..." );
          await indivRequest.BeginServiceRequest();
        }
        catch ( Exception ex )
        {
          TestLogger.LogMessage("Saving exception.." );
          exception = ex;
        }
        finally
        {
          IndivServiceRequestCompleted( indivRequest, exception );
        }
    
        TestLogger.LogMessage("Leave Indiv.IndivReactively()" );
      }
    
    }
    
    

    The Indiv class contains methods that allow you to handle any cleanup after an individualization request has completed and methods for reactively performing the individualization. Individualization of a device does not need to occur each time the media is played back, therefore handling individualization reactively saves the latency inherit in proactively calling the individualization server whenever the media is played again.

    When the asynchronous IndivReactively method is called, the await operator suspends this method. IndivReactively cannot continue until BeginServiceRequest is completed. Meanwhile, control returns to the caller of IndivReactively. Control resumes here once BeginServiceRequest is completed. Any exceptions are then passed to the IndivServiceRequestCompleted method.

  8. Add the following class after the end of the Indiv class:

    public class IndivAndReportResult : Indiv
    {
      ReportResultDelegate _reportResult = null;
    
      public IndivAndReportResult( ReportResultDelegate callback)
      {
        _reportResult = callback;
      }
    
      protected override void IndivServiceRequestCompleted( PlayReadyIndividualizationServiceRequest  sender, Exception hrCompletionStatus )
      {
        TestLogger.LogMessage("Enter IndivAndReportResult.IndivServiceRequestCompleted()" );
    
        if( hrCompletionStatus == null )
        {
          TestLogger.LogMessage("********************************************Indiv succeeded**************************************************");
          _reportResult( true );
        }
        else
        {
          if( !HandleExpectedError(hrCompletionStatus) )
          {
            TestLogger.LogError( "IndivServiceRequestCompleted ERROR: " + hrCompletionStatus.ToString());
            _reportResult( false );
          }
        }
    
        TestLogger.LogMessage("Leave IndivAndReportResult.IndivServiceRequestCompleted()" );
      }
    }
    
    

    The IndivAndReportResult class contains methods to report whether the individualization process succeeded or failed.

  9. From the FILE menu, choose Save All.

License Acquisition

Before media content can be played back in a PlayReady protected Windows Store app, the app must first acquire a license that allows it to play back the content. A license defines the usage rights and restrictions that the content provider has specified for the content.

To add license acquisition service request capabilities

  1. In Solution Explorer, open the shortcut menu for the project and choose Add, New Item.

  2. In the Add New Item dialog box, under Visual C#, choose Code.

  3. In the Templates pane, choose Code File.

  4. Change the name to LicenseAcquisition.cs, and then choose the Add button.

  5. Add the following directives to the beginning of the file:

    using System;
    using Windows.Foundation;
    using Microsoft.Media.PlayReadyClient;
    
    
  6. Add the PlayReady_CS_RTM_Windows_Store_app namespace after the directives:

    namespace PlayReady_CS_RTM_Windows_Store_app
    {
    
    }
    
    
  7. Add the following class to the PlayReady_CS_RTM_Windows_Store_app namespace:

    public class LicenseAcquisition : ServiceRequest
    {
    
    }
    
    
  8. Add the following code to the LicenseAcquisition class:

    
    protected virtual void LAServiceRequestCompleted( PlayReadyLicenseAcquisitionServiceRequest  sender, Exception hrCompletionStatus )
    {
    }
    
    static public void DumpContentHeaderValues(PlayReadyContentHeader contentHeader)
    {
      TestLogger.LogMessage(" " );
      TestLogger.LogMessage("Content header values:" );
      if( contentHeader == null )
      {
        return;
      }
      TestLogger.LogMessage("CustomAttributes :" + contentHeader.CustomAttributes );
      TestLogger.LogMessage("DecryptorSetup   :" + contentHeader.DecryptorSetup.ToString() );
      TestLogger.LogMessage("DomainServiceId  :" + contentHeader.DomainServiceId.ToString() );
      TestLogger.LogMessage("EncryptionType   :" + contentHeader.EncryptionType.ToString() );
      TestLogger.LogMessage("KeyId            :" + contentHeader.KeyId.ToString() );
      TestLogger.LogMessage("KeyIdString      :" + contentHeader.KeyIdString );
      TestLogger.LogMessage("LicenseAcquisitionUrl :" + contentHeader.LicenseAcquisitionUrl.ToString() );
    }
    
    

    The LAServiceRequestCompleted method allows you to handle any cleanup after a license acquisition service request has completed (in this class it does nothing, but it is overridden by the LAAndReportResult.LAServiceRequestCompleted method). The DumpContentHeaderValues method just displays the current values of the PlayReady content header in the main page's text box.

  9. Add the following lines after the DumpContentHeaderValues method:

    void ConfigureServiceRequest()
    {
      PlayReadyLicenseAcquisitionServiceRequest licenseRequest = _serviceRequest as PlayReadyLicenseAcquisitionServiceRequest;
    
      DumpContentHeaderValues( licenseRequest.ContentHeader );
    
      TestLogger.LogMessage(" " );
      TestLogger.LogMessage("Configure license request to these values:" );
    
      licenseRequest.Uri = new Uri(MainPage.LAURL);
    
      TestLogger.LogMessage("ChallengeCustomData:" + "Custom Data" );
      licenseRequest.ChallengeCustomData = "Custom Data";
    
      TestLogger.LogMessage(" " );
    }
    
    

    This method takes the PlayReady content header data and adds the PlayReady license acquisition URL and any custom data to the service request.

  10. Add the following lines after the ConfigureServiceRequest method:

    async public void  AcquireLicenseReactively(PlayReadyLicenseAcquisitionServiceRequest licenseRequest)
    {
      TestLogger.LogMessage("Enter LicenseAcquisition.AcquireLicenseReactively()" );
      Exception exception = null;
    
      try
      {
        _serviceRequest = licenseRequest;
        ConfigureServiceRequest();
    
        TestLogger.LogMessage("ChallengeCustomData = " + licenseRequest.ChallengeCustomData);
        TestLogger.LogMessage("Begin license acquisition service request..." );
        await licenseRequest.BeginServiceRequest();
      }
      catch( Exception ex )
      {
        TestLogger.LogMessage("Saving exception.." );
        exception = ex;
      }
      finally
      {
        TestLogger.LogMessage("Post-LicenseAcquisition Values:");
        TestLogger.LogMessage("DomainServiceId          = " + licenseRequest.DomainServiceId.ToString());
        if( exception == null )
        {
          TestLogger.LogMessage("ResponseCustomData       = " + licenseRequest.ResponseCustomData);
        }
        DumpContentHeaderValues( licenseRequest.ContentHeader );
    
        LAServiceRequestCompleted( licenseRequest, exception );
      }
    
      TestLogger.LogMessage("Leave LicenseAcquisition.AcquireLicenseReactively()" );
    }
    
    

    If you were going to acquire a license reactively you would call the AcquireLicenseReactively method. When the asynchronous AcquireLicenseReactively method is called, the await operator suspends this method. AcquireLicenseReactively cannot continue until the BeginServiceRequest method is completed. Meanwhile, control returns to the caller of AcquireLicenseReactively. Control resumes here once the BeginServiceRequest method is completed. The PlayReady content header values are displayed on the main page text box and any exceptions are then passed to the LAServiceRequestCompleted method.

  11. Add the following class after the end of the LicenseAcquisition class:

    public class LAAndReportResult : LicenseAcquisition
    {
      ReportResultDelegate _reportResult = null;
      string _strExpectedError = null;
    
      public string ExpectedError
      {
        set { this._strExpectedError =  value; }
        get { return this._strExpectedError; }
      }
    
      public LAAndReportResult( ReportResultDelegate callback)
      {
        _reportResult = callback;
      }
    
      protected override void LAServiceRequestCompleted( PlayReadyLicenseAcquisitionServiceRequest  sender, Exception hrCompletionStatus )
      {
        TestLogger.LogMessage("Enter LAAndReportResult.LAServiceRequestCompleted()" );
    
        if( hrCompletionStatus == null )
        {
          TestLogger.LogMessage("************************************    License acquisition succeeded       ****************************************");
          _reportResult( true );
        }
        else
        {
          if( !HandleExpectedError(hrCompletionStatus) )
          {
            TestLogger.LogError( "LAServiceRequestCompleted ERROR: " + hrCompletionStatus.ToString() );
            _reportResult( false );
          }
        }
    
        TestLogger.LogMessage("Leave LAAndReportResult.LAServiceRequestCompleted()" );
      }
    
      protected override bool HandleExpectedError(Exception ex)
      {
        TestLogger.LogMessage("Enter LAAndReportResult.HandleExpectedError()" );
    
        if( string.IsNullOrEmpty( _strExpectedError ) )
        {
          TestLogger.LogMessage("Setting error code to " + RequestConfigData.ExpectedLAErrorCode );
          _strExpectedError = RequestConfigData.ExpectedLAErrorCode;
        }
    
        bool bHandled = false;
        if( _strExpectedError != null )
        {
          if ( ex.Message.ToLower().Contains( _strExpectedError.ToLower() ) )
          {
            TestLogger.LogMessage( "'" + ex.Message + "' Contains " + _strExpectedError + "  as expected" );
            bHandled = true;
            _reportResult( true );
          }
        }
    
        TestLogger.LogMessage("Leave LAAndReportResult.HandleExpectedError()" );
        return bHandled;
      }
    }
    
    

    The LAAndReportResult class contains methods to report whether the license acquisition service request succeeded or failed.

  12. From the FILE menu, choose Save All.

This walkthrough continues in Simple C# PlayReady App Walkthrough - Part 3.