Adaptives Streaming mit PlayReadyAdaptive streaming with PlayReady

In diesem Artikel wird beschrieben, wie Sie einer UWP-App (Universelle Windows-Plattform) adaptives Streaming von Multimediainhalten mit Microsoft PlayReady-Inhaltsschutz hinzufügen.This article describes how to add adaptive streaming of multimedia content with Microsoft PlayReady content protection to a Universal Windows Platform (UWP) app.

Dieses Feature unterstützt derzeit die Wiedergabe von Dynamic Streaming over HTTP (DASH)-Inhalten.This feature currently supports playback of Dynamic streaming over HTTP (DASH) content.

HLS (Apple HTTP Live Streaming) wird mit PlayReady nicht unterstützt.HLS (Apple's HTTP Live Streaming) is not supported with PlayReady.

Smooth Streaming wird zurzeit ebenfalls nicht nativ unterstützt. PlayReady ist jedoch erweiterbar, und mithilfe von zusätzlichem Code oder zusätzlichen Bibliotheken kann PlayReady-geschütztes Smooth Streaming unterstützt werden, um die Nutzung von Software- oder sogar Hardware-DRM (Digital Rights Management) zu ermöglichen.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).

Dieser Artikel befasst sich nur mit den Aspekten des für PlayReady spezifischen adaptiven Streamings.This article only deals with the aspects of adaptive streaming specific to PlayReady. Informationen zur allgemeinen Implementierung des adaptiven Streamings finden Sie unter Adaptives Streaming.For information about implementing adaptive streaming in general, see Adaptive streaming.

In diesem Artikel wird Code aus dem Beispiel zu adaptivem Streaming im Microsoft-Repository Beispiele für die Universelle Windows-Plattform auf GitHub verwendet.This article uses code from the Adaptive streaming sample in Microsoft's Windows-universal-samples repository on GitHub. Szenario 4 behandelt adaptives Streaming mit PlayReady.Scenario 4 deals with using adaptive streaming with PlayReady. Sie können das Repository als ZIP-Datei herunterladen, indem Sie auf die Stammebene des Repository navigieren und die Schaltfläche ZIP-Datei herunterladen wählen.You can download the repo in a ZIP file by navigating to the root level of the repository and selecting the Download ZIP button.

Sie benötigen die folgenden using -Anweisungen: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;

Der Namespace LicenseRequest stammt aus CommonLicenseRequest.cs, einer PlayReady-Datei, die Lizenznehmern von Microsoft zur Verfügung gestellt wird.The LicenseRequest namespace is from CommonLicenseRequest.cs, a PlayReady file provided by Microsoft to licensees.

Sie müssen einige globale Variablen deklarieren:You will need to declare a few global variables:

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

Sie sollten auch die folgende Konstante deklarieren:You will also want to declare the following constant:

private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;

Einrichten von „MediaProtectionManager“Setting up the MediaProtectionManager

Um Ihrer UWP-App PlayReady-Inhaltsschutz hinzuzufügen, müssen Sie ein MediaProtectionManager-Objekt einrichten.To add PlayReady content protection to your UWP app, you will need to set up a MediaProtectionManager object. Diese Einrichtung erfolgt beim Initialisieren Ihres AdaptiveMediaSource-Objekts.You do this when initializing your AdaptiveMediaSource object.

Mit dem folgenden Code wird ein MediaProtectionManager-Objekt eingerichtet: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;
}

Dieser Code kann einfach in Ihre App kopiert werden, da er für die Einrichtung des Inhaltsschutzes erforderlich ist.This code can simply be copied to your app, since it is mandatory for setting up content protection.

Das ComponentLoadFailed-Ereignis wird ausgelöst, wenn das Laden von Binärdaten fehlschlägt.The ComponentLoadFailed event is fired when the load of binary data fails. Zum Behandeln dieses Ereignisses müssen wir einen Ereignishandler hinzufügen, der signalisiert, dass das Laden nicht abgeschlossen wurde: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);
}

Ebenso müssen wir einen Ereignishandler für das ServiceRequested-Ereignis hinzufügen, das beim Anfordern eines Diensts ausgelöst wird.Similarly, we need to add an event handler for the ServiceRequested event, which fires when a service is requested. Dieser Code überprüft, um welche Art von Anforderung es sich handelt, und reagiert entsprechend: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);
    }
}

IndividualisierungsdienstanforderungIndividualization service requests

Der folgende Code sendet reaktiv eine PlayReady-Individualisierungsdienstanforderung.The following code reactively makes a PlayReady individualization service request. Wir übergeben die Anforderung als Parameter an die Funktion.We pass in the request as a parameter to the function. Wir platzieren den Aufruf in einem try/catch-Block. Wenn keine Ausnahmen auftreten, wurde die Anforderung in unseren Augen erfolgreich abgeschlossen: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;
}

Alternativ soll vielleicht proaktiv eine Individualisierungsdienstanforderung gesendet werden. In diesem Fall wird anstelle des Codes, mit dem ReactiveIndivRequest in ProtectionManager_ServiceRequested aufgerufen wird, die folgende Funktion aufgerufen: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);
}

Lizenzerwerb-DienstanforderungenLicense acquisition service requests

Falls es sich bei der Anforderung stattdessen um PlayReadyLicenseAcquisitionServiceRequest handelt, rufen wir die folgende Funktion auf, um die PlayReady-Lizenz anzufordern und zu erwerben.If instead the request was a PlayReadyLicenseAcquisitionServiceRequest, we call the following function to request and acquire the PlayReady license. Wir teilen dem mediaschutzservicecompletion -Objekt mit, ob die Anforderung erfolgreich war, und wir führen die Anforderung aus: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);
}

Initialisieren von „AdaptiveMediaSource“Initializing the AdaptiveMediaSource

Schließlich benötigen Sie eine Funktion zum Initialisieren des AdaptiveMediaSource-Objekts, das aus einem bestimmten Uri- und MediaElement-Objekt erstellt wurde.Finally, you will need a function to initialize the AdaptiveMediaSource, created from a given Uri and MediaElement. Bei Uri sollte es sich um den Link zur Mediendatei (HLS oder DASH) handeln. MediaElement muss in Ihrem XAML-Code definiert werden.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
    }
}

Sie können diese Funktion in jedem Ereignis aufrufen, das den Start des adaptiven Streamings behandelt, z. B. in einem Click-Ereignis für eine Schaltfläche.You can call this function in whichever event handles the start of adaptive streaming; for example, in a button click event.

Weitere InformationenSee also