Compartir a través de


Streaming adaptable con PlayReady

En este artículo se describe cómo agregar streaming adaptable de contenido multimedia con la protección de contenido de Microsoft PlayReady a una aplicación de Plataforma universal de Windows (UWP).

Esta característica admite actualmente la reproducción de contenido de streaming dinámico a través de HTTP (DASH).

HLS (Streaming en vivo HTTP de Apple) no se admite con PlayReady.

Smooth Streaming tampoco se admite actualmente de forma nativa; Sin embargo, PlayReady es extensible y mediante código o bibliotecas adicionales, se puede admitir el streaming Smooth protegido por PlayReady, aprovechando software o incluso DRM de hardware (administración de derechos digitales).

En este artículo solo se tratan los aspectos del streaming adaptable específico de PlayReady. Para obtener información sobre cómo implementar el streaming adaptable en general, consulte Streaming adaptable.

En este artículo se usa código del ejemplo de streaming adaptable en el repositorio Windows-universal-samples de Microsoft en GitHub. El escenario 4 se ocupa del uso del streaming adaptable con PlayReady. Para descargar el repositorio en un archivo ZIP, vaya al nivel raíz del repositorio y seleccione el botón Descargar ARCHIVO ZIP .

Necesitará las siguientes instrucciones using :

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;

El espacio de nombres LicenseRequest procede de CommonLicenseRequest.cs, un archivo de PlayReady proporcionado por Microsoft a las licencias.

Deberá declarar algunas variables globales:

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

También querrá declarar la siguiente constante:

private const uint MSPR_E_CONTENT_ENABLING_ACTION_REQUIRED = 0x8004B895;

Configuración de MediaProtectionManager

Para agregar la protección de contenido de PlayReady a la aplicación para UWP, tendrás que configurar un objeto MediaProtectionManager . Esto se hace al inicializar el objeto AdaptiveMediaSource.

El código siguiente configura un 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;
}

Este código simplemente se puede copiar en la aplicación, ya que es obligatorio configurar la protección de contenido.

El evento ComponentLoadFailed se desencadena cuando se produce un error en la carga de datos binarios. Es necesario agregar un controlador de eventos para controlar esto, lo que indica que la carga no se completó:

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

Del mismo modo, es necesario agregar un controlador de eventos para el evento ServiceRequested , que se desencadena cuando se solicita un servicio. Este código comprueba qué tipo de solicitud es y responde adecuadamente:

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);
    }
}

Solicitudes de servicio de individualización

El código siguiente realiza reactivamente una solicitud de servicio de individualización de PlayReady. Pasamos la solicitud como un parámetro a la función . Rodeamos la llamada en un bloque try/catch y, si no hay ninguna excepción, decimos que la solicitud se completó correctamente:

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;
}

Como alternativa, es posible que deseemos realizar de forma proactiva una solicitud de servicio de individualización, en cuyo caso llamamos a la siguiente función en lugar del código que llama a ReactiveIndivRequest en ProtectionManager_ServiceRequested:

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

Solicitudes de servicio de adquisición de licencias

Si en su lugar la solicitud era un PlayReadyLicenseAcquisitionServiceRequest, llamamos a la siguiente función para solicitar y adquirir la licencia de PlayReady. Indicamos al objeto MediaProtectionServiceCompletion que pasamos si la solicitud se realizó correctamente o no y completamos la solicitud:

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);
}

Inicialización de AdaptiveMediaSource

Por último, necesitará una función para inicializar AdaptiveMediaSource, creada a partir de un URI determinado y MediaElement. El URI debe ser el vínculo al archivo multimedia (HLS o DASH); mediaElement debe definirse en el 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
    }
}

Puede llamar a esta función en cualquier evento que controle el inicio del streaming adaptable; por ejemplo, en un evento de clic de botón.

Consulte también