Application de lecture Audio/Vidéo pour Windows 8 et Windows Phone 8 utilisant les services de la classe HttpClient pour l’acquisition de la licence PlayReady

Introduction

Cet article décrit comment créer des applications Windows 8 et Windows Phone 8 capables de jouer des vidéos Smooth Streaming protégées par PlayReady.  Avec les exemples d’application fournis avec cet article l’utilisateur peut définir:
- l’URL de la vidéo Smooth Streaming,
- l’URL utilisée pour l’acquisition de la licence PlayReady,
- le « Challenge Custom Data » transmis au serveur PlayReady pour acquérir la licence PlayReady.
Si l’URL utilisée pour l’acquisition de licence n’est pas définie, l’application utilisera l’URL inclue dans l’en-tête de protection PlayReady du manifeste de la vidéo.
                                                                    clip_image002

                                     clip_image004

Les étapes pour l’acquisition d’une licence PlayReady pour les applications du Windows Store:

Avant de décrire comment le client PlayReady acquiert la licence PlayReady, quelques mots sur le mécanisme d’individualisation. Chaque client PlayReady est associé à un unique certificat et à une clé unique, l’installation de ce certificat et de cette clé s’appelle l’individualisation.
De plus avant que le client PlayReady ne demande une licence pour décrypter le contenu, il doit d’abord déterminer si le logiciel PlayReady ad hoc est installé sur l’ordinateur de l’utilisateur. Ce logiciel est appelé “Individualized Black boX” (appelé aussi IBX) et est le composant PlayReady client nécessaire pour pouvoir jouer des contenus protégés par PlayReady. Ce composant logiciel permet à l’application cliente de demander et d’utiliser une licence PlayReady.
Le client PlayReady pour les applications du Windows Store supporte l’individualisation dynamique: l’IBX est télélchargé depuis le serveur d’individualisation. Dans votre application Windows Store vous pouvez implémenter une demande d’individualisation réactive ou une demande d’individualisation pro-active.

  • · La demande pro-active d’individualisation peut être transmise après l’installation de l’application ou quand l’utilisateur lance l’application pour la première fois.
  • · La demande réactive d’individualisation est automatiquement préparée par le client PlayReady quand l’application est sur le point de jouer un contenu protégé par PlayReady et si l’IBX n’est pas déjà installé sur l’ordinateur.

Le schéma ci-dessous décrit l’acquisition d’une licence PlayReady pour jouer un contenu protégé par PlayReady avec la transmission d’une requête réactive d’individualisation préalable: 
clip_image006

Le schéma ci-dessous décrit l’acquisition d’une licence PlayReady pour jouer un contenu protégé par PlayReady sans transmission d’une requête d’individualisation comme l’IBX est déjà installé: 
clip_image008

Le schéma ci-dessous décrit les échanges entre le client et le serveur quand l’application joue un contenu protégé par PlayReady quand l’IBX est déjà installé et la licence PlayReady déjà acquise: 
clip_image010

Librairie Http Client:

Les exemples d’application PlayReady fournis avec cet article sont basés sur Player Framework V1.2 et l’acquisition des licences PlayReady est basée sur la classe HttpClient pour les deux plates-formes Windows et Windows Phone.  
HttpClient fait partie intégrante de NET Framework 4.5 et des applications Windows Store et fournit aux développeurs la possibilité d’écrire très facilement des applications se connectant à des services Internet incluant les services basés sur des APIs REST. HttpClient est maintenant disponible pour les développeurs Windows Phone si vous installez le package Nuget : « Microsoft http Client Libraries »
clip_image012

Générer les exemples d’application

Pour générer les exemples d’application PlayReady pour Windows Store et Windows Phone vous devez disposer:
1. d’un ordinateur sous Windows 8,
2. du Visual Studio 2012,
3. du Windows Phone 8 SDK.

Pour générer l’application pour Windows Store vous devez installer:
1. Le Smooth Streaming Client SDK for Windows 8
2. Le PlayReady Client SDK for Windows Store Apps
3. Le Player Framework for Windows 8 and Windows Phone 8 (v1.2).

Pour générer l’application pour Windows Phone vous devez installer:
1. Le Microsoft Smooth Streaming Client 2.0 RTW
2. Le Player Framework for Windows 8 and Windows Phone 8 (v1.2).
L’application pour Windows Phone 8 est livrée avec les packages Nuget suivants:
1. Microsoft HTTP Client Libraries: Microsof.Net.Http 
2. Microsoft BCL Portability Pack: Microsoft.BCL
3. Microsoft BCL Build Components: Microsoft.BCL.Build

Installer le Microsoft Smooth Streaming Client SDK pour Windows 8

1. Téléchargez le SDK client Smooth Streaming depuis ce lien:
https://visualstudiogallery.msdn.microsoft.com/04423d13-3b3e-4741-a01c-1ae29e84fea6
2. Double-cliquez sur le fichier VSIX pour lancer l’installation
clip_image014
3. Sur la boîte de dialogue « VSIX Installer » cliquez sur le bouton Install
clip_image016
4. Après quelques secondes, le SDK est installé. Cliquez sur le bouton Close pour fermer la boîte de dialogue « VSIX Installer ».
clip_image018

Installer le Microsoft PlayReady Client SDK for Windows Store Apps

1. Téléchargez le SDK PlayReady Client depuis ce lien:
https://visualstudiogallery.msdn.microsoft.com/e02ccac7-f3eb-4b53-b11a-c657d5631483
2. Double-cliquez sur le fichier VSIX pour lancer l’installation
clip_image020
3. Sur la boîte de dialogue « VSIX Installer » cliquez sur le bouton Install
clip_image022
4. Après quelques secondes, le SDK est installé. Cliquez sur le bouton Close pour fermer la boîte de dialogue « VSIX Installer ».
clip_image024

Installer le Microsoft Smooth Streaming Client 2.0 RTW

1. Téléchargez le SDK Smooth Streaming Client depuis ce ligne:
https://www.microsoft.com/en-us/download/details.aspx?id=29940
2. Double-cliquez sur le fichier MSI pour lancer l’installation
clip_image026
3. Sur la boîte de dialogue « Microsoft Smooth Streaming Client 2.0 Setup » cliquez sur le bouton Next
clip_image028
4. Sur la boîte de dialogue « Microsoft Smooth Streaming Client 2.0 Setup » cliquez sur le bouton Next
clip_image030
5. Sur la boîte de dialogue « Microsoft Smooth Streaming Client 2.0 Setup » cliquez sur le bouton Next
clip_image032
6. Sur la boîte de dialogue « Microsoft Smooth Streaming Client 2.0 Setup » cliquez sur le bouton Install
clip_image034
7. Sur la boîte de dialogue « Microsoft Smooth Streaming Client 2.0 Setup » cliquez sur le bouton Finish
clip_image036

Installer le Player Framework pour Windows 8 et Windows Phone 8 (v1.2)

1. Téléchargez le Player Framework depuis ce lien:
https://playerframework.codeplex.com/releases/view/105214
2. Double-cliquez sur le fichier VSIX pour lancer l’installation
clip_image038
3. On the VSIX Installer dialog box click on the Install button
clip_image040
4. Après quelques secondes, le SDK est installé. Cliquez sur le bouton Close pour fermer la boîte de dialogue.
clip_image042

Ouvrir et Générer la solution

Dès que les SDKs sont installés, vous pouvez ouvrir le fichier Solution pour générer les applications pour Windows Store et Windows Phone.

1. Démarrez Visual Studio 2012 et sélectionnez File > Open > Project/Solution.

2. Déplacez-vous dans le répertoire dans lequel vous avez décompressé le fichier ZIP et double-cliquez sur le fichier solution Microsoft Visual Studio.
clip_image044

3. Vérifiez qu’il n’y a pas d’avertissement pour les références Windows 8.
clip_image046

4. Vérifiez qu’il n’y a pas d’avertissement pour les références Windows Phone 8.
clip_image048

5. Les deux projets utilisent un code source C# commun pour l’acquisition de la license PlayReady : CommonLicenseRequest.cs
clip_image050

6. Le fichier CommonLicenseRequest.cs a été partagé entre les deux projets en utilisant l’option Add As Link pour l’ajout d’élément existant dans le projet.
clip_image052

7. Appuyez sur F7 ou utilisez Build > Build Solution pour générer les exemples.

Exploration du projet de l’application Windows Store

Le projet de l’application Windows Store est un projet XAML. 

clip_image054

  • MainPage.cs contient le code source de la page principale.
  • CommonLicenseRequest.cs contient le code source de la classe commune utilisant HttpClient pour l’acquisition de la licence PlayReady.
  • ApplicationConfiguration.cs contient le code source pour stocker les paramètres de l’application.

Les événements PlayReady et Smooth Streaming sont enregistrés dans le constructeur de la classe MainPage. 

 public MainPage()
{
    this.InitializeComponent();

    // Add Plugins
    Player.Plugins.Add(new Microsoft.PlayerFramework.Adaptive.AdaptivePlugin());

    // Init PlayReady Protection Manager
    var protectionManager = new MediaProtectionManager();
    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}");
    this.Player.ProtectionManager = protectionManager;
    // PlayReady Events registration
    Player.ProtectionManager.ComponentLoadFailed += ProtectionManager_ComponentLoadFailed;
    Player.ProtectionManager.ServiceRequested += ProtectionManager_ServiceRequested;


    // Init Smooth Streaming Handler
    Windows.Media.MediaExtensionManager extensions = new Windows.Media.MediaExtensionManager();
    extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".ism", "text/xml");
    extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".isml", "text/xml");
    extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".ism", "application/vnd.ms-ss");
    extensions.RegisterByteStreamHandler("Microsoft.Media.AdaptiveStreaming.SmoothByteStreamHandler", ".isml", "application/vnd.ms-ss");
    extensions.RegisterSchemeHandler("Microsoft.Media.AdaptiveStreaming.SmoothSchemeHandler", "ms-sstr:");
    Player.MediaExtensionManager = extensions;

La méthode ProtectionManager_ServiceRequested est appelée à chaque demande d’acquisition de licence ou à chaque demande d’invidualisation. S’il s’agit d’une demande d’individualisation la méthode ReactiveIndivRequest est appelée. S’il s’agit d’une demande d’acquisition de licence la méthode LicenseAcquisitionRequest est appelée.    

 async void ProtectionManager_ServiceRequested(MediaProtectionManager sender, ServiceRequestedEventArgs srEvent)
{
    LogMessage("ProtectionManager ServiceRequested");
    if (srEvent.Request is PlayReadyIndividualizationServiceRequest)
    {
        PlayReadyIndividualizationServiceRequest IndivRequest = srEvent.Request as PlayReadyIndividualizationServiceRequest;
        bool bResultIndiv = await ReactiveIndivRequest(IndivRequest, srEvent.Completion);
    }
    else if (srEvent.Request is PlayReadyLicenseAcquisitionServiceRequest)
    {
        PlayReadyLicenseAcquisitionServiceRequest licenseRequest = srEvent.Request as PlayReadyLicenseAcquisitionServiceRequest;
        LicenseAcquisitionRequest(licenseRequest, srEvent.Completion, PlayReadyLicenseUrl, PlayReadyChallengeCustomData);
    }
}

Si l’URL utilisée pour l’acquisition de licence PlayReady n’est pas précisée par l’utilisateur, l’application utilisera l’URL définie dans l’entête de protection PlayReady du manifeste de la vidéo, la méthode LicenseAcquisitionRequest appelle licenseRequest.BeginServiceRequest. Si l’URL utilisée pour l’acquisition de licence PlayReady est précisée par l’utilisateur, la méthode LicenseAcquisitionRequest crée un challenge PlayReady en utilisant la valeur du champ ChallengeCustomData et appelle la méthode licenseAcquisition.AcquireLicense en utilisant l’URL définie par l’utilisateur.

 /// <summary>
/// Invoked to acquire the PlayReady License
/// </summary>
async void LicenseAcquisitionRequest(PlayReadyLicenseAcquisitionServiceRequest licenseRequest, MediaProtectionServiceCompletion CompletionNotifier, string Url, string ChallengeCustomData)
{
    bool bResult = false;
    string ExceptionMessage = string.Empty;

    try
    {
        if (!string.IsNullOrEmpty(Url))
        {
            LogMessage("ProtectionManager PlayReady Manual License Acquisition Service Request in progress - URL: " + Url);

            if (!string.IsNullOrEmpty(ChallengeCustomData))
            {
                System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
                byte[] b = encoding.GetBytes(ChallengeCustomData);
                licenseRequest.ChallengeCustomData = Convert.ToBase64String(b, 0, b.Length);
            }

            PlayReadySoapMessage soapMessage = licenseRequest.GenerateManualEnablingChallenge(); 
            
            byte[] messageBytes = soapMessage.GetMessageBody();
            HttpContent httpContent = new ByteArrayContent(messageBytes);

            IPropertySet propertySetHeaders = soapMessage.MessageHeaders;
            foreach (string strHeaderName in propertySetHeaders.Keys)
            {
                string strHeaderValue = propertySetHeaders[strHeaderName].ToString();

                // The Add method throws an ArgumentException try to set protected headers like "Content-Type"
                // so set it via "ContentType" property
                if (strHeaderName.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
                    httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse(strHeaderValue);
                else
                    httpContent.Headers.Add(strHeaderName.ToString(), strHeaderValue);
            }
            CommonLicenseRequest licenseAcquision = new CommonLicenseRequest();
            HttpContent responseHttpContent = await licenseAcquision.AcquireLicense(new Uri(Url), httpContent);
            if (responseHttpContent != null)
            {
                Exception exResult = licenseRequest.ProcessManualEnablingResponse(await responseHttpContent.ReadAsByteArrayAsync());
                if (exResult != null)
                {
                    throw exResult;
                }
                bResult = true;
            }
            else
                ExceptionMessage = licenseAcquision.GetLastErrorMessage();
        }
        else
        {
            LogMessage("ProtectionManager PlayReady License Acquisition Service Request in progress - URL: " + licenseRequest.Uri.ToString());
            await licenseRequest.BeginServiceRequest();
            bResult = true;
        }
    }
    catch (Exception e)
    {
        ExceptionMessage = e.Message;
    }
    
    if (bResult == true)
            LogMessage(!string.IsNullOrEmpty(Url) ? "ProtectionManager Manual PlayReady License Acquisition Service Request successful":
                "ProtectionManager PlayReady License Acquisition Service Request successful");
    else
        LogMessage(!string.IsNullOrEmpty(Url) ? "ProtectionManager Manual PlayReady License Acquisition Service Request failed: "+ExceptionMessage :
            "ProtectionManager PlayReady License Acquisition Service Request failed: " + ExceptionMessage);
    CompletionNotifier.Complete(bResult);
}

The method AcquireLicense sends the PlayReady license Http Request to the PlayReady server. The request contains the PlayReady challenge and add http headers. The method PostAsync of class HttpClient is used to transmit this request.

 /// <summary>
 /// Invoked to acquire the PlayReady license.
 /// </summary>
 /// <param name="licenseServerUri">License Server URI to retrieve the PlayReady license.</param>
 /// <param name="httpRequestContent">HttpContent including the Challenge transmitted to the PlayReady server.</param>
 public async virtual Task<HttpContent> AcquireLicense(Uri licenseServerUri, HttpContent httpRequestContent)
 {
     try
     {
         HttpClient httpClient = new HttpClient();
         httpClient.DefaultRequestHeaders.Add("msprdrm_server_redirect_compat", "false");
         httpClient.DefaultRequestHeaders.Add("msprdrm_server_exception_compat", "false");

         HttpResponseMessage response = await httpClient.PostAsync(licenseServerUri, httpRequestContent);
         response.EnsureSuccessStatusCode();

         if (response.StatusCode == HttpStatusCode.OK)
         {
             _lastErrorMessage = string.Empty;
             return response.Content;
         }
         else
         {
             _lastErrorMessage = "AcquireLicense - Http Response Status Code: " + response.StatusCode.ToString();
         }
     }
     catch (Exception exception)
     {
         _lastErrorMessage = exception.Message;
         return null;
     }
     return null;
 }

 

Exploration du projet de l’application Windows Phone 8

Le projet de l’application Windows Phone 8 est un projet XAML. 
clip_image056

  • · MainPage.cs contient le code source de la page principale.
  • · CommonLicenseRequest.cs contient le code source commun pour l’acquisition de la licence PlayReady en utilisant la classe HttpClient.  
  • · ApplicationConfiguration.cs contient le code source utilisé pour stocker et restaurer les paramètres de l’application.    
  • · WindowsPhoneLicenseAcquisition.cs contient le code source de la classe WindowsPhoneLicenseAcquisition dérivée de la classe PlayReady LicenseAcquirer utilisée pour l’acquisition de licence PlayReady.

Dans le constructeur de la page principale, les événements PlayReady sont enregistrés par la classe WindowsPhoneLicenseAcquisition.  

 //Add Smooth Streaming and Caption plugins 
Microsoft.PlayerFramework.Adaptive.AdaptivePlugin AP = new Microsoft.PlayerFramework.Adaptive.AdaptivePlugin();
Player.Plugins.Add(AP);
Player.Plugins.Add(new Microsoft.PlayerFramework.TimedText.CaptionsPlugin());
Microsoft.PlayerFramework.Adaptive.AdaptivePlugin ap = Player.Plugins.OfType<Microsoft.PlayerFramework.Adaptive.AdaptivePlugin>().FirstOrDefault();
if (ap != null)
{
    ap.MediaElement.LicenseAcquirer = new WindowsPhoneLicenseAcquisition(LogMessage);
    ap.MediaElement.MediaFailed += Player_MediaFailed;
}

La méthode OnAcquireLicense est appelée pour acquérir la licence PlayReady. Si LicenseServerUriOverride est défini il s’agit d’une acquisition de licence manuelle. La méthode converti la Stream qui contient le challenge PlayReady en un objet HttpContent avant d’appeler _licenseRequest.AcquireLicense qui est la méthode commune pour l’acquisition de licence PlayReady pour les applications Windows Phone et Windows Store.

 protected async override void OnAcquireLicense(System.IO.Stream licenseChallenge, Uri licenseServerUri)
{

    // 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)
    {
        if (_LogMessage != null) _LogMessage("PlayReady License Acquisition Service Request in progress - URL: " + licenseServerUri.ToString());
        resolvedLicenseServerUri = licenseServerUri;
    }
    else
    {
        if (_LogMessage != null) _LogMessage("PlayReady Manual License Acquisition Service Request in progress - URL: " + LicenseServerUriOverride.ToString());
        resolvedLicenseServerUri = LicenseServerUriOverride;
    }

    try
    {
        StreamReader sr = new StreamReader(licenseChallenge);
        string challengeString = sr.ReadToEnd();
        Byte[] Data = System.Text.Encoding.UTF8.GetBytes(challengeString);
        HttpContent httpRequestContent = new ByteArrayContent(Data);

        httpRequestContent.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
        httpRequestContent.Headers.ContentLength = Data.Length;


        HttpContent httpResponseContent = await _licenseRequest.AcquireLicense(resolvedLicenseServerUri, httpRequestContent);
        if (httpResponseContent != null)
        {
            Stream srep = await httpResponseContent.ReadAsStreamAsync();
            if (srep != null)
            {
                if (_LogMessage != null) _LogMessage("PlayReady License Acquisition successful.");
                SetLicenseResponse(srep);
                return;
            }
        }
        else
            if (_LogMessage != null) _LogMessage("PlayReady License Acquisition failed. Exception: " + _licenseRequest.GetLastErrorMessage());
    }
    catch(Exception e)
    {
        if (_LogMessage != null) _LogMessage("PlayReady License Acquisition failed. Exception: " + e.Message);
    }
    
    if (_LogMessage != null) _LogMessage("PlayReady License Acquisition failed.");
    // Call CancelAsync 
    // In that case the MediaElement transitions from the AcquiringLicense state to the error state and
    // calling this method causes playback to fail with a "license acquisition failure" error.
    try
    {
        CancelAsync();
    }
    catch
    {
    }

}

Tests des applications Windows Store and Windows Phone

Par défaut les applications essaieront de jouer le contenu Smooth Streaming suivant:
https://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism/Manifest
en utilisant l’URL suivante pour l’acquisition de licence:
https://playready.directtaps.net/pr/svc/rightsmanager.asmx?PlayRight=1&UseSimpleNonPersistentLicense=1
Cette URL renverra une licence non persistante. A chaque tentative de lecture du contenu smooth streaming, une requête sera transmise au serveur PlayReady.
Par défaut, le challenge custom data est vide, vous devez modifier ce champ si le serveur PlayReady attend un challenge custom data spécifique.
Licence persistante:
Si vous utilisez l’URL suivante, la licence retournée par le serveur PlayReady sera persistante:
https://playready.directtaps.net/pr/svc/rightsmanager.asmx

Téléchargement des projets

Téléchargement:              C# (3.3 MB)
Prérequis:                         Windows 8, Visual Studio 2012, Windows Phone 8 SDK
Dernière modification:    10/18/2013
Licence:                            MS-LPL
Technologies:                   PlayReady, Smooth Streaming, PlayerFramework, C#, XAML
Thème:                             PlayReady Smooth Streaming Player for Windows Phone and Windows Store