Streaming adaptativo

En este artículo se describe cómo agregar la reproducción de contenido multimedia de streaming adaptable a una aplicación para la Plataforma universal de Windows (UWP). Esta característica admite la reproducción de contenido http Live Streaming (HLS) y Streaming dinámico a través de HTTP (DASH).

A partir de Windows 10, versión 1803, Smooth Streaming es compatible con AdaptiveMediaSource. Tenga en cuenta que para Smooth Streaming, solo se admiten los códecs H264 y WVC1. Otros tipos de manifiesto no tienen esta limitación.

Para obtener una lista de las etiquetas de protocolo HLS admitidas, consulta Compatibilidad con etiquetas HLS.

Para obtener una lista de los perfiles DASH admitidos, consulte Compatibilidad con perfiles dash.

Nota:

El código de este artículo es una adaptación de la Adaptive streaming sample (Muestra de streaming adaptable) para la UWP.

Streaming adaptable simple con MediaPlayer y MediaPlayerElement

Para reproducir contenido multimedia adaptable en una aplicación para UWP, crea un objeto Uri que apunte a un archivo de manifiesto DASH o HLS. Crea una instancia de la clase MediaPlayer. Llama a MediaSource.CreateFromUri para crear un nuevo objeto MediaSource y, a continuación, establécelo en la propiedad Source de la clase MediaPlayer. Llama a Play para iniciar la reproducción del contenido multimedia.

MediaPlayer _mediaPlayer;
System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = MediaSource.CreateFromUri(manifestUri);
_mediaPlayer.Play();

El ejemplo anterior reproducirá el audio del contenido multimedia, pero no representa automáticamente el contenido en la interfaz de usuario. La mayoría de las aplicaciones que reproducen contenido de vídeo querrán representar dicho contenido en una página XAML. Para ello, agrega un control MediaPlayerElement a la página XAML.

<MediaPlayerElement x:Name="mediaPlayerElement" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>

Llama a MediaSource.CreateFromUri para crear un objeto MediaSource desde el identificador URI de un archivo de manifiesto DASH o HLS. A continuación, establece la propiedad Source del control MediaPlayerElement. El control MediaPlayerElement creará automáticamente un nuevo objeto MediaPlayer para el contenido. Puedes llamar a Play en el objeto MediaPlayer para iniciar la reproducción del contenido.

System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
mediaPlayerElement.Source = MediaSource.CreateFromUri(manifestUri);
mediaPlayerElement.MediaPlayer.Play();

Nota

A partir de Windows 10, versión 1607, se recomienda usar la clase MediaPlayer para reproducir elementos multimedia. MediaPlayerElement es un control XAML ligero que se usa para representar el contenido de un objeto MediaPlayer en una página XAML. El control MediaElement se sigue admitiendo para la compatibilidad con versiones anteriores. Para obtener más información sobre el uso de MediaPlayer y MediaPlayerElement para reproducir contenido multimedia, consulta Reproducir audio y vídeo con MediaPlayer. Para obtener información sobre cómo usar MediaSource y las API relacionada para trabajar con contenido multimedia, consulta Elementos multimedia, listas de reproducción y pistas.

Streaming adaptable con AdaptiveMediaSource

Si la aplicación requiere características de streaming adaptable más avanzadas, como proporcionar encabezados HTTP personalizados, la supervisión de las velocidades de bits actuales de descarga y reproducción o el ajuste de las proporciones que determinan cuándo cambia el sistema las velocidades de bits de la secuencia adaptable, usa el objeto AdaptiveMediaSource.

Las API de streaming adaptable se encuentran en el espacio de nombres Windows.Media.Streaming.Adaptive. En los ejemplos de este artículo se usan las API de los siguientes espacios de nombres.

using Windows.Media.Streaming.Adaptive;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Media.Playback;
using Windows.Media.Core;

Inicialice adaptiveMediaSource a partir de un URI.

Inicializa el objeto AdaptiveMediaSource con el URI de un archivo de manifiesto de streaming adaptable llamando al método CreateFromUriAsync. El valor de AdaptiveMediaSourceCreationStatus devuelto desde este método te permite saber si el origen multimedia se creó correctamente. Si es así, puedes establecer el objeto como el origen de la secuencia de tu objeto MediaPlayer creando un objeto MediaSource llamando a MediaSource.CreateFromAdaptiveMediaSource y, a continuación, asignandolo a la propiedad Source del reproductor multimedia. En este ejemplo, se consulta la propiedad AvailableBitrates para determinar la máxima velocidad de bits admitida para esta secuencia y luego ese valor se establece como la velocidad de bits inicial. En este ejemplo también se registran controladores para los distintos eventos AdaptiveMediaSource que se describen más adelante en este artículo.

async private void InitializeAdaptiveMediaSource(System.Uri uri)
{
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);

    if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
    {
        ams = result.MediaSource;
        mediaPlayerElement.SetMediaPlayer(new MediaPlayer());
        mediaPlayerElement.MediaPlayer.Source = MediaSource.CreateFromAdaptiveMediaSource(ams);
        mediaPlayerElement.MediaPlayer.Play();


        ams.InitialBitrate = ams.AvailableBitrates.Max<uint>();

        //Register for download requests
        ams.DownloadRequested += DownloadRequested;

        //Register for download failure and completion events
        ams.DownloadCompleted += DownloadCompleted;
        ams.DownloadFailed += DownloadFailed;

        //Register for bitrate change events
        ams.DownloadBitrateChanged += DownloadBitrateChanged;
        ams.PlaybackBitrateChanged += PlaybackBitrateChanged;

        //Register for diagnostic event
        ams.Diagnostics.DiagnosticAvailable += DiagnosticAvailable;
    }
    else
    {
        // Handle failure to create the adaptive media source
        MyLogMessageFunction($"Adaptive source creation failed: {uri} - {result.ExtendedError}");
    }
}

Inicialización de adaptiveMediaSource mediante HttpClient

Si necesitas definir encabezados HTTP personalizados para obtener el archivo de manifiesto, puedes crear un objeto HttpClient, definir los encabezados deseados y, a continuación, pasar el objeto a la sobrecarga de CreateFromUriAsync.

httpClient = new Windows.Web.Http.HttpClient();
httpClient.DefaultRequestHeaders.TryAppendWithoutValidation("X-CustomHeader", "This is a custom header");
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(manifestUri, httpClient);

El evento DownloadRequested se genera cuando el sistema está a punto de recuperar un recurso del servidor. El AdaptiveMediaSourceDownloadRequestedEventArgs que se pasa al controlador de eventos expone las propiedades que proporcionan información sobre el recurso solicitado, como el tipo y el identificador URI del recurso.

Modificación de las propiedades de solicitud de recursos mediante el evento DownloadRequested

Puedes usar el controlador de eventos DownloadRequested para modificar la solicitud de recursos mediante la actualización de las propiedades del objeto AdaptiveMediaSourceDownloadResult proporcionado por los argumentos de evento. En el ejemplo siguiente, se modifica el URI desde el que se recuperará el recurso actualizando las propiedades ResourceUri del objeto de resultado. También puede volver a escribir el desplazamiento del intervalo de bytes y la longitud de los segmentos multimedia o, como se muestra en el ejemplo siguiente, cambiar el URI del recurso para descargar el recurso completo y establecer el desplazamiento y la longitud del intervalo de bytes en null.

Puedes reemplazar el contenido del recurso solicitado estableciendo las propiedades Buffer o InputStream del objeto de resultado. En el ejemplo siguiente, se reemplaza el contenido del recurso de manifiesto estableciendo la propiedad Buffer. Ten en cuenta que, si actualizas la solicitud de recursos con los datos que se obtienen de forma asincrónica (por ejemplo, la recuperación de datos de un servidor remoto o la autenticación de usuario asincrónica), debes llamar a AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral para obtener un aplazamiento y, después, llamar a Complete cuando la operación se complete para indicar al sistema que la operación de solicitud de descarga puede continuar.

    private async void DownloadRequested(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadRequestedEventArgs args)
    {

        // rewrite key URIs to replace http:// with https://
        if (args.ResourceType == AdaptiveMediaSourceResourceType.Key)
        {
            string originalUri = args.ResourceUri.ToString();
            string secureUri = originalUri.Replace("http:", "https:");

            // override the URI by setting property on the result sub object
            args.Result.ResourceUri = new Uri(secureUri);
        }

        if (args.ResourceType == AdaptiveMediaSourceResourceType.Manifest)
        {
            AdaptiveMediaSourceDownloadRequestedDeferral deferral = args.GetDeferral();
            args.Result.Buffer = await CreateMyCustomManifest(args.ResourceUri);
            deferral.Complete();
        }

        if (args.ResourceType == AdaptiveMediaSourceResourceType.MediaSegment)
        {
            var resourceUri = args.ResourceUri.ToString() + "?range=" + 
                args.ResourceByteRangeOffset + "-" + (args.ResourceByteRangeLength - 1);

            // override the URI by setting a property on the result sub object
            args.Result.ResourceUri = new Uri(resourceUri);

            // clear the byte range properties on the result sub object
            args.Result.ResourceByteRangeOffset = null;
            args.Result.ResourceByteRangeLength = null;
        }
    }

Uso de eventos de velocidad de bits para administrar y responder a los cambios de velocidad de bits

El objeto AdaptiveMediaSource proporciona eventos que te permiten reaccionar cuando cambie la velocidad de bits de descarga o reproducción. En este ejemplo, la velocidad de bits actual se actualiza simplemente en la interfaz de usuario. Ten en cuenta que puedes modificar las proporciones que determinan cuándo el sistema cambia velocidades de bits de la secuencia adaptable. Para obtener más información, consulta la propiedad AdvancedSettings.

private async void DownloadBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadBitrateChangedEventArgs args)
{
    await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
    {
        txtDownloadBitrate.Text = args.NewValue.ToString();
    }));
}

private async void PlaybackBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourcePlaybackBitrateChangedEventArgs args)
{
    await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
    {
        txtPlaybackBitrate.Text = args.NewValue.ToString();
    }));
}

Control de eventos de finalización y error de descarga

El objeto AdaptiveMediaSource genera el evento DownloadFailed cuando se produce un error en la descarga de un recurso solicitado. Puede usar este evento para actualizar la interfaz de usuario en respuesta al error. También puede usar el evento para registrar información estadística sobre la operación de descarga y el error.

El objeto AdaptiveMediaSourceDownloadFailedEventArgs pasado al controlador de eventos contiene metadatos sobre la descarga de recursos con errores, como el tipo de recurso, el URI del recurso y la posición dentro de la secuencia donde se produjo el error. RequestId obtiene un identificador único generado por el sistema para la solicitud que se puede usar para correlacionar la información de estado sobre una solicitud individual en varios eventos.

La propiedad Statistics devuelve un objeto AdaptiveMediaSourceDownloadStatistics que proporciona información detallada sobre el número de bytes recibidos en el momento del evento y el tiempo de varios hitos en la operación de descarga. Puede registrar esta información para identificar los problemas de perfomancia en la implementación de streaming adaptable.

private void DownloadFailed(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadFailedEventArgs args)
{
    var statistics = args.Statistics;

    MyLogMessageFunction("download failed for: " + args.ResourceType + 
     " - " + args.ResourceUri +
     " – Error:" + args.ExtendedError.HResult +
     " - RequestId" + args.RequestId + 
     " – Position:" + args.Position +
     " - Duration:" + args.ResourceDuration +
     " - ContentType:" + args.ResourceContentType +
     " - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived + 
     " - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived + 
     " - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
     " - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);

}

El evento DownloadCompleted se produce cuando una descarga de recursos se completa y provdes datos similares al evento DownloadFailed . Una vez más, se proporciona un RequestId para correlacionar eventos para una sola solicitud. Además, se proporciona un objeto AdaptiveMediaSourceDownloadStatistics para habilitar el registro de estadísticas de descarga.

private void DownloadCompleted(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadCompletedEventArgs args)
{
    var statistics = args.Statistics;

    MyLogMessageFunction("download completed for: " + args.ResourceType + " - " +
     args.ResourceUri +
     " – RequestId:" + args.RequestId +
     " – Position:" + args.Position +
     " - Duration:" + args.ResourceDuration +
     " - ContentType:" + args.ResourceContentType +
     " - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived + 
     " - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived + 
     " - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
     " - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);

}

Recopilación de datos de telemetría de streaming adaptables con AdaptiveMediaSourceDiagnostics

AdaptiveMediaSource expone una propiedad Diagnostics que devuelve un objeto AdaptiveMediaSourceDiagnostics. Use este objeto para registrarse para el evento DiagnosticAvailable . Este evento está diseñado para usarse para la recopilación de telemetría y no debe usarse para modificar el comportamiento de la aplicación en tiempo de ejecución. Este evento de diagnóstico se genera por muchas razones diferentes. Compruebe la propiedad DiagnosticType del objeto AdaptiveMediaSourceDiagnosticAvailableEventArgs pasado al evento para determinar el motivo por el que se generó el evento. Entre los posibles motivos se incluyen los errores que acceden al recurso solicitado y los errores que analizan el archivo de manifiesto de streaming. Para obtener una lista de situaciones que pueden desencadenar un evento de diagnóstico, consulte AdaptiveMediaSourceDiagnosticType. Al igual que los argumentos de otros eventos de streaming adaptables, AdaptiveMediaSourceDiagnosticAvailableEventArgs proporciona un requestId adecuado para correlacionar la información de solicitud entre diferentes eventos.

private void DiagnosticAvailable(AdaptiveMediaSourceDiagnostics sender, AdaptiveMediaSourceDiagnosticAvailableEventArgs args)
{
    MySendTelemetryFunction(args.RequestId, args.Position,
                            args.DiagnosticType, args.SegmentId,
                            args.ResourceType, args.ResourceUri,
                            args.ResourceDuration, args.ResourceContentType,
                            args.ResourceByteRangeOffset,
                            args.ResourceByteRangeLength, 
                            args.Bitrate,
                            args.ExtendedError);

}

Aplazar el enlace del contenido de streaming adaptable para los elementos de una lista de reproducción mediante MediaBinder

La clase MediaBinder permite aplazar el enlace de contenido multimedia en un objeto MediaPlaybackList. A partir de Windows 10, versión 1703, puedes proporcionar adaptiveMediaSource como contenido enlazado. El proceso de enlace diferido de un origen multimedia adaptable es en gran medida el mismo que el enlace de otros tipos de medios, que se describe en Elementos multimedia, listas de reproducción y pistas.

Cree una instancia de MediaBinder , establezca una cadena de token definida por la aplicación para identificar el contenido que se va a enlazar y regístrese para el evento Binding . Cree un objeto MediaSource desde el enlazador llamando a MediaSource.CreateFromMediaBinder. A continuación, cree un objeto MediaPlaybackItem desde MediaSource y agréguelo a la lista de reproducción.

_mediaPlaybackList = new MediaPlaybackList();

var binder = new MediaBinder();
binder.Token = "MyBindingToken1";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));

binder = new MediaBinder();
binder.Token = "MyBindingToken2";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));

_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

En el controlador de eventos Binding , use la cadena de token para identificar el contenido que se va a enlazar y, a continuación, cree el origen multimedia adaptable llamando a una de las sobrecargas de CreateFromStreamAsync o CreateFromUriAsync. Dado que se trata de métodos asincrónicos, primero debe llamar al método MediaBindingEventArgs.GetDeferral para indicar al sistema que espere a que se complete la operación antes de continuar. Establezca el origen multimedia adaptable como contenido enlazado llamando a SetAdaptiveMediaSource. Por último, llame a Deferral.Complete una vez completada la operación para indicar al sistema que continúe.

private async void Binder_Binding_AdaptiveMediaSource(MediaBinder sender, MediaBindingEventArgs args)
{
    var deferral = args.GetDeferral();

    var contentUri = new Uri($"http://contoso.com/media/{args.MediaBinder.Token}");
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(contentUri);

    if (result.MediaSource != null)
    {
        args.SetAdaptiveMediaSource(result.MediaSource);
    }
    args.SetUri(contentUri);

    deferral.Complete();
}

Si desea registrar controladores de eventos para el origen multimedia adaptable enlazado, puede hacerlo en el controlador para el evento CurrentItemChanged de MediaPlaybackList. La propiedad CurrentMediaPlaybackItemChangedEventArgs.NewItem contiene la nueva reproducción actual de MediaPlaybackItem en la lista. Obtenga una instancia de AdaptiveMediaSource que representa el nuevo elemento accediendo a la propiedad Source de MediaPlaybackItem y, a continuación, a la propiedad AdaptiveMediaSource del origen multimedia. Esta propiedad será nula si el nuevo elemento de reproducción no es adaptiveMediaSource, por lo que debe probar el valor NULL antes de intentar registrar controladores para cualquiera de los eventos del objeto.

private void AMSMediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args)
{
    if (!(args.NewItem is null))
    {
        var ams = args.NewItem.Source.AdaptiveMediaSource;
        if (!(ams is null))
        {
            ams.PlaybackBitrateChanged += Ams_PlaybackBitrateChanged;
        }
    }
}