Itens de mídia, playlists e faixas

Este artigo mostra como usar a classe MediaSource, que fornece uma maneira comum de referenciar e reproduzir mídia de diferentes fontes, como arquivos locais ou remotos, e expõe um modelo comum para acessar dados de mídia, independentemente do formato de mídia subjacente. A classe MediaPlaybackItem estende a funcionalidade do MediaSource, permitindo que você gerencie e selecione várias faixas de áudio, vídeo e metadados contidas em um item de mídia. MediaPlaybackList permite que você crie playlists a partir de um ou mais itens de reprodução de mídia.

Criar e executar um MediaSource

Crie uma nova instância do MediaSource chamando um dos métodos de fábrica expostos pela classe:

Depois de criar uma MediaSource, você poderá reproduzi-la com um MediaPlayer definindo a propriedade Source. Desde o Windows 10, versão 1607, você pode atribuir um MediaPlayer a um MediaPlayerElement chamando SetMediaPlayer para renderizar o conteúdo do player de mídia em uma página XAML. Este é o método preferido em relação ao uso de MediaElement. Para obter mais informações sobre como usar MediaPlayer, consulte Reproduzir áudio e vídeo com MediaPlayer.

O exemplo a seguir mostra como reproduzir um arquivo de mídia selecionado pelo usuário um MediaPlayer usando MediaSource.

Você precisará incluir os namespaces Windows.Media.Core e Windows.Media.Playback para completar esse cenário.

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

Declare uma variável de membro do tipo MediaSource. Para os exemplos deste artigo, a fonte de mídia é declarada como membro da classe para poder ser acessada a partir de vários locais.

MediaSource _mediaSource;

Declare uma variável para armazenar o objeto MediaPlayer e, se você quiser renderizar o conteúdo de mídia em XAML, adicione um MediaPlayerElement à página.

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

Para permitir que o usuário selecione um arquivo de mídia para reproduzir, use um FileOpenPicker. Com o objeto StorageFile retornado no método PickSingleFileAsync do seletor, inicialize um novo MediaObject chamando MediaSource.CreateFromStorageFile. Por fim, defina a fonte de mídia como a fonte de reprodução para o MediaElement chamando o 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);
}

Por padrão, o MediaPlayer não começa reproduzido automaticamente quando a origem da mídia é definida. Você pode começar manualmente a reprodução chamando Play.

_mediaPlayer.Play();

Você também pode definir a propriedade AutoPlay do MediaPlayer como true para solicitar ao player que inicie a reprodução assim que a origem da mídia for definida.

_mediaPlayer.AutoPlay = true;

Criar um MediaSource de DownloadOperation

A partir do Windows, versão 1803, você pode criar um objeto MediaSource a partir de um 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);

Observe que, embora você possa criar um MediaSource a partir de um download sem iniciá-lo ou configurar a propriedade IsRandomAccessRequired como true, você deve executar ambas as ações antes de tentar associar MediaSource a MediaPlayer ou MediaPlayerElement para reprodução.

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

Manipular várias faixas de áudio, vídeo e metadados com MediaPlaybackItem

Usar um MediaSource para reprodução é conveniente porque ele fornece uma maneira comum de reproduzir mídia de tipos de fontes diferentes, mas o comportamento mais avançado pode ser acessado criando um MediaPlaybackItem na MediaSource. Isso inclui a capacidade de acessar e gerenciar várias faixas de áudio, vídeo e dados de um item de mídia.

Declare uma variável para armazenar seu MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Crie um MediaPlaybackItem chamando o construtor e passando um objeto MediaSource inicializado.

Se seu aplicativo possui suporte a várias faixas de áudio, vídeo ou dados em um item de reprodução de mídia, registre manipuladores de eventos para os eventos AudioTracksChanged, VideoTracksChanged ou TimedMetadataTracksChanged.

Por fim, defina a origem da reprodução do MediaElement ou MediaPlayer para sua 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);

Observação

Um MediaSource só pode ser associado a um único MediaPlaybackItem. Depois de criar um MediaPlaybackItem a partir de uma fonte, a tentativa de criar outro item de reprodução a partir da mesma fonte resultará em um erro. Além disso, depois de criar um MediaPlaybackItem a partir de uma fonte de mídia, você não pode definir o objeto MediaSource diretamente como a fonte de um MediaPlayer, mas deve usar o MediaPlaybackItem.

O evento VideoTracksChanged é lançado depois que um MediaPlaybackItem que contém várias faixas de vídeos é atribuído como fonte de reprodução, e pode ser gerado novamente se a lista de faixas de vídeo do item for alterada. O manipulador desse evento oferece a oportunidade de atualizar sua interface do usuário para permitir que o usuário alterne entre as faixas disponíveis. Este exemplo usa um ComboBox para exibir as faixas de vídeo disponíveis.

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

No manipulador VideoTracksChanged, execute um loop por todas as faixas na lista VideoTracks do item de reprodução. Para cada faixa, um novo ComboBoxItem é criado. Se a faixa ainda não tiver um rótulo, um rótulo será gerado do índice de faixas. A propriedade Tag do item da caixa de combinação é definida como o índice de faixas para que ele possa ser identificado mais tarde. Por fim, o item é adicionado à caixa de combinação. Observe que essas operações serão executadas em uma chamada CoreDispatcher.RunAsync, pois todas as alterações da interface do usuário devem ser feitas no thread da interface do usuário, e esse evento é lançado em um thread 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);
        }
    });
}

No manipulador SelectionChanged da caixa de combinação, o índice de faixas é recuperado a partir da propriedade Tag do item selecionado. Definir a propriedade SelectedIndex da lista VideoTracks do item de reprodução de mídia faz com que o MediaElement ou o MediaPlayer alterne a faixa de vídeo ativa para o índice especificado.

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

O gerenciamento de itens de mídia com várias faixas de áudio funciona exatamente da mesma forma do que com faixas de vídeo. Manipule o evento AudioTracksChanged para atualizar a interface do usuário com as faixas de áudio encontradas na lista AudioTracks do item de reprodução. Quando o usuário selecionar uma faixa de áudio, defina a propriedade SelectedIndex da lista AudioTracks para fazer com que o MediaElement ou o MediaPlayer alterne a faixa de áudio ativa para o í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;
}

Além de áudio e vídeo, um objeto MediaPlaybackItem pode conter zero ou mais objetos TimedMetadataTrack. Uma faixa de metadados com tempo pode conter texto de legenda ou pode conter dados personalizados que são próprios de seu aplicativo. Uma faixa de metadados com tempo contém uma lista de indicações representadas por objetos herdados de IMediaCue, como DataCue ou TimedTextCue Cada indicação tem uma hora de início e uma duração que determinam quando a indicação será ativada e por quanto tempo.

Semelhante às faixas de áudio e vídeo, as faixas de metadados com tempo de um item de mídia podem ser descobertas manipulando o evento TimedMetadataTracksChanged de um MediaPlaybackItem. Com as faixas de metadados com tempo, porém, o usuário pode habilitar mais de uma faixa de metadados por vez. Além disso, dependendo do cenário do aplicativo, convém habilitar ou desabilitar faixas de metadados automaticamente, sem a intervenção do usuário. Para fins de ilustração, esse exemplo adiciona um ToggleButton para cada controle de metadados em um item de mídia a fim de permitir que o usuário habilite ou desabilite o controle. A propriedade Tag de cada botão está definida no índice do controle de metadados associado para que possa ser identificado quando o botão é acionado.

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

Como mais de uma faixa de metadados pode estar ativa por vez, você simplesmente não define o índice ativo para a lista de faixas de metadados. Em vez disso, chame o método SetPresentationMode do objeto MediaPlaybackItem, passando o índice da faixa que você deseja alternar e fornecendo um valor da enumeração TimedMetadataTrackPresentationMode. O modo de apresentação que você escolher depende da implementação de seu aplicativo. Neste exemplo, a faixa de metadados é definida como PlatformPresented quando habilitada. Para controles com base em texto, isso significa que o sistema exibe automaticamente as indicações de texto no controle. Quando o botão de alternância é desativado, o modo de apresentação é definido como Desabilitado, ou seja, nenhum texto é exibido e nenhum evento de indicação é gerado. Os eventos de indicação são explicados mais adiante neste artigo.

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

Na medida em que processa as faixas de metadados, você pode acessar o conjunto de indicações dentro da faixa acessando a propriedade Cues ou ActiveCues. Você pode fazer isso para atualizar a interface do usuário a fim de mostrar os locais de indicação de um item de mídia.

Manipular codecs incompatíveis e erros desconhecidos durante a abertura de itens de mídia

Desde o Windows 10, versão 1607, você pode verificar se o codec necessário para reprodução de um item de mídia é compatível ou parcialmente compatível com o dispositivo no qual o aplicativo está em execução. No manipulador de evento eventos com alteração controlada de MediaPlaybackItem, como AudioTracksChanged, primeiro verifique se o controle de alterações é uma inserção do novo controle. Caso afirmativo, você pode obter uma referência para que continue inserido usando o índice passado no parâmetro IVectorChangedEventArgs.Index com a coleção de controle adequada do parâmetro MediaPlaybackItem, como a coleção AudioTracks.

Quando você tem uma referência à faixa inserida, verifique o DecoderStatus da propriedade SupportInfo da faixa. Se o valor for FullySupported, o codec adequado necessário para reproduzir a faixa estará presente no dispositivo. Se o valor for Degraded, a faixa poderá ser reproduzida pelo sistema, mas a reprodução será degradada de alguma forma. Por exemplo, uma faixa de áudio 5.1 pode ser reproduzida como estéreo em 2 canais. Se esse for o caso, será necessário atualizar a interface do usuário para alertar o usuário da degradação. Se o valor for UnsupportedSubtype ou UnsupportedEncoderProperties, a faixa não poderá ser reproduzida em todos os codecs atuais no dispositivo. É necessário alertar o usuário e ignorar a reprodução do item ou implementar a interface do usuário para permitir que o usuário baixe o codec correto. O método GetEncodingProperties da faixa pode ser usado para determinar o codec necessário para reprodução.

Por fim, você pode se registrar no evento OpenFailed da faixa, que será acionado se a faixa for compatível com o dispositivo, mas deixará de ser aberta por causa de um erro desconhecido no pipeline.

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

}

No manipulador de eventos OpenFailed, você pode verificar se o status MediaSource é desconhecido e, em caso afirmativo, selecionar programaticamente uma faixa diferente a ser reproduzida, permitir que o usuário escolha uma faixa diferente ou abandone a reprodução.

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

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

Definir propriedades de exibição usadas pelos controles de transporte de mídia do sistema

Desde o Windows 10, versão 1607, a mídia reproduzida em um MediaPlayer é integrada automaticamente aos controles de transporte de mídia do sistema (SMTC) por padrão. Você pode especificar os metadados que serão exibidos pelo SMTC atualizando as propriedades de exibição de um MediaPlaybackItem. Obtenha um objeto representando as propriedades de exibição para um item chamando GetDisplayProperties. Defina se o item de reprodução é música ou vídeo configurando a propriedade Type. Em seguida, defina as propriedades de VideoProperties ou MusicProperties do objeto. Chame ApplyDisplayProperties para atualizar as propriedades do item para os valores fornecidos. Normalmente, um aplicativo recuperará os valores de exibição dinamicamente de um serviço web, mas o exemplo a seguir ilustra esse processo com valores codificados.

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

Adicionar texto com tempo externo com TimedTextSource

Para alguns cenários, você pode ter arquivos externos que contenham texto com tempo associado a um item de mídia, como arquivos separados que contêm legendas para localidades diferentes. Use a classe TimedTextSource para carregar em arquivos de texto com tempo externos de um fluxo ou URI.

Este exemplo usa uma coleção Dictionary para armazenar uma lista das fontes de texto com tempo para o item de mídia usando o URI da fonte e o objeto TimedTextSource como o par de chave/valor para identificar as faixas depois que elas forem resolvidas.

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Crie um novo TimedTextSource para cada arquivo de texto com tempo externo chamando CreateFromUri. Adicione uma entrada ao Dictionary para a fonte de texto com tempo. Adicione um manipulador para o evento TimedTextSource.Resolved a ser manipulado se o item não for carregado ou para definir propriedades adicionais depois que o item for carregado com êxito.

Registre todos os seus objetos TimedTextSource com o MediaSource adicionando-os à coleção ExternalTimedTextSources. Observe que fontes de texto com tempo externas são adicionadas diretamente ao MediaSource e não ao MediaPlaybackItem criado a partir da fonte. Para atualizar a interface do usuário para refletir as faixas de texto externo, registre e manipule o evento TimedMetadataTracksChanged conforme descrito anteriormente neste artigo.

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

No manipulador para o evento TimedTextSource.Resolved, verifique a propriedade Error do TimedTextSourceResolveResultEventArgs passado para o manipulador para determinar se ocorreu um erro ao tentar carregar os dados de texto com tempo. Se o item for resolvido com êxito, você pode usar esse manipulador para atualizar as propriedades adicionais do controle resolvido. Esse exemplo adiciona um rótulo para cada controle com base no URI armazenado anteriormente no Dicionário.

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

Adicionar mais faixas de metadados

Você pode criar faixas de metadados personalizadas dinamicamente no código e associá-las a uma fonte de mídia. As faixas que você cria podem conter texto de legenda ou podem conter seus dados próprios do aplicativo.

Crie um novo TimedMetadataTrack chamando o construtor e especificando um ID, o identificador do idioma e um valor da enumeração TimedMetadataKind. Registre manipuladores para os eventos CueEntered e CueExited. Esses eventos são lançados na chegada da hora de início de uma indicação e quando a duração de uma indicação tiver expirado, respectivamente.

Crie um novo objeto de indicação, adequado para o tipo de controle de metadados criado, e defina a ID, a hora de início e a duração do controle. Esse exemplo cria um controle de dados, portanto, um conjunto de objetos DataCue é gerado e um buffer contendo dados específicos do aplicativo é fornecido para cada indicação. Para registrar a nova faixa, adicione-a à coleção ExternalTimedMetadataTracks do objeto MediaSource.

A partir do Windows 10, versão 1703, a propriedade DataCue.Properties expõe um PropertySet que você pode usar para armazenar propriedades personalizadas em pares de chave/dados que podem ser recuperados nos eventos CueEntered e 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);

O evento CueEntered é gerado quando a hora de início de uma indicação é atingida, desde que a faixa associada tenha um modo de apresentação de ApplicationPresented, Hidden ou PlatformPresented. Os eventos de indicação não são gerados para faixas de metadados enquanto o modo de apresentação da faixa é Desabilitado. Este exemplo simplesmente gera os dados personalizados associados à indicação para a janela de depuração.

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

Este exemplo adiciona uma faixa de texto personalizada especificando TimedMetadataKind.Caption ao criar a faixa e usando objetos TimedTextCue para adicionar indicações à faixa.

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

Reproduzir uma lista de itens de mídia com MediaPlaybackList

MediaPlaybackList permite criar uma playlist de itens de mídia, que são representados por objetos MediaPlaybackItem.

Nota Os itens em uma MediaPlaybackList são renderizados usando a reprodução sem lacunas. O sistema usará metadados fornecidos em arquivos MP3 ou AAC codificados para determinar o atraso ou a compensação de preenchimento necessária à reprodução sem intervalos. Se os arquivos MP3 ou AAC codificados não fornecerem esses metadados, o sistema determinará o atraso ou o preenchimento heuristicamente. Para os formatos sem perdas, como PCM, FLAC ou ALAC, o sistema não executa nenhuma ação porque esses codificadores não apresentam atraso ou preenchimento.

Para começar, declare uma variável para armazenar seu MediaPlaybackList.

MediaPlaybackList _mediaPlaybackList;

Crie um MediaPlaybackItem para cada item de mídia que você deseja adicionar à sua lista usando o mesmo procedimento descrito anteriormente neste artigo. Inicialize seu objeto MediaPlaybackList e adicione os itens de reprodução de mídia a ele. Registre um manipulador para o evento CurrentItemChanged. Esse evento permite atualizar a interface do usuário para refletir o item de mídia que está sendo reproduzido. Você também pode registrar o evento ItemOpened, que é acionado quando um item na lista é aberto com êxito, e o evento ItemFailed, que é acionado quando um item na lista não pode ser aberto.

A partir do Windows 10, versão 1703, você pode especificar o número máximo de objetos MediaPlaybackItem na MediaPlaybackList que o sistema manterá aberto depois que eles tiverem sido reproduzidos por meio da definição da propriedade MaxPlayedItemsToKeepOpen propriedade. Quando um MediaPlaybackItem é mantido aberto, a reprodução do item pode começar instantaneamente quando o usuário alterna para esse item porque o item não precisa ser recarregado. Mas manter itens abertos também aumenta o consumo de memória do seu aplicativo, portanto, você deve considerar o equilíbrio entre a capacidade de resposta e o uso de memória ao definir esse valor.

Para habilitar a reprodução da sua lista, defina a origem da reprodução do MediaPlayer para sua 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);

No manipulador de eventos CurrentItemChanged, atualize a interface do usuário para refletir o item atualmente em reprodução, que pode ser recuperado usando a propriedade NewItem do objeto CurrentMediaPlaybackItemChangedEventArgs passado para o evento. Lembre-se: se você atualizar a interface do usuário a partir desse evento, deverá fazer isso chamando CoreDispatcher.RunAsync para que as atualizações sejam feitas no thread da interface do usuário.

A partir do Windows 10, versão 1703, você pode marcar a propriedade CurrentMediaPlaybackItemChangedEventArgs.Reason para obter um valor que indica o motivo pelo qual que o item foi alterado, como o app alternar itens de maneira programática, o item anterior chegar ao seu final ou um erro ocorrer.

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

Chame MovePrevious ou MoveNext para fazer o player de mídia reproduzir o item anterior ou o próximo na MediaPlaybackList.

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

Defina a propriedade ShuffleEnabled para especificar se o media player deve reproduzir os itens da lista em ordem aleatória.

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

Defina a propriedade AutoRepeatEnabled para especificar se o media player deve reproduzir sua lista em loop.

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

Manipular a falha de itens de mídia em uma playlist

O evento ItemFailed é acionado quando um item na lista deixa de abrir. A propriedade ErrorCode do objeto MediaPlaybackItemError passado para o manipulador enumera a causa específica da falha quando possível, inclusive erros de rede, de decodificação ou de criptografia.

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

Desabilitar a reprodução de itens em uma playlist

A partir do Windows 10, versão 1703, você pode desabilitar a reprodução de um ou mais itens em uma MediaPlaybackItemList ao definir a propriedade IsDisabledInPlaybackList de um MediaPlaybackItem como falsa.

Um cenário típico para esse recurso é para apps que reproduzem música transmitida da Internet. O aplicativo pode ouvir as alterações no status de conexão de rede do dispositivo e desabilitar a reprodução de itens que não são completamente baixados. No exemplo a seguir, um manipulador é registrado para o evento NetworkInformation.NetworkStatusChanged.

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

No manipulador para NetworkStatusChanged, verifique se GetInternetConnectionProfile retorna null, o que indica que a rede não está conectada. Se esse for o caso, percorra todos os itens da playlist em loop e, se TotalDownloadProgress para o item for menor do que 1, o que significa que o item não foi completamente baixado, desabilite o item. Se a conexão de rede estiver habilitada, percorra todos os itens na playlist em loop e habilite cada item.

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

Adie a associação de conteúdo de mídia para itens em uma playlist usando o MediaBinder

Nos exemplos anteriores, uma MediaSource é criada de um arquivo, URL ou streaming, depois do qual um MediaPlaybackItem é criado e adicionado a uma MediaPlaybackList. Para alguns cenários, por exemplo, se o usuário está sendo cobrado pela exibição de conteúdo, talvez você queira adiar a recuperação do conteúdo de uma MediaSource até que o item da playlist esteja pronto para ser reproduzido. Para implementar esse cenário, crie uma instância da classe MediaBinder. Defina a propriedade Token como uma cadeia de caracteres definida pelo aplicativo que identifica o conteúdo para o qual você deseja adiar a recuperação e, em seguida, registre um manipulador para o evento Binding. Em seguida, crie uma MediaSource do Binder ao chamar MediaSource.CreateFromMediaBinder. Em seguida, crie um MediaPlaybackItem da MediaSource e o adicione à playlist normalmente.

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

Quando o sistema determina que o conteúdo associado a MediaBinder precisa ser recuperado, ele gerará o evento Binding. No manipulador para este evento, você pode recuperar a instância MediaBinder dos MediaBindingEventArgs passados para o evento. Recupere a cadeia de caracteres especificada para a propriedade Token e use-a para determinar qual conteúdo deve ser recuperado. MediaBindingEventArgs fornece métodos para a configuração do conteúdo associado em diversas representações diferentes, incluindo SetStorageFile, SetStream, SetStreamReference e 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();
}

Observe que, se você estiver executando operações assíncronas, como solicitações da Web, no manipulador de eventos Binding, você deve chamar o método MediaBindingEventArgs.GetDeferral para instruir o sistema a aguardar até que a operação seja concluída antes de continuar. Chame Deferral.Complete depois que a operação estiver concluída para instruir o sistema a continuar.

A partir do Windows 10, versão 1703, você pode fornecer uma AdaptiveMediaSource como conteúdo associado ao chamar SetAdaptiveMediaSource. Para saber mais sobre como usar o streaming adaptável em seu app, veja Streaming adaptável.