Streaming adaptatif avec PlayReadyAdaptive streaming with PlayReady

Cet article décrit comment ajouter le streaming adaptatif de contenu multimédia avec la protection de contenu Microsoft PlayReady à une application UWP.This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a Universal Windows Platform (UWP) app.

Cette fonctionnalité prend actuellement en charge la lecture de contenu en streaming dynamique sur HTTP (DASH).This feature currently supports playback of Dynamic streaming over HTTP (DASH) content.

HLS (streaming HTTP d’Apple) n’est pas pris en charge par PlayReady.HLS (Apple's HTTP Live Streaming) is not supported with PlayReady.

Smooth Streaming n’est pas non plus pris en charge en mode natif pour le moment. Toutefois, PlayReady est extensible et, à l’aide de code ou de bibliothèques supplémentaires, il est possible de prendre en charge Smooth Streaming protégé par PlayReady en tirant parti de la gestion des droits numériques basée sur le logiciel ou même sur le matériel.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).

Cet article traite uniquement des aspects du streaming adaptatif propres à PlayReady.This article only deals with the aspects of adaptive streaming specific to PlayReady. Pour des informations plus générales sur l’implémentation du streaming adaptatif, voir treaming adaptatif.For information about implementing adaptive streaming in general, see Adaptive streaming.

Cet article utilise le code de l’exemple de streaming adaptatif dans le dépôt Windows-universal-samples de Microsoft sur GitHub.This article uses code from the Adaptive streaming sample in Microsoft's Windows-universal-samples repository on GitHub. Le scénario 4 porte sur l’utilisation du streaming adaptatif avec PlayReady.Scenario 4 deals with using adaptive streaming with PlayReady. Vous pouvez télécharger le dépôt dans un fichier ZIP en accédant au niveau racine du dépôt et en sélectionnant le bouton Télécharger le ZIP.You can download the repo in a ZIP file by navigating to the root level of the repository and selecting the Download ZIP button.

Vous avez besoin des instructions using suivantes :You will need the following using statements:

using LicenseRequest;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Foundation.Collections;
using Windows.Media.Protection;
using Windows.Media.Protection.PlayReady;
using Windows.Media.Streaming.Adaptive;
using Windows.UI.Xaml.Controls;

L’espace de noms LicenseRequest provient de CommonLicenseRequest.cs, un fichier PlayReady fourni par Microsoft aux détenteurs de licences.The LicenseRequest namespace is from CommonLicenseRequest.cs, a PlayReady file provided by Microsoft to licensees.

Vous devez déclarer plusieurs variables globales :You will need to declare a few global variables:

private AdaptiveMediaSource ams = null;
private MediaProtectionManager protectionManager = null;
private string playReadyLicenseUrl = "";
private string playReadyChallengeCustomData = "";

Vous devrez également déclarer la constante suivante :You will also want to declare the following constant:

private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;

Configuration du MediaProtectionManagerSetting up the MediaProtectionManager

Pour ajouter la protection de contenu PlayReady à votre application UWP, vous devez configurer un objet MediaProtectionManager.To add PlayReady content protection to your UWP app, you will need to set up a MediaProtectionManager object. Cette opération s’effectue lors de l’initialisation de votre objet AdaptiveMediaSource.You do this when initializing your AdaptiveMediaSource object.

Le code suivant définit un objet MediaProtectionManager :The following code sets up a MediaProtectionManager:

private void SetUpProtectionManager(ref MediaElement mediaElement)
{
    protectionManager = new MediaProtectionManager();

    protectionManager.ComponentLoadFailed += 
        new ComponentLoadFailedEventHandler(ProtectionManager_ComponentLoadFailed);

    protectionManager.ServiceRequested += 
        new ServiceRequestedEventHandler(ProtectionManager_ServiceRequested);

    PropertySet cpSystems = new PropertySet();

    cpSystems.Add(
        "{F4637010-03C3-42CD-B932-B48ADF3A6A54}", 
        "Windows.Media.Protection.PlayReady.PlayReadyWinRTTrustedInput");

    protectionManager.Properties.Add("Windows.Media.Protection.MediaProtectionSystemIdMapping", cpSystems);

    protectionManager.Properties.Add(
        "Windows.Media.Protection.MediaProtectionSystemId", 
        "{F4637010-03C3-42CD-B932-B48ADF3A6A54}");

    protectionManager.Properties.Add(
        "Windows.Media.Protection.MediaProtectionContainerGuid", 
        "{9A04F079-9840-4286-AB92-E65BE0885F95}");

    mediaElement.ProtectionManager = protectionManager;
}

Ce code peut simplement être copié sur votre application, dans la mesure où il est obligatoire pour la configuration de la protection de contenu.This code can simply be copied to your app, since it is mandatory for setting up content protection.

L’événement ComponentLoadFailed est déclenché en cas d’échec de chargement des données binaires.The ComponentLoadFailed event is fired when the load of binary data fails. Nous devons ajouter un gestionnaire d’événements pour gérer ceci, afin de signaler que le chargement ne s’est pas terminé :We need to add an event handler to handle this, signaling that the load did not complete:

private void ProtectionManager_ComponentLoadFailed(
    MediaProtectionManager sender, 
    ComponentLoadFailedEventArgs e)
{
    e.Completion.Complete(false);
}

De même, nous devons ajouter un gestionnaire d’événements pour l’événement ServiceRequested, qui est déclenché lorsqu’un service est demandé.Similarly, we need to add an event handler for the ServiceRequested event, which fires when a service is requested. Ce code vérifie le type de demande et réagit de façon appropriée :This code checks what kind of request it is, and responds appropriately:

private async void ProtectionManager_ServiceRequested(
    MediaProtectionManager sender, 
    ServiceRequestedEventArgs e)
{
    if (e.Request is PlayReadyIndividualizationServiceRequest)
    {
        PlayReadyIndividualizationServiceRequest IndivRequest = 
            e.Request as PlayReadyIndividualizationServiceRequest;

        bool bResultIndiv = await ReactiveIndivRequest(IndivRequest, e.Completion);
    }
    else if (e.Request is PlayReadyLicenseAcquisitionServiceRequest)
    {
        PlayReadyLicenseAcquisitionServiceRequest licenseRequest = 
            e.Request as PlayReadyLicenseAcquisitionServiceRequest;

        LicenseAcquisitionRequest(
            licenseRequest, 
            e.Completion, 
            playReadyLicenseUrl, 
            playReadyChallengeCustomData);
    }
}

Demandes de service d’individualisationIndividualization service requests

Le code suivant effectue de manière réactive une demande de service d’individualisation PlayReady.The following code reactively makes a PlayReady individualization service request. Nous transmettons la demande en tant que paramètre à la fonction.We pass in the request as a parameter to the function. Nous insérons l’appel dans un bloc try/catch, et s’il n’y a aucune exception, nous supposons que la demande s’est déroulée correctement :We surround the call in a try/catch block, and if there are no exceptions, we say the request completed successfully:

async Task<bool> ReactiveIndivRequest(
    PlayReadyIndividualizationServiceRequest IndivRequest, 
    MediaProtectionServiceCompletion CompletionNotifier)
{
    bool bResult = false;
    Exception exception = null;

    try
    {
        await IndivRequest.BeginServiceRequest();
    }
    catch (Exception ex)
    {
        exception = ex;
    }
    finally
    {
        if (exception == null)
        {
            bResult = true;
        }
        else
        {
            COMException comException = exception as COMException;
            if (comException != null && comException.HResult == MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED)
            {
                IndivRequest.NextServiceRequest();
            }
        }
    }

    if (CompletionNotifier != null) CompletionNotifier.Complete(bResult);
    return bResult;
}

Par ailleurs, nous pouvons effectuer de manière proactive une demande de service d’individualisation, auquel cas nous appelons la fonction suivante à la place du code appelant ReactiveIndivRequest dans ProtectionManager_ServiceRequested :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:

async void ProActiveIndivRequest()
{
    PlayReadyIndividualizationServiceRequest indivRequest = new PlayReadyIndividualizationServiceRequest();
    bool bResultIndiv = await ReactiveIndivRequest(indivRequest, null);
}

Demandes de service d’acquisition de licenceLicense acquisition service requests

Si au lieu de cela, il s’agit d’une demande PlayReadyLicenseAcquisitionServiceRequest, nous appelons la fonction suivante pour demander et obtenir la licence PlayReady.If instead the request was a PlayReadyLicenseAcquisitionServiceRequest, we call the following function to request and acquire the PlayReady license. Nous demandons à l’objet MediaProtectionServiceCompletion transmis d’indiquer si la demande a réussi ou non, et nous terminons la demande :We tell the MediaProtectionServiceCompletion object that we passed in whether the request was successful or not, and we complete the request:

async void LicenseAcquisitionRequest(
    PlayReadyLicenseAcquisitionServiceRequest licenseRequest, 
    MediaProtectionServiceCompletion CompletionNotifier, 
    string Url, 
    string ChallengeCustomData)
{
    bool bResult = false;
    string ExceptionMessage = string.Empty;

    try
    {
        if (!string.IsNullOrEmpty(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();

                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
        {
            await licenseRequest.BeginServiceRequest();
            bResult = true;
        }
    }
    catch (Exception e)
    {
        ExceptionMessage = e.Message;
    }

    CompletionNotifier.Complete(bResult);
}

Lancement d’AdaptiveMediaSourceInitializing the AdaptiveMediaSource

Enfin, vous aurez besoin d’une fonction pour initialiser AdaptiveMediaSource, créée à partir d’un URI et d’un MediaElement donnés.Finally, you will need a function to initialize the AdaptiveMediaSource, created from a given Uri and MediaElement. L’URI doit être le lien vers le fichier multimédia (TLS ou DASH). L’élément MediaElement doit être défini dans votre code XAML.The Uri should be the link to the media file (HLS or DASH); the MediaElement should be defined in your XAML.

async private void InitializeAdaptiveMediaSource(System.Uri uri, MediaElement m)
{
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);
    if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
    {
        ams = result.MediaSource;
        SetUpProtectionManager(ref m);
        m.SetMediaStreamSource(ams);
    }
    else
    {
        // Error handling
    }
}

Vous pouvez appeler cette fonction dans n’importe quel événement gérant le début du streaming adaptatif, par exemple dans un événement de clic sur un bouton.You can call this function in whichever event handles the start of adaptive streaming; for example, in a button click event.

Voir aussiSee also