Flusso adattivo con PlayReadyAdaptive streaming with PlayReady

Questo articolo descrive come aggiungere flussi adattivi di contenuti multimediali con la protezione del contenuto Microsoft PlayReady a un’app UWP.This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a Universal Windows Platform (UWP) app.

Questa funzionalità supporta attualmente la riproduzione di contenuto DASH (Dynamic Streaming over HTTP).This feature currently supports playback of Dynamic streaming over HTTP (DASH) content.

HLS (lo streaming live HTTP di Apple) non è supportato con PlayReady.HLS (Apple's HTTP Live Streaming) is not supported with PlayReady.

Anche Smooth Streaming attualmente non è supportato in modo nativo. Tuttavia, PlayReady è estendibile e, usando un codice o librerie aggiuntive, Smooth Streaming protetto da PlayReady può essere supportato, sfruttando la tecnologia DRM (Digital Rights Management) software o addirittura hardware.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).

Questo articolo tratta solo gli aspetti relativi ai flussi adattivi specifici di PlayReady.This article only deals with the aspects of adaptive streaming specific to PlayReady. Per informazioni sull'implementazione dei flussi adattivi in generale, vedi Flusso adattivo.For information about implementing adaptive streaming in general, see Adaptive streaming.

Questo articolo usa il codice dell'esempio di flusso adattivo nel repository Windows-universal-samples di Microsoft su GitHub.This article uses code from the Adaptive streaming sample in Microsoft's Windows-universal-samples repository on GitHub. Lo scenario 4 illustra l'uso del flusso adattivo con PlayReady.Scenario 4 deals with using adaptive streaming with PlayReady. Puoi scaricare il repo in un file ZIP passando al livello radice del repository e selezionando il pulsante Download 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.

Dovrai aggiungere le istruzioni using seguenti: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;

Lo spazio dei nomi LicenseRequest deriva da CommonLicenseRequest.cs, un file PlayReady fornito da Microsoft ai licenziatari.The LicenseRequest namespace is from CommonLicenseRequest.cs, a PlayReady file provided by Microsoft to licensees.

Dovrai dichiarare alcune variabili globali:You will need to declare a few global variables:

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

È anche possibile dichiarare la costante seguente:You will also want to declare the following constant:

private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;

Configurazione di MediaProtectionManagerSetting up the MediaProtectionManager

Per aggiungere la protezione del contenuto PlayReady alla tua app UWP, devi configurare un oggetto MediaProtectionManager.To add PlayReady content protection to your UWP app, you will need to set up a MediaProtectionManager object. Questa operazione viene eseguita quando si inizializza l'oggetto AdaptiveMediaSource .You do this when initializing your AdaptiveMediaSource object.

Il codice seguente configura un oggetto 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;
}

Questo codice può essere semplicemente copiato nella tua app, dato che è obbligatorio per impostare la protezione del contenuto.This code can simply be copied to your app, since it is mandatory for setting up content protection.

L’evento ComponentLoadFailed viene generato quando il caricamento dei dati binari non riesce.The ComponentLoadFailed event is fired when the load of binary data fails. Dobbiamo aggiungere un gestore eventi per gestire il problema, così da segnalare che il caricamento non è stato completato: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);
}

Analogamente, dobbiamo aggiungere un gestore eventi per l’evento ServiceRequested, generato quando viene richiesto un servizio.Similarly, we need to add an event handler for the ServiceRequested event, which fires when a service is requested. Questo codice controlla il tipo di richiesta e risponde in modo appropriato: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);
    }
}

Richieste per il servizio di individualizzazioneIndividualization service requests

Il codice seguente effettua in modo reattivo una richiesta per il servizio di individualizzazione di PlayReady.The following code reactively makes a PlayReady individualization service request. Passiamo la richiesta come parametro per la funzione.We pass in the request as a parameter to the function. Racchiudiamo la chiamata in un blocco try/catch e, in assenza di eccezioni, possiamo dire che la richiesta è stata completata correttamente: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;
}

In alternativa, potremmo eseguire una richiesta per il servizio di individualizzazione in modo proattivo e in questo caso chiamiamo la funzione seguente al posto della chiamata di ReactiveIndivRequest nel codice in 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);
}

Richieste per il servizio di acquisizione della licenzaLicense acquisition service requests

Se invece la richiesta è PlayReadyLicenseAcquisitionServiceRequest, chiamiamo la funzione seguente per richiedere e acquisire la licenza di PlayReady.If instead the request was a PlayReadyLicenseAcquisitionServiceRequest, we call the following function to request and acquire the PlayReady license. Indichiamo all'oggetto MediaProtectionServiceCompletion che abbiamo passato se la richiesta è riuscita o meno e completiamo la richiesta: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);
}

Inizializzazione di AdaptiveMediaSourceInitializing the AdaptiveMediaSource

Infine, ti servirà una funzione per inizializzare AdaptiveMediaSource, creato da un oggetto Uri e un oggetto MediaElement specifici.Finally, you will need a function to initialize the AdaptiveMediaSource, created from a given Uri and MediaElement. L'oggetto Uri deve essere il link al file multimediale (HLS o DASH). MediaElement deve essere definito nel codice 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
    }
}

Puoi chiamare questa funzione in qualsiasi evento che gestisce l'inizio del flusso adattivo, ad esempio nell'evento Click per un pulsante.You can call this function in whichever event handles the start of adaptive streaming; for example, in a button click event.

Vedere ancheSee also