Elementos multimedia, listas de reproducción y pistas

En este artículo se muestra cómo usar la clase MediaSource, que proporciona una forma común de hacer referencia a contenido multimedia y reproducirlo desde diferentes orígenes (como archivos locales o remotos) y se expone un modelo común para acceder a datos multimedia, independientemente del formato multimedia subyacente. La clase MediaPlaybackItem amplía la funcionalidad del objeto MediaSource, lo que permite administrar y seleccionar entre varias pistas de audio, vídeo y metadatos incluidos en un elemento multimedia. MediaPlaybackList permite crear listas de reproducción de uno o más elementos de reproducción de contenido multimedia.

Crear y reproducir un objeto MediaSource

Crea una nueva instancia del objeto MediaSource con una llamada a uno de los métodos de fábrica expuestos por la clase:

Después de crear un objeto MediaSource, puedes reproducirlo con una clase MediaPlayer estableciendo la propiedad Source. A partir de Windows 10, versión 1607, se puede asignar un objeto MediaPlayer a una clase MediaPlayerElement mediante una llamada al método SetMediaPlayer para representar el contenido del reproductor multimedia en una página XAML. Este es el método preferido en comparación con el uso de MediaElement. Para obtener más información sobre cómo usar MediaPlayer, consulta Reproducir audio y vídeo con MediaPlayer.

En el siguiente ejemplo se muestra cómo reproducir un archivo multimedia seleccionado por el usuario en un objeto MediaPlayer con MediaSource.

Para completar este escenario, tendrás que incluir los espacios de nombres Windows.Media.Core y Windows.Media.Playback.

using Windows.Media.Core;
using Windows.Media.Playback;

Declara una variable de tipo MediaSource. Para los ejemplos de este artículo, el origen de contenido multimedia se declara como un miembro de clase para que se pueda acceder a él desde varias ubicaciones.

MediaSource _mediaSource;

Declara una variable para almacenar el objeto MediaPlayer y, si quieres representar el contenido multimedia en XAML, agrega un control MediaPlayerElement a la página.

MediaPlayer _mediaPlayer;
<MediaPlayerElement x:Name="mediaPlayerElement"/>

Para permitir al usuario seleccionar un archivo multimedia para su reproducción, usa una clase FileOpenPicker. Con el objeto StorageFile devuelto desde el método PickSingleFileAsync del selector, inicializa un nuevo objeto MediaObject con una llamada a MediaSource.CreateFromStorageFile. Por último, establece el origen del contenido multimedia como el origen de reproducción del objeto MediaElement con una llamada al método SetPlaybackSource.

//Create a new picker
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();

//make a collection of all video types you want to support (for testing we are adding just 3).
string[] fileTypes = new string[] {".wmv", ".mp4", ".mkv"};   
   
//Add your fileTypes to the FileTypeFilter list of filePicker.
foreach (string fileType in fileTypes)
{
    filePicker.FileTypeFilter.Add(fileType);
}

//Set picker start location to the video library
filePicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;

//Retrieve file from picker
StorageFile file = await filePicker.PickSingleFileAsync();

if (!(file is null))
{
    _mediaSource = MediaSource.CreateFromStorageFile(file);
    _mediaPlayer = new MediaPlayer();
    _mediaPlayer.Source = _mediaSource;
    mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
}

De manera predeterminada, el objeto MediaPlayer no comienza a reproducirse automáticamente cuando se establece el origen del contenido multimedia. Puedes comenzar la reproducción de forma manual con una llamada al método Play.

_mediaPlayer.Play();

También puedes establecer la propiedad AutoPlay de MediaPlayer en true para indicar al reproductor que comience la reproducción en cuanto se establezca el origen del contenido multimedia.

_mediaPlayer.AutoPlay = true;

Creación de un objeto MediaSource a partir de una clase DownloadOperation

A partir de Windows, versión 1803, puede crear un objeto MediaSource a partir de una clase DownloadOperation.

StorageFile destinationFile = await KnownFolders.VideosLibrary.CreateFileAsync("file.mp4", CreationCollisionOption.GenerateUniqueName);

var downloader = new BackgroundDownloader();
var downloadOperation = downloader.CreateDownload(new Uri("http://server.com/file.mp4"), destinationFile);
MediaSource mediaSource =
      MediaSource.CreateFromDownloadOperation(downloadOperation);

Ten en cuenta que, aunque puedes crear un objeto MediaSource desde una descarga sin iniciarlo o establecer su propiedad IsRandomAccessRequired en true, debes hacer ambas cosas antes de intentar adjuntar MediaSource a un MediaPlayer o MediaPlayerElement para su reproducción.

downloadOperation.IsRandomAccessRequired = true;
var startAsyncTask = downloadOperation.StartAsync().AsTask();
mediaPlayerElement.Source = mediaSource;

Controlar varias pistas de audio, vídeo y metadatos con MediaPlaybackItem

El uso de un objeto MediaSource para la reproducción es conveniente porque proporciona una forma común de reproducir contenido multimedia desde diferentes tipos de orígenes, pero puedes acceder a un comportamiento más avanzado al crear una clase MediaPlaybackItem a partir de MediaSource. Esto incluye la posibilidad de acceder a varias pistas de audio, vídeo y datos de un elemento multimedia y administrarlas.

Declara una variable para almacenar el objeto MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Crea un objeto MediaPlaybackItem con una llamada al constructor y pasándolo en un objeto MediaSource inicializado.

Si tu aplicación admite varias pistas de audio, vídeo o datos en un elemento de reproducción de contenido multimedia, registra controladores de eventos para los eventos AudioTracksChanged, VideoTracksChanged o TimedMetadataTracksChanged.

Por último, establece el origen de reproducción de la clase MediaElement o MediaPlayer en tu objeto MediaPlaybackItem.

_mediaSource = MediaSource.CreateFromStorageFile(file);
_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);

_mediaPlaybackItem.AudioTracksChanged += PlaybackItem_AudioTracksChanged;
_mediaPlaybackItem.VideoTracksChanged += MediaPlaybackItem_VideoTracksChanged;
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;

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

Nota

Un objeto MediaSource únicamente se puede asociar a un solo objeto MediaPlaybackItem. Después de crear un objeto MediaPlaybackItem a partir de un origen, si intentas crear otro elemento de reproducción desde el mismo origen, se generará un error. Además, después de crear un objeto MediaPlaybackItem desde un origen de contenido multimedia, no puedes establecer el objeto MediaSource directamente como el origen de un objeto MediaPlayer. En cambio, debes usar el objeto MediaPlaybackItem.

El evento VideoTracksChanged se genera después de que un objeto MediaPlaybackItem que contiene varias pistas de vídeos se asigne como origen de reproducción y se puede volver a lanzar si la lista de pistas de vídeo cambia para los cambios de elemento. El controlador de este evento te permite actualizar la interfaz de usuario para permitir al usuario cambiar entre pistas disponibles. En este ejemplo se usa un objeto ComboBox para mostrar las pistas de vídeo disponibles.

<ComboBox x:Name="videoTracksComboBox" SelectionChanged="videoTracksComboBox_SelectionChanged"/>

En el controlador VideoTracksChanged, repite todas las pistas de la lista VideoTracks del elemento de reproducción. Para cada pista, se crea un nuevo objeto ComboBoxItem. Si la pista todavía no tiene etiqueta, esta se genera a partir del índice de pistas. La propiedad Tag del elemento de cuadro combinado se establece en el índice de pistas para que se pueda identificar más adelante. Por último, el elemento se agrega al cuadro combinado. Ten en cuenta que estas operaciones se realizan en una llamada CoreDispatcher.RunAsync porque todos los cambios de la interfaz de usuario deben realizarse en el subproceso de interfaz de usuario y este evento se lanza en un subproceso diferente.

private async void MediaPlaybackItem_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        videoTracksComboBox.Items.Clear();
        for (int index = 0; index < sender.VideoTracks.Count; index++)
        {
            var videoTrack = sender.VideoTracks[index];
            ComboBoxItem item = new ComboBoxItem();
            item.Content = String.IsNullOrEmpty(videoTrack.Label) ? $"Track {index}" : videoTrack.Label;
            item.Tag = index;
            videoTracksComboBox.Items.Add(item);
        }
    });
}

En el controlador SelectionChanged del cuadro combinado, el índice de pistas se recupera de la propiedad Tag del elemento seleccionado. Si estableces la propiedad SelectedIndex en la lista VideoTracks del elemento de reproducción de contenido multimedia, el objeto MediaElement o MediaPlayer cambiará la pista de vídeo activa al índice especificado.

private void videoTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
    _mediaPlaybackItem.VideoTracks.SelectedIndex = trackIndex;
}

La administración de elementos multimedia con varias pistas de audio funciona exactamente del mismo modo que con las pistas de vídeo. Controla el objeto AudioTracksChanged para actualizar la interfaz de usuario con las pistas de audio que se encuentran en la lista AudioTracks del elemento de reproducción. Cuando el usuario selecciona una pista de audio, establece la propiedad SelectedIndex de la lista AudioTracks para hacer que el objeto MediaElement o MediaPlayer cambie la pista de audio activa al índice especificado.

<ComboBox x:Name="audioTracksComboBox" SelectionChanged="audioTracksComboBox_SelectionChanged"/>
private async void PlaybackItem_AudioTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        audioTracksComboBox.Items.Clear();
        for (int index = 0; index < sender.AudioTracks.Count; index++)
        {
            var audioTrack = sender.AudioTracks[index];
            ComboBoxItem item = new ComboBoxItem();
            item.Content = String.IsNullOrEmpty(audioTrack.Label) ? $"Track {index}" : audioTrack.Label;
            item.Tag = index;
            videoTracksComboBox.Items.Add(item);
        }
    });
}
private void audioTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
    _mediaPlaybackItem.AudioTracks.SelectedIndex = trackIndex;
}

Además de contenido de audio y vídeo, un objeto MediaPlaybackItem puede contener cero o más objetos TimedMetadataTrack. Una pista de metadatos temporizada puede contener texto de subtítulo o título, o puede contener datos personalizados que son propiedad de la aplicación. Una pista de metadatos temporizada contiene una lista de indicaciones representadas por objetos que heredan del objeto IMediaCue, como un objeto DataCue o TimedTextCue. Cada indicación tiene una hora de inicio y una duración que determina el momento de activación de la indicación y durante cuánto tiempo.

Similar a las pistas de audio y vídeos, las pistas de metadatos temporizadas de un elemento multimedia se pueden descubrir al controlar el evento TimedMetadataTracksChanged de un objeto MediaPlaybackItem. Sin embargo, con las pistas de metadatos temporizadas, quizás el usuario quiera habilitar más de una pista de metadatos a la vez. Además, en función del escenario de la aplicación, es posible que quieras habilitar o deshabilitar las pitas de metadatos automáticamente, sin la intervención del usuario. Con fines ilustrativos, en este ejemplo se agrega un control ToggleButton para cada pista de metadatos de un elemento multimedia para permitir que el usuario habilite y deshabilite la pista. La propiedad Tag de cada botón se establece en el índice de la pista de metadatos asociada para que se pueda identificar cuando se alterna el botón.

<StackPanel x:Name="MetadataButtonPanel" Orientation="Horizontal"/>
private async void MediaPlaybackItem_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            var timedMetadataTrack = sender.TimedMetadataTracks[index];

            ToggleButton toggle = new ToggleButton()
            {
                Content = String.IsNullOrEmpty(timedMetadataTrack.Label) ? $"Track {index}" : timedMetadataTrack.Label,
                Tag = (uint)index
            };
            toggle.Checked += Toggle_Checked;
            toggle.Unchecked += Toggle_Unchecked;

            MetadataButtonPanel.Children.Add(toggle);
        }
    });
}

Dado que puede haber más de una pista de metadatos activa a la vez, no se establece simplemente el índice activo de la lista de pistas de metadatos. En cambio, llama al método SetPresentationMode del objeto MediaPlaybackItem, pasando el índice de la pista que quieras cambiar y luego proporcionando un valor de la enumeración TimedMetadataTrackPresentationMode. El modo de presentación que elijas depende de la implementación de la aplicación. En este ejemplo, la pista de metadatos se establece en PlatformPresented cuando está habilitada. En el caso de las pistas basadas en texto, esto significa que el sistema mostrará automáticamente las indicaciones de texto en la pista. Cuando el botón de alternancia está desactivado, el modo de presentación se establece en Deshabilitado, lo que significa que no se muestra texto y no se genera ningún evento de indicación. Los eventos de indicación se describen más adelante en este artículo.

private void Toggle_Checked(object sender, RoutedEventArgs e) =>         
    _mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
        TimedMetadataTrackPresentationMode.PlatformPresented);
private void Toggle_Unchecked(object sender, RoutedEventArgs e) =>         
    _mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
        TimedMetadataTrackPresentationMode.Disabled);

Mientras se procesan las pistas de metadatos, puedes acceder al conjunto de indicaciones de la pista mediante el acceso a las propiedades Cues o ActiveCues. Puedes hacerlo para actualizar la interfaz de usuario con el fin de mostrar las ubicaciones de indicación de un elemento multimedia.

Controlar errores desconocidos y códecs no admitidos al abrir elementos multimedia

A partir de Windows 10, versión 1607, puedes comprobar si el códec necesario para la reproducción de un elemento multimedia se admite parcial o totalmente en el dispositivo en el que se ejecuta la aplicación. En el controlador de eventos de los eventos modificados por pistas de MediaPlaybackItem , como AudioTracksChanged, compruebe primero si el cambio de pista es una inserción de una pista nueva. Si es así, puede obtener una referencia a la pista que se va a insertar mediante el índice pasado en el parámetro IVectorChangedEventArgs.Index con la colección de pistas adecuada del parámetro MediaPlaybackItem , como la colección AudioTracks .

Una vez que tengas una referencia a la pista insertada, comprueba el valor de DecoderStatus de la propiedad SupportInfo de la pista. Si el valor es FullySupported, entonces el códec adecuado que se necesita para reproducir la pista está presente en el dispositivo. Si el valor es Degraded, el sistema puede reproducir la pista, pero la reproducción se degradará de alguna manera. Por ejemplo, una pista de audio 5.1 puede reproducirse como estéreo de dos canales. Si este es el caso, quizás quieras actualizar tu interfaz de usuario para alertar al usuario sobre la degradación. Si el valor es UnsupportedSubtype o UnsupportedEncoderProperties, la pista no se puede reproducir en absoluto con los códecs actuales del dispositivo. Quizás quieras alertar al usuario y omitir la reproducción del elemento o implementar la interfaz de usuario para que el usuario pueda descargar el códec correcto. El método GetEncodingProperties de la pista puede usarse para determinar el códec necesario para la reproducción.

Por último, puedes registrar el evento OpenFailed de la pista, que se genera si se admite la pista en el dispositivo, pero no se pudo abrir debido a un error desconocido en la canalización.

private async void SnippetAudioTracksChanged_CodecCheck(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        var insertedTrack = sender.AudioTracks[(int)args.Index];

        var decoderStatus = insertedTrack.SupportInfo.DecoderStatus;
        if (decoderStatus != MediaDecoderStatus.FullySupported)
        {
            if (decoderStatus == MediaDecoderStatus.Degraded)
            {
                ShowMessageToUser($"Track {insertedTrack.Name} can play but playback will be degraded. {insertedTrack.SupportInfo.DegradationReason}");
            }
            else
            {
                // status is MediaDecoderStatus.UnsupportedSubtype or MediaDecoderStatus.UnsupportedEncoderProperties
                ShowMessageToUser($"Track {insertedTrack.Name} uses an unsupported media format.");
            }

            Windows.Media.MediaProperties.AudioEncodingProperties props = insertedTrack.GetEncodingProperties();
            await HelpUserInstallCodec(props);
        }
        else
        {
            insertedTrack.OpenFailed += InsertedTrack_OpenFailed;
        }
    }

}

En el controlador del evento OpenFailed, puedes comprobar si se desconoce el estado de MediaSource y, si es así, puedes seleccionar mediante programación una pista diferente para reproducir, permitir al usuario que elija una pista diferente o abandonar la reproducción.

private async void InsertedTrack_OpenFailed(AudioTrack sender, AudioTrackOpenFailedEventArgs args)
{
    LogError(args.ExtendedError.HResult);

    if (sender.SupportInfo.MediaSourceStatus == MediaSourceStatus.Unknown)
    {
        await SelectAnotherTrackOrSkipPlayback(sender.PlaybackItem);
    }
}

Establecer las propiedades de visualización que se usan en los controles de transporte de contenido multimedia del sistema

A partir de Windows 10, versión 1607, el contenido multimedia que se reproduce en una clase MediaPlayer se integra automáticamente en los controles de transporte de contenido multimedia del sistema (SMTC) de manera predeterminada. Puedes especificar los metadatos que se mostrarán en SMTC actualizando las propiedades de visualización de MediaPlaybackItem. Obtén un objeto que representa las propiedades de visualización de un elemento mediante una llamada a GetDisplayProperties. Establece si el elemento de reproducción es música o vídeo al establecer el valor de la propiedad Type. A continuación, establece las propiedades VideoProperties o MusicProperties del objeto. Llama a ApplyDisplayProperties para actualizar las propiedades del elemento en los valores que hayas especificado. Por lo general, una aplicación recuperará los valores de visualización dinámicamente desde un servicio web, pero en el siguiente ejemplo se muestra este proceso con los valores codificados de forma rígida.

MediaItemDisplayProperties props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Video;
props.VideoProperties.Title = "Video title";
props.VideoProperties.Subtitle = "Video subtitle";
props.VideoProperties.Genres.Add("Documentary");
mediaPlaybackItem.ApplyDisplayProperties(props);
props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Music;
props.MusicProperties.Title = "Song title";
props.MusicProperties.Artist = "Song artist";
props.MusicProperties.Genres.Add("Polka");
mediaPlaybackItem.ApplyDisplayProperties(props);

Agregar texto temporizado externo con TimedTextSource

En algunos escenarios, puedes tener archivos externos que contienen texto temporizado asociado con un elemento multimedia, tales como archivos independientes que contienen los subtítulos en las distintas configuraciones regionales. Usa la clase TimedTextSource para cargarla en archivos de texto temporizado externos de una secuencia o un URI.

En este ejemplo, se usa una colección Dictionary para almacenar una lista de los orígenes de texto temporizado del elemento multimedia con el URI de origen y el objeto TimedTextSource como el par clave y valor con el fin de identificar las pistas después de que se hayan resuelto.

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Crea un nuevo objeto TimedTextSource para cada archivo de texto temporizado externo con una llamada a CreateFromUri. Agrega una entrada al objeto Dictionary para el origen de texto temporizado. Agrega un controlador para el evento TimedTextSource.Resolved con el fin de controlar si el elemento no se pudo cargar o para establecer propiedades adicionales después de que el elemento se haya cargado correctamente.

Registra todos los objetos TimedTextSource en el objeto MediaSource agregándolos a la colección ExternalTimedTextSources. Ten en cuenta que los orígenes de texto temporizado externos se agregan directamente al objeto MediaSource y no al objeto MediaPlaybackItem creado a partir del origen. Para actualizar la interfaz de usuario y reflejar las pistas de texto externo, registra y controla el evento TimedMetadataTracksChanged según se describió anteriormente en este artículo.

// Create the TimedTextSource and add entry to URI map
var timedTextSourceUri_En = new Uri("http://contoso.com/MyClipTimedText_en.srt");
var timedTextSource_En = TimedTextSource.CreateFromUri(timedTextSourceUri_En);
timedTextSourceMap[timedTextSource_En] = timedTextSourceUri_En;
timedTextSource_En.Resolved += TimedTextSource_Resolved;

var timedTextSourceUri_Pt = new Uri("http://contoso.com/MyClipTimedText_pt.srt");
var timedTextSource_Pt = TimedTextSource.CreateFromUri(timedTextSourceUri_Pt);
timedTextSourceMap[timedTextSource_Pt] = timedTextSourceUri_Pt;
timedTextSource_Pt.Resolved += TimedTextSource_Resolved;

// Add the TimedTextSource to the MediaSource
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_En);
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_Pt);

_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;

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

En el controlador del evento TimedTextSource.Resolved, comprueba la propiedad Error del objeto TimedTextSourceResolveResultEventArgs que se pasó al controlador para determinar si se produjo un error al intentar cargar los datos de texto temporizado. Si el elemento se resolvió correctamente, puede usar este controlador para actualizar propiedades adicionales de la pista resuelta. En este ejemplo se agrega una etiqueta para cada pista en función del URI almacenado anteriormente en dictionary.

private void TimedTextSource_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
    var timedTextSourceUri = timedTextSourceMap[sender];

    if (!(args.Error is null))
    {
        // Show that there was an error in your UI
        ShowMessageToUser($"There was an error resolving track: {timedTextSourceUri}");
        return;
    }

    // Add a label for each resolved track
    var timedTextSourceUriString = timedTextSourceUri.AbsoluteUri;
    if (timedTextSourceUriString.Contains("_en"))
    {
        args.Tracks[0].Label = "English";
    }
    else if (timedTextSourceUriString.Contains("_pt"))
    {
        args.Tracks[0].Label = "Portuguese";
    }
}

Agregar pistas de metadatos adicionales

Puedes crear pistas de metadatos personalizadas dinámicamente en el código y asociarlas con un origen de contenido multimedia. Las pistas que creas pueden contener texto de subtítulo o título, o pueden contener los datos de propiedad de la aplicación.

Crea un nuevo objeto TimedMetadataTrack con una llamada al constructor y especificando el identificador, el identificador de idioma y un valor de la enumeración TimedMetadataKind. Registra los controladores para los eventos CueEntered y CueExited. Estos eventos se generan cuando se alcanza el tiempo de inicio para una identificación y cuando la duración de una identificación ha expirado, respectivamente.

Cree un nuevo objeto de indicación, adecuado para el tipo de seguimiento de metadatos que creó y establezca el identificador, la hora de inicio y la duración de la pista. En este ejemplo se crea una pista de datos, por lo que se genera un conjunto de objetos DataCue y se proporciona un búfer que contiene datos específicos de la aplicación para cada indicación. Para registrar la pista nueva, agrégala a la colección ExternalTimedMetadataTracks del objeto MediaSource.

A partir de Windows 10, versión 1703, la propiedad DataCue.Properties expone un PropertySet que puedes usar para almacenar propiedades personalizadas en pares clave/datos que se pueden recuperar en los eventos CueEntered y CueExited .

TimedMetadataTrack metadataTrack = new TimedMetadataTrack("ID_0", "en-us", TimedMetadataKind.Data);
metadataTrack.Label = "Custom data track";
metadataTrack.CueEntered += MetadataTrack_DataCueEntered;
metadataTrack.CueExited += MetadataTrack_CueExited;

// Example cue data
string data = "Cue data";
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
Windows.Storage.Streams.IBuffer buffer = bytes.AsBuffer();

for (int i = 0; i < 10; i++)
{
    DataCue cue = new DataCue();
    cue.Id = "ID_" + i;
    cue.Data = buffer;
    cue.Properties["AdUrl"] = "http://contoso.com/ads/123";
    cue.StartTime = TimeSpan.FromSeconds(3 + i * 3);
    cue.Duration = TimeSpan.FromSeconds(2);

    metadataTrack.AddCue(cue);
}

_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);

El evento CueEntered se genera cuando se haya alcanzado la hora de inicio de la indicación, siempre y cuando la pista asociada tenga un modo de presentación de ApplicationPresented, Hidden o PlatformPresented. Los eventos de indicación no se generan para las pistas de metadatos mientras el modo de presentación de la pista sea Disabled. En este ejemplo, simplemente se envían a la ventana de depuración los datos personalizados asociados con la indicación.

private void MetadataTrack_DataCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
    DataCue cue = (DataCue)args.Cue;
    string data = System.Text.Encoding.Unicode.GetString(cue.Data.ToArray());
    System.Diagnostics.Debug.WriteLine("Cue entered: " + data);
    System.Diagnostics.Debug.WriteLine("Custom prop value: " + cue.Properties["AdUrl"]);
}

En este ejemplo se agrega una pista de texto personalizado al especificar TimedMetadataKind.Caption durante la creación de la pista y usando objetos TimedTextCue para agregar indicaciones a la pista.

TimedMetadataTrack metadataTrack = new TimedMetadataTrack("TrackID_0", "en-us", TimedMetadataKind.Caption);
metadataTrack.Label = "Custom text track";
metadataTrack.CueEntered += MetadataTrack_TextCueEntered;

for (int i = 0; i < 10; i++)
{
    TimedTextCue cue = new TimedTextCue()
    {
        Id = "TextCueID_" + i,
        StartTime = TimeSpan.FromSeconds(i * 3),
        Duration = TimeSpan.FromSeconds(2)
    };

    cue.Lines.Add(new TimedTextLine() { Text = "This is a custom timed text cue." });
    metadataTrack.AddCue(cue);
}

_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);
private void MetadataTrack_TextCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
    TimedTextCue cue = (TimedTextCue)args.Cue;
    System.Diagnostics.Debug.WriteLine("Cue entered: " + cue.Id + " " + cue.Lines[0].Text);
}

Reproducir una lista de elementos multimedia con MediaPlaybackList

El objeto MediaPlaybackList permite crear una lista de reproducción de elementos multimedia, que se representan con objetos MediaPlaybackItem.

Nota Los elementos de mediaPlaybackList se representan mediante la reproducción sin espacio. El sistema usará los metadatos proporcionados en archivos codificados MP3 o AAC para determinar la compensación del retraso o el espaciado interno necesaria para la reproducción sin pausas. Si los archivos codificados MP3 o AAC no proporcionan estos metadatos, el sistema determina el retraso o el espaciado interno de forma heurística. Para los formatos sin pérdida, como PCM, FLAC o ALAC, el sistema no realiza ninguna acción porque estos codificadores no introducen ningún retraso ni espaciado interno.

Para empezar, declara una variable para almacenar el objeto MediaPlaybackList.

MediaPlaybackList _mediaPlaybackList;

Crear un objeto MediaPlaybackItem para cada elemento multimedia que quieras agregar a tu lista usando el mismo procedimiento descrito anteriormente en este artículo. Inicializa el objeto MediaPlaybackList y agrégale los elementos de reproducción de contenido multimedia. Registra el controlador del evento CurrentItemChanged. Este evento permite actualizar la interfaz de usuario para reflejar el elemento multimedia que se encuentra en reproducción. También puede registrarse para el evento ItemOpened , que se genera cuando un elemento de la lista se abre correctamente y el evento ItemFailed , que se genera cuando no se puede abrir un elemento de la lista.

A partir de Windows 10, versión 1703, puedes especificar el número máximo de objetos MediaPlaybackItem en mediaPlaybackList que el sistema mantendrá abierto después de que se hayan reproducido estableciendo la propiedad MaxPlayedItemsToKeepOpen . Cuando se mantiene abierto un objeto MediaPlaybackItem , la reproducción del elemento puede iniciarse instantáneamente cuando el usuario cambia a ese elemento porque no es necesario volver a cargarlo. Pero mantener abiertos los elementos también aumenta el consumo de memoria de la aplicación, por lo que debes considerar el equilibrio entre la capacidad de respuesta y el uso de memoria al establecer este valor.

Para habilitar la reproducción de la lista, establezca el origen de reproducción de MediaPlayer en su clase MediaPlaybackList.

_mediaPlaybackList = new MediaPlaybackList();

var files = await filePicker.PickMultipleFilesAsync();

foreach (var file in files)
{
    var mediaPlaybackItem = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(file));
    _mediaPlaybackList.Items.Add(mediaPlaybackItem);
}

_mediaPlaybackList.CurrentItemChanged += MediaPlaybackList_CurrentItemChanged;
_mediaPlaybackList.ItemOpened += MediaPlaybackList_ItemOpened;
_mediaPlaybackList.ItemFailed += MediaPlaybackList_ItemFailed;

_mediaPlaybackList.MaxPlayedItemsToKeepOpen = 3;

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

En el controlador de eventos CurrentItemChanged, actualiza la interfaz de usuario para reflejar el elemento en reproducción, que se puede recuperar con la propiedad NewItem del objeto CurrentMediaPlaybackItemChangedEventArgs pasado al evento. Recuerda que si actualizas la interfaz de usuario de este evento, debes hacerlo dentro de una llamada a CoreDispatcher.RunAsync para que las actualizaciones se hagan en el subproceso de la interfaz de usuario.

A partir de Windows 10, versión 1703, puedes comprobar la propiedad CurrentMediaPlaybackItemChangedEventArgs.Reason para obtener un valor que indique el motivo por el que cambió el elemento, como los elementos de conmutación de la aplicación mediante programación, el elemento que se reproducía anteriormente al final o se produjo un error.

private void MediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args) => 
    LogTelemetryData($"CurrentItemChanged reason: {args.Reason.ToString()}");

Llama a MovePrevious o MoveNext para hacer que el reproductor de contenido multimedia reproduzca el elemento anterior o siguiente en tu objeto MediaPlaybackList.

private void prevButton_Click(object sender, RoutedEventArgs e) =>  _mediaPlaybackList.MovePrevious();
private void nextButton_Click(object sender, RoutedEventArgs e) => _mediaPlaybackList.MoveNext();

Establece la propiedad ShuffleEnabled para especificar si el reproductor de contenido multimedia debe reproducir los elementos de la lista en orden aleatorio.

private async void shuffleButton_Click(object sender, RoutedEventArgs e)
{
    _mediaPlaybackList.ShuffleEnabled = !_mediaPlaybackList.ShuffleEnabled;

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        shuffleButton.FontWeight =
            _mediaPlaybackList.ShuffleEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
    });
}

Establece la propiedad AutoRepeatEnabled para especificar si el reproductor de contenido multimedia debe repetir la reproducción de la lista.

private async void autoRepeatButton_Click(object sender, RoutedEventArgs e)
{
    _mediaPlaybackList.AutoRepeatEnabled = !_mediaPlaybackList.AutoRepeatEnabled;

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        autoRepeatButton.FontWeight =
            _mediaPlaybackList.AutoRepeatEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
    });
}

Controlar los errores de los elementos multimedia en una lista de reproducción

El evento ItemFailed se genera cuando se produce un error al intentar abrir un elemento de la lista. La propiedad ErrorCode del objeto MediaPlaybackItemError pasado al controlador enumera la causa específica del error cuando es posible, incluidos los errores de red, descodificación o de cifrado.

private void MediaPlaybackList_ItemFailed(MediaPlaybackList sender, MediaPlaybackItemFailedEventArgs args)
{
    LogError(args.Error.ErrorCode.ToString());
    LogError(args.Error.ExtendedError.HResult);
}

Deshabilitar la reproducción de elementos en una lista de reproducción

A partir de Windows 10, versión 1703, puedes deshabilitar la reproducción de uno o varios elementos en mediaPlaybackItemList estableciendo la propiedad IsDisabledInPlaybackList de un objeto MediaPlaybackItem en false.

Un escenario típico de esta característica es para las aplicaciones que reproducen música transmitida desde Internet. La aplicación puede escuchar los cambios en el estado de conexión de red del dispositivo y deshabilitar la reproducción de elementos que no están totalmente descargados. En el ejemplo siguiente, se registra un controlador para el evento NetworkInformation.NetworkStatusChanged .

Windows.Networking.Connectivity.NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;

En el controlador de NetworkStatusChanged, compruebe si GetInternetConnectionProfile devuelve null, lo que indica que la red no está conectada. Si este es el caso, recorra en bucle todos los elementos de la lista de reproducción y, si totalDownloadProgress para el elemento es inferior a 1, lo que significa que el elemento no se ha descargado completamente, deshabilite el elemento. Si la conexión de red está habilitada, recorra en bucle todos los elementos de la lista de reproducción y habilite cada elemento.

private void NetworkInformation_NetworkStatusChanged(object sender)
{
    if (Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile() == null)
    {
        // Check download status of each item in the list. (TotalDownloadProgress < 1 means not completely downloaded)
        foreach (var item in _mediaPlaybackList.Items)
        {
            if (item.TotalDownloadProgress < 1)
            {
                item.IsDisabledInPlaybackList = true;
            }
        }
    }
    else
    {
        // Connected to internet, re-enable all playlist items
        foreach (var item in _mediaPlaybackList.Items)
        {
            item.IsDisabledInPlaybackList = true;
        }
    }
}

Aplazar el enlace de contenido multimedia para los elementos de una lista de reproducción mediante MediaBinder

En los ejemplos anteriores, se crea un objeto MediaSource a partir de un archivo, una dirección URL o una secuencia, después del cual se crea un objeto MediaPlaybackItem y se agrega a un objeto MediaPlaybackList. En algunos escenarios, como si el usuario se cobra por ver contenido, es posible que desee aplazar la recuperación del contenido de un MediaSource hasta que el elemento de la lista de reproducción esté listo para reproducirse realmente. Para implementar este escenario, cree una instancia de la clase MediaBinder . Establezca la propiedad Token en una cadena definida por la aplicación que identifique el contenido para el que desea aplazar la recuperación y, a continuación, registre un controlador para el evento Binding . A continuación, 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 como de costumbre.

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

Cuando el sistema determina que es necesario recuperar el contenido asociado a MediaBinder , generará el evento Binding . En el controlador de este evento, puede recuperar la instancia de MediaBinder de la clase MediaBindingEventArgs pasada al evento. Recupere la cadena que especificó para la propiedad Token y úsela para determinar qué contenido se debe recuperar. MediaBindingEventArgs proporciona métodos para establecer el contenido enlazado en varias representaciones diferentes, como SetStorageFile, SetStream, SetStreamReference y SetUri.

private void Binder_Binding(MediaBinder sender, MediaBindingEventArgs args)
{
    // Get a deferral if you need to perform async operations
    // var deferral = args.GetDeferral();

    var contentUri = new Uri("http://contoso.com/media/" + args.MediaBinder.Token);
    args.SetUri(contentUri);

    // Call complete after your async operations are complete
    // deferral.Complete();
}

Tenga en cuenta que si está realizando operaciones asincrónicas, como las solicitudes web, en el controlador de eventos Binding , debe llamar al método MediaBindingEventArgs.GetDeferral para indicar al sistema que espere a que se complete la operación antes de continuar. Llame a Deferral.Complete una vez completada la operación para indicar al sistema que continúe.

A partir de Windows 10, versión 1703, puedes proporcionar adaptiveMediaSource como contenido enlazado llamando a SetAdaptiveMediaSource. Para obtener más información sobre el uso de streaming adaptable en la aplicación, consulta Streaming adaptable.