Streaming adaptativo
En este artículo se describe cómo agregar la reproducción de contenido multimedia de streaming adaptable a una aplicación de Plataforma universal de Windows (UWP). Esta característica admite la reproducción de contenido de Http Live Streaming (HLS) y de 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, consulte 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 se adaptó del ejemplo de streaming adaptable para UWP.
Streaming adaptable simple con MediaPlayer y MediaPlayerElement
Para reproducir medios de streaming adaptables en una aplicación para UWP, cree un objeto URI que apunte a un archivo de manifiesto DASH o HLS. Creación de una instancia de la clase MediaPlayer. Llame a MediaSource.CreateFromUri para crear un nuevo objeto MediaSource y, a continuación, establézcalo en la propiedad Source de MediaPlayer. Llame a Reproducir 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();
En el ejemplo anterior se reproducirá el audio del contenido multimedia, pero no se 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 el contenido en una página XAML. Para ello, agregue un control MediaPlayerElement a su página XAML.
<MediaPlayerElement x:Name="mediaPlayerElement" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>
Llame a MediaSource.CreateFromUri para crear un objeto MediaSource a partir del URI de un archivo de manifiesto DASH o HLS. A continuación, establezca la propiedad Source de MediaPlayerElement. MediaPlayerElement creará automáticamente un nuevo objeto MediaPlayer para el contenido. Puede llamar a Play en 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, consulte Reproducir audio y vídeo con MediaPlayer. Para obtener información sobre el uso de MediaSource y las API relacionadas para trabajar con contenido multimedia, consulte 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, supervisar las velocidades de bits de descarga y reproducción actuales o ajustar las relaciones que determinan cuándo cambia el sistema las velocidades de bits de la secuencia adaptable, use 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 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 un AdaptiveMediaSource desde un URI.
Inicialice el AdaptiveMediaSource con el URI de un archivo de manifiesto de streaming adaptable llamando a CreateFromUriAsync. El valor AdaptiveMediaSourceCreationStatus devuelto desde este método le permite saber si el origen multimedia se creó correctamente. Si es así, puede establecer el objeto como origen de secuencia para su MediaPlayer creando un objeto MediaSource llamando a MediaSource.CreateFromAdaptiveMediaSource y, a continuación, asignándolo a la propiedad Source del reproductor multimedia. En este ejemplo, se consulta la propiedad AvailableBitrates para determinar la velocidad de bits máxima admitida para esta secuencia y, a continuación, ese valor se establece como 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 necesita establecer encabezados HTTP personalizados para obtener el archivo de manifiesto, puede crear un objeto HttpClient, establecer 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. Los AdaptiveMediaSourceDownloadRequestedEventArgs pasados al controlador de eventos exponen propiedades que proporcionan información sobre el recurso que se solicita, como el tipo y el URI del recurso.
Modificación de las propiedades de la solicitud de recursos mediante el evento DownloadRequested
Puede usar el controlador de eventos DownloadRequested para modificar la solicitud de recursos actualizando las propiedades del objeto AdaptiveMediaSourceDownloadResult proporcionado por los argumentos del evento. En el ejemplo siguiente, el URI desde el que se recuperará el recurso se modifica 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.
Puede invalidar el contenido del recurso solicitado estableciendo las propiedades Buffer o InputStream del objeto de resultado. En el ejemplo siguiente, el contenido del recurso de manifiesto se reemplaza estableciendo la propiedad Buffer. Tenga en cuenta que si va a actualizar la solicitud de recursos con datos obtenidos de forma asincrónica, como recuperar datos de un servidor remoto o una autenticación de usuario asincrónica, debe llamar a AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral para obtener un aplazamiento y, a continuación, llamar a Complete cuando se complete la operación 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 permiten reaccionar cuando cambian las velocidades de bits de descarga o reproducción. En este ejemplo, las velocidades de bits actuales simplemente se actualizan en la interfaz de usuario. Tenga en cuenta que puede modificar las relaciones que determinan cuándo cambia el sistema las velocidades de bits de la secuencia adaptable. Para obtener más información, vea 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 problemas de rendimiento 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 se completa una descarga de recursos y proporciona 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 adaptable con AdaptiveMediaSourceDiagnostics
AdaptiveMediaSource expone una propiedad Diagnostics que devuelve un objeto AdaptiveMediaSourceDiagnostics. Use este objeto para registrarse para el evento DiagnosticAvailable. Este evento está pensado 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 errores al acceder al recurso solicitado y errores al analizar 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 una propiedad RequestId para correlacionar la información de solicitud entre distintos 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, puede proporcionar un 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 registre 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 de eventos 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 del objeto MediaPlaybackItem y, a continuación, a la propiedad AdaptiveMediaSource del origen multimedia. Esta propiedad será null si el nuevo elemento de reproducción no es AdaptiveMediaSource, por lo que debe probar para 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;
}
}
}