Elementos multimedia, listas de reproducción y pistas

En este artículo se muestra cómo usar la clase MediaSource, que proporciona una manera común de hacer referencia y reproducir medios desde diferentes orígenes, como archivos locales o remotos, y expone un modelo común para acceder a datos multimedia, independientemente del formato multimedia subyacente. La clase MediaPlaybackItem amplía la funcionalidad de MediaSource, lo que le permite administrar y seleccionar varias pistas de audio, vídeo y metadatos contenidas en un elemento multimedia. MediaPlaybackList permite crear listas de reproducción a partir de uno o varios elementos de reproducción multimedia.

Creación y reproducción de un objeto MediaSource

Cree una nueva instancia de MediaSource llamando a uno de los métodos de fábrica expuestos por la clase:

Después de crear un objeto MediaSource, puede reproducirlo con un MediaPlayer estableciendo la propiedad Source. A partir de Windows 10, versión 1607, puede asignar un MediaPlayer a un MediaPlayerElement llamando a SetMediaPlayer para reproducir el contenido del reproductor multimedia en una página XAML. Este es el método preferido en lugar de utilizar MediaElement. Para obtener más información sobre cómo usar MediaPlayer, consulte Reproducción de audio y vídeo con MediaPlayer.

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

Deberá incluir los espacios de nombres Windows.Media.Core y Windows.Media.Playback para completar este escenario.

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

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

MediaSource _mediaSource;

Declare una variable para almacenar el objeto MediaPlayer y, si desea reproducir el contenido multimedia en XAML, agregue un control MediaPlayerElement a la página.

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

Para permitir que el usuario elija un archivo multimedia para reproducirlo, use FileOpenPicker. Con el objeto StorageFile devuelto desde el método PickSingleFileAsync del selector, inicialice un nuevo MediaObject llamando a MediaSource.CreateFromStorageFile. Por último, establezca el origen multimedia como origen de reproducción para MediaElement llamando 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 forma predeterminada, MediaPlayer no comienza a reproducir automáticamente cuando se establece el origen multimedia. Puedes iniciar manualmente la reproducción llamando a Play.

_mediaPlayer.Play();

También puede establecer la propiedad AutoPlay de MediaPlayer en true para indicar al reproductor que empiece a reproducir en cuanto se establezca el origen multimedia.

_mediaPlayer.AutoPlay = true;

Creación de un objeto MediaSource a partir de DownloadOperation

A partir de Windows, versión 1803, puede crear un objeto MediaSource a partir de 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);

Tenga en cuenta que, aunque puede crear un MediaSource desde una descarga sin iniciarlo o establecer su propiedad IsRandomAccessRequired en true, debe hacer ambas cosas antes de intentar conectar MediaSource to a MediaPlayer o MediaPlayerElement para su reproducción.

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

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

El uso de MediaSource para la reproducción resulta cómodo porque proporciona una manera común de reproducir elementos multimedia de diferentes tipos de orígenes, pero se puede tener acceso a un comportamiento más avanzado mediante la creación de un MediaPlaybackItem desde MediaSource. Esto incluye la capacidad de acceder y administrar varias pistas de audio, vídeo y datos para un elemento multimedia.

Declare una variable para almacenar MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Cree un MediaPlaybackItem llamando al constructor y transfiriendo un objeto MediaSource inicializado.

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

Por último, establezca el origen de reproducción de MediaElement o MediaPlayer en 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 MediaSource solo se puede asociar a un único MediaPlaybackItem. Después de crear un MediaPlaybackItem desde un origen, al intentar crear otro elemento de reproducción desde el mismo origen se producirá un error. Además, después de crear un MediaPlaybackItem desde un origen multimedia, no puedes establecer el objeto MediaSource directamente como origen de MediaPlayer, en su lugar debe usar MediaPlaybackItem.

El evento VideoTracksChanged se genera después de que un MediaPlaybackItem que contenga varias pistas de vídeo se asigne como origen de reproducción y se puede volver a generar si la lista de pistas de vídeo cambia para el elemento. El controlador de este evento le permite actualizar la interfaz de usuario para que el usuario cambie entre las pistas disponibles. En este ejemplo se usa un ComboBox para mostrar las pistas de vídeo disponibles.

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

En el controlador VideoTracksChanged, recorre en bucle todas las pistas de la lista VideoTracks del elemento de reproducción. Para cada pista, se crea un nuevo ComboBoxItem is created. Si la pista aún no tiene una etiqueta, se genera una etiqueta a partir del índice de pista. La propiedad Tag del elemento de cuadro combinado se establece en el índice de pista para que se pueda identificar más adelante. Por último, el elemento se agrega al cuadro combinado. Tenga en cuenta que estas operaciones se realizan dentro de una llamada CoreDispatcher.RunAsync porque todos los cambios de la interfaz de usuario deben realizarse en el subproceso de la interfaz de usuario y este evento se genera 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 pista se recupera de la propiedad Tag del elemento seleccionado. Establecer la propiedad SelectedIndex de la lista VideoTracks del elemento de reproducción multimedia hace que MediaElement o MediaPlayer cambien 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 igual que con las pistas de vídeo. Controle 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, establezca la propiedad SelectedIndex de la lista AudioTracks para hacer que MediaElement o MediaPlayer cambien 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 del audio y el vídeo, un objeto MediaPlaybackItem puede contener cero o más objetos TimedMetadataTrack. Una pista de metadatos con tiempo puede contener subtítulos o leyendas, o puede contener datos personalizados que sean propietarios de la aplicación. Una pista de metadatos con tiempo contiene una lista de indicaciones representadas por objetos que heredan de IMediaCue, como DataCue o TimedTextCue. Cada indicación tiene una hora de inicio y una duración que determinan cuándo se activa la indicación y durante cuánto tiempo.

Al igual que las pistas de audio y de vídeo, las pistas de metadatos con tiempo de un elemento multimedia se pueden detectar controlando el evento TimedMetadataTracksChanged de un MediaPlaybackItem. Sin embargo, con las pistas de metadatos con tiempo, puede que el usuario desee 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 quiera habilitar o deshabilitar las pistas de metadatos automáticamente, sin intervención del usuario. Con fines ilustrativos, en este ejemplo se agrega un ToggleButton para cada pista de metadatos de un elemento multimedia para permitir al usuario habilitar y deshabilitar 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 active 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 basta con establecer el índice activo de la lista de pistas de metadatos. En su lugar, llame al método SetPresentationMode del objeto MediaPlaybackItem, transfiriendo el índice de la pista que desea alternar y, a continuación, proporcionando un valor de la enumeración TimedMetadataTrackPresentationMode. El modo de presentación que elija 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 ningún 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);

A medida que procesa las pistas de metadatos, puede acceder al conjunto de indicaciones dentro de la pista accediendo a las propiedades Cues o ActiveCues. Puede hacerlo para actualizar la interfaz de usuario para mostrar las ubicaciones de indicaciones de un elemento multimedia.

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

A partir de Windows 10, versión 1607, puede comprobar si el códec necesario para reproducir un elemento multimedia es compatible total o parcialmente con 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 nueva pista. Si es así, puede obtener una referencia a la pista que se va a insertar mediante el índice transferido al parámetro IVectorChangedEventArgs.Index con la colección de pistas adecuada del parámetro MediaPlaybackItem, como la colección AudioTracks.

Una vez que tenga una referencia a la pista insertada, compruebe DecoderStatus de la propiedad SupportInfo de la pista. Si el valor es FullySupported, significa que el códec adecuado necesario para reproducir la pista está presente en el dispositivo. Si el valor es Degraded, significa que 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 en estéreo de 2 canales. Si este es el caso, es posible que quiera actualizar la interfaz de usuario para avisar al usuario de la degradación. Si el valor es UnsupportedSubtype o UnsupportedEncoderProperties, significa que la pista no se puede reproducir en absoluto con los códecs actuales en el dispositivo. Es posible que desee avisar al usuario y omitir la reproducción del elemento o implementar la interfaz de usuario para permitir que el usuario descargue el códec correcto. Se puede usar el método GetEncodingProperties de la pista para determinar el códec necesario para la reproducción.

Por último, puede registrarse para el evento OpenFailed de la pista, que se generará si la pista es compatible con 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 de eventos OpenFailed, puede comprobar si el estado de MediaSource es desconocido y, si es así, puede seleccionar una pista diferente mediante programación para reproducir, permitir al usuario elegir 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);
    }
}

Establecimiento de las propiedades de visualización que usan los controles de transporte multimedia del sistema

A partir de Windows 10, versión 1607, los medios reproducidos en MediaPlayer se integran automáticamente en los controles de transporte multimedia del sistema (SMTC) de forma predeterminada. Puede especificar los metadatos que mostrará el SMTC actualizando las propiedades de visualización de un MediaPlaybackItem. Para obtener un objeto que represente las propiedades de visualización de un elemento, llame a GetDisplayProperties. Establezca si el elemento de reproducción es música o vídeo estableciendo la propiedad Type. A continuación, establezca las propiedades VideoProperties o MusicProperties del objeto. Llame a ApplyDisplayProperties para actualizar las propiedades del elemento a los valores proporcionados. Normalmente, una aplicación recuperará los valores de visualización dinámicamente desde un servicio web, pero en el ejemplo siguiente se muestra este proceso con valores codificados de forma fija.

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

Adición de texto con tiempo externo con TimedTextSource

En algunos escenarios, es posible que tenga archivos externos que contengan texto con tiempo asociado a un elemento multimedia, como archivos independientes que contienen subtítulos para diferentes configuraciones regionales. Use la clase TimedTextSource para cargar archivos de texto con tiempo externo desde una secuencia o un URI.

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

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Cree un TimedTextSource para cada archivo de texto con tempo llamando a CreateFromUri. Agregue una entrada a Dictionary para el origen de texto con tiempo. Agregue un controlador para el evento TimedTextSource.Resolved para controlar si el elemento no se pudo cargar o para establecer propiedades adicionales después de que el elemento se haya cargado correctamente.

Registre todos los objetos TimedTextSource con MediaSource agregándolos a la colección ExternalTimedTextSources. Tenga en cuenta que los orígenes de texto con tiempo externos se agregan directamente a MediaSource y no a MediaPlaybackItem creados a partir del origen. Para actualizar la interfaz de usuario para reflejar las pistas de texto externo, registre y controle el evento TimedMetadataTracksChanged, tal como se ha descrito 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, compruebe la propiedad Error del elemento TimedTextSourceResolveResultEventArgs transferido al controlador para determinar si se produjo un error al intentar cargar los datos de texto con tiempo. 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";
    }
}

Para obtener una lista de los formatos de texto con tiempo que se admiten en Windows, consulte Códecs admitidos.

Adición de pistas de metadatos adicionales

Puede crear dinámicamente pistas de metadatos personalizadas en el código y asociarlas a un origen multimedia. Las pistas que cree pueden contener subtítulos o leyendas, o pueden contener los datos de la aplicación propietaria.

Cree un nuevo elemento TimedMetadataTrack llamando al constructor y especificando un identificador, el identificador de idioma y un valor de la enumeración TimedMetadataKind. Registre controladores para los eventos CueEntered y CueExited. Estos eventos se generan cuando se ha alcanzado la hora de inicio de una indicación y cuando la duración de una indicación ha caducado, respectivamente.

Cree un nuevo objeto de indicación, adecuado para el tipo de pista 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 nueva pista, agréguela a la colección ExternalTimedMetadataTracks del objeto MediaSource.

A partir de Windows 10, versión 1703, la propiedad DataCue.Properties expone un PropertySet que puede usar para almacenar propiedades personalizadas en pares de 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 alcanza la hora de inicio de una indicación, siempre que 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 si el modo de presentación de la pista es Disabled. En este ejemplo simplemente se generan los datos personalizados asociados a la indicación en la ventana de depuració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 personalizada especificando TimedMetadataKind.Caption al crear 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);
}

Reproducción de una lista de elementos multimedia con MediaPlaybackList

MediaPlaybackList le permite crear una lista de reproducción de elementos multimedia, que se representan mediante objetos MediaPlaybackItem.

Nota Los elementos de una MediaPlaybackList se representan mediante la reproducción sin pausas. El sistema usará los metadatos proporcionados en archivos codificados en MP3 o AAC para determinar la compensación de retraso o relleno necesaria para la reproducción sin pausas. Si los archivos codificados MP3 o AAC no proporcionan estos metadatos, el sistema determina el retraso o relleno heurísticamente. En el caso de los formatos sin pérdida de información, como PCM, FLAC o ALAC, el sistema no realiza ninguna acción porque estos codificadores no introducen retrasos ni rellenos.

Para empezar, declare una variable para almacenar MediaPlaybackList.

MediaPlaybackList _mediaPlaybackList;

Cree un MediaPlaybackItem para cada elemento multimedia que quiera agregar a la lista mediante el mismo procedimiento descrito anteriormente en este artículo. Inicialice el objeto MediaPlaybackList y agréguele los elementos de reproducción multimedia. Registre un controlador para el evento CurrentItemChanged. Este evento permite actualizar la interfaz de usuario para reflejar el elemento multimedia que se está reproduciendo actualmente. También puede registrarse para el evento ItemOpened, que se genera cuando se abre correctamente un elemento de la lista 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á abiertos después de que se hayan reproducido, estableciendo la propiedad MaxPlayedItemsToKeepOpen. Cuando se mantiene abierto un MediaPlaybackItem, la reproducción del elemento puede iniciarse instantáneamente cuando el usuario cambia a ese elemento porque no es necesario volver a cargar el elemento. Pero mantener abiertos los elementos también aumenta el consumo de memoria de la aplicación, por lo que debe tener en cuenta 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 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, actualice la interfaz de usuario para reflejar el elemento que se está reproduciendo actualmente, que se puede recuperar mediante la propiedad NewItem del objeto CurrentMediaPlaybackItemChangedEventArgs transferido al evento. Recuerde que si actualiza la interfaz de usuario de este evento, debe hacerlo dentro de una llamada a CoreDispatcher.RunAsync para que las actualizaciones se realicen en el subproceso de la interfaz de usuario.

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

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

Llame a MovePrevious o MoveNext para hacer que el reproductor multimedia reproduzca el elemento anterior o siguiente de MediaPlaybackList.

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

Establezca la propiedad ShuffleEnabled para especificar si el reproductor 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;
    });
}

Establezca la propiedad AutoRepeatEnabled para especificar si el reproductor multimedia debe reproducir en bucle 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;
    });
}

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

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

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

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

A partir de Windows 10, versión 1703, puede deshabilitar la reproducción de uno o varios elementos de MediaPlaybackItemList estableciendo la propiedad IsDisabledInPlaybackList de 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 completamente 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 nulo, 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 el valor de TtotalDownloadProgress para el elemento es menor que 1, 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;
        }
    }
}

Aplazamiento del enlace del contenido multimedia para los elementos de una lista de reproducción mediante MediaBinder

En los ejemplos anteriores, se creó un MediaSource a partir de un archivo, una dirección URL o una secuencia y después se creó un MediaPlaybackItem y se agregó a MediaPlaybackList. En algunos escenarios, como si el usuario se está cobrando por ver contenido, es posible que desee aplazar la recuperación del contenido de 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 cuya recuperación desee aplazar y, a continuación, registre un controlador para el evento Binding. A continuación, cree un MediaSource desde Binder llamando a MediaSource.CreateFromMediaBinder. A continuación, cree un MediaPlaybackItem desde MediaSource y agréguelo a la lista de reproducción de la forma habitual.

_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 el contenido asociado a MediaBinder debe recuperarse, genera el evento Binding. En el controlador de este evento, puede recuperar la instancia de MediaBinder del elemento MediaBindingEventArgs transferido 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 solicitudes web, en el controlador de eventos Binding, debe llamar al método MediaBindingEventArgs.GetDeferral para indicar al sistema que debe esperar 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, puede proporcionar un AdaptiveMediaSource como contenido enlazado llamando a SetAdaptiveMediaSource. Para obtener más información sobre el uso del streaming adaptable en la aplicación, consulte Streaming adaptable.