Compartilhar via


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 entre várias faixas de áudio, vídeo e metadados contidas em um item de mídia. MediaPlaybackList permite que você crie listas de reprodução a partir de um ou mais itens de reprodução de mídia.

Criar e reproduzir um MediaSource

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

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

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

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

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

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

MediaSource _mediaSource;

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

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

Para permitir que o usuário escolha um arquivo de mídia para reproduzir, use um FileOpenPicker. Com o objeto StorageFile retornado do método PickSingleFileAsync do seletor, inicialize um novo MediaObject chamando MediaSource.CreateFromStorageFile. Finalmente, 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 a ser reproduzido automaticamente quando a fonte de mídia é definida. Você pode iniciar manualmente a reprodução chamando Play.

_mediaPlayer.Play();

Você também pode definir a propriedade AutoPlay do MediaPlayer como "true" para informar ao player para começar a reproduzir assim que a fonte de mídia for definida.

_mediaPlayer.AutoPlay = true;

Criar um MediaSource a partir de um 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 definir sua propriedade IsRandomAccessRequired como true, você deve fazer as duas coisas antes de tentar anexar o MediaSource a um MediaPlayer ou MediaPlayerElement para reprodução.

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

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

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

Declare uma variável para armazenar o MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Crie um MediaPlaybackItem chamando o construtor e enviando em um objeto MediaSource inicializado.

Se seu aplicativo oferecer 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.

Finalmente, defina a origem de reprodução do MediaElement ou MediaPlayer para seu 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 origem, a tentativa de criar outro item de reprodução a partir da mesma origem resultará em um erro. Além disso, depois de criar um MediaPlaybackItem de uma fonte de mídia, você não pode definir o objeto MediaSource diretamente como a origem de um MediaPlayer, mas deve usar o MediaPlaybackItem.

O evento VideoTracksChanged é gerado depois que um MediaPlaybackItem contendo várias faixas de vídeo é atribuído como uma fonte de reprodução e pode ser gerado novamente se a lista de faixas de vídeo for alterada para o item. O manipulador desse evento permite 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, faça 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 a partir do índice da faixa. A propriedade Tag do item da caixa de combinação é definida como o índice de faixa para que possa ser identificada posteriormente. Finalmente, o item é adicionado à caixa de combinação. Observe que essas operações são executadas em uma chamada CoreDispatcher.RunAsync porque todas as alterações da interface do usuário devem ser feitas no thread da interface do usuário e esse evento é gerado 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 faixa é recuperado 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 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 que com faixas de vídeo. Manipule o AudioTracksChanged para atualizar sua interface do usuário com as faixas de áudio encontradas na lista AudioTracks do item de reprodução. Quando o usuário seleciona uma faixa de áudio, defina a propriedade SelectedIndex da lista AudioTracks para fazer com que o MediaElement ou 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 cronometrada pode conter texto de legenda ou legenda ou pode conter dados personalizados que são proprietários do seu aplicativo. Uma faixa de metadados cronometrada contém uma lista de sugestões representadas por objetos que herdam do IMediaCue, como um DataCue ou um TimedTextCue. Cada sugestão tem uma hora de início e uma duração que determina quando a sugestão é ativada e por quanto tempo.

Semelhante às faixas de áudio e de vídeo, as faixas de metadados cronometradas de um item de mídia podem ser descobertas manipulando o evento TimedMetadataTracksChanged de um MediaPlaybackItem. No entanto, com as faixas de metadados cronometradas, o usuário pode querer habilitar mais de uma faixa de metadados ao mesmo tempo. Além disso, dependendo do cenário do aplicativo, convém habilitar ou desabilitar as faixas de metadados automaticamente, sem intervenção do usuário. Para fins de ilustração, este exemplo adiciona um ToggleButton para cada faixa de metadados em um item de mídia para permitir que o usuário habilite e desabilite a faixa. A propriedade Tag de cada botão é definida como o índice da faixa de metadados associada para que possa ser identificada quando o botão for alternado.

<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 ao mesmo tempo, não defina simplesmente o índice ativo para a lista de faixas de metadados. Em vez disso, chame o método MediaPlaybackItem do SetPresentationMode, enviando no índice da faixa que você deseja alternar e, em seguida, fornecendo um valor da enumeração TimedMetadataTrackPresentationMode. O modo de apresentação escolhido depende da implementação do seu aplicativo. Neste exemplo, a faixa de metadados é definida como PlatformPresented quando habilitada. Para faixas baseadas em texto, isso significa que o sistema exibirá automaticamente as sugestões de texto na faixa. Quando o botão de alternância é desativado, o modo de apresentação é definido como Desabilitado, o que significa que nenhum texto é exibido e nenhum evento de sinalização é gerado. Os eventos de sugestão são discutidos posteriormente 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);

À medida que você processar as faixas de metadados, poderá acessar o conjunto de sugestões dentro da faixa acessando as propriedades Cues ou ActiveCues. Você pode fazer isso para atualizar sua interface do usuário para mostrar os locais de sugestão de um item de mídia.

Manipular codecs não suportados e erros desconhecidos ao abrir itens de mídia

A partir do Windows 10, versão 1607, você pode verificar se o codec necessário para reproduzir um item de mídia tem suporte total ou parcial no dispositivo no qual seu aplicativo está sendo executado. No manipulador de eventos para os eventos alterados por faixas MediaPlaybackItem, como AudioTracksChanged, primeiro verifique se a alteração de faixa é uma inserção de uma nova faixa. Nesse caso, você pode obter uma referência à faixa que está sendo inserida usando o índice enviado no parâmetro IVectorChangedEventArgs.Index com a coleção de faixas apropriada do parâmetro MediaPlaybackItem, como a coleção AudioTracks.

Depois de referenciar a faixa inserida, verifique o DecoderStatus da propriedade SupportInfo da faixa. Se o valor for FullySupported, o codec apropriado 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 de 2 canais. Se esse for o caso, convém atualizar sua interface do usuário para alertar o usuário sobre a degradação. Se o valor for UnsupportedSubtype ou UnsupportedEncoderProperties, a faixa não poderá ser reproduzida com os codecs atuais no dispositivo. Você pode querer 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.

Finalmente, você pode se registrar para o evento OpenFailed da faixa, que será gerado se a faixa for suportada no dispositivo, mas não conseguir abrir devido a 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 do MediaSource é desconhecido e, em caso afirmativo, pode selecionar programaticamente uma faixa diferente para reproduzir, permitir que o usuário escolha uma faixa diferente ou abandonar 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

A partir do Windows 10, versão 1607, a mídia reproduzida em um MediaPlayer é automaticamente integrada 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 que representa as propriedades de exibição de um item chamando GetDisplayProperties. Defina se o item de reprodução é música ou vídeo definindo 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 cronometrado externo com TimedTextSource

Para alguns cenários, você pode ter arquivos externos que contém texto cronometrado 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 cronometrados externos de um fluxo ou URI.

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

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Crie um novo TimedTextSource para cada arquivo de texto cronometrado externo chamando CreateFromUri. Adicione uma entrada ao Dicionário para a fonte de texto cronometrada. Adicione um manipulador para o evento TimedTextSource.Resolved para manipular se o item falhou ao carregar ou para definir propriedades adicionais depois que o item foi carregado com êxito.

Registre todos os objetos TimedTextSource com o MediaSource adicionando-os à coleção ExternalTimedTextSources. Observe que fontes de texto cronometradas externas são adicionadas diretamente ao MediaSource e não ao MediaPlaybackItem criado a partir da origem. Para atualizar sua interface do usuário para refletir as faixas de texto externas, 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 do evento TimedTextSource.Resolved, verifique a propriedade Error do TimedTextSourceResolveResultEventArgs enviado ao manipulador para determinar se ocorreu um erro ao tentar carregar os dados de texto cronometrados. Se o item foi resolvido com êxito, você pode usar esse manipulador para atualizar propriedades adicionais da faixa resolvida. Este exemplo adiciona um rótulo para cada faixa 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";
    }
}

Para obter uma lista dos formatos de texto cronometrados suportados no Windows, consulte Codecs suportados.

Adicionar faixas de metadados adicionais

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

Crie um novo TimedMetadataTrack chamando o construtor e especificando uma ID, o identificador de idioma e um valor da enumeração TimedMetadataKind. Registre manipuladores para os eventos CueEntered e CueExited. Esses eventos são gerados quando a hora de início de uma sugestão foi atingida e quando a duração de uma sinalização expirou, respectivamente.

Crie um novo objeto de sugestão apropriado para o tipo de faixa de metadados que você criou e defina ID, hora de início e duração da faixa. Este exemplo cria uma faixa de dados, portanto, um conjunto de objetos DataCue é gerado e um buffer contendo dados específicos do aplicativo é fornecido para cada sinalizaçã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 sugestão é atingida, desde que a faixa associada tenha um modo de apresentação de ApplicationPresented, Hidden ou PlatformPresented. Os eventos de sugestão não são gerados para faixas de metadados enquanto o modo de apresentação da faixa estiver Desabilitado. Este exemplo simplesmente gera a saída dos dados personalizados associados à sugestã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 sugestõ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

O MediaPlaybackList permite que você crie uma lista de reprodução de itens de mídia, que são representados por objetos MediaPlaybackItem.

Observação: os itens em uma MediaPlaybackList são renderizados usando reprodução sem intervalos. O sistema usará metadados fornecidos em arquivos codificados em MP3 ou AAC para determinar o atraso ou a compensação de preenchimento necessários para a reprodução sem lacunas. Se os arquivos codificados em MP3 ou AAC não fornecerem esses metadados, o sistema determinará o atraso ou o preenchimento de forma heurística. Para formatos sem perdas, como PCM, FLAC ou ALAC, o sistema não executa nenhuma ação porque esses codificadores não introduzem atraso ou preenchimento.

Para começar, declare uma variável para armazenar sua 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 o objeto MediaPlaybackList e adicione os itens de reprodução de mídia a ele. Registre um manipulador para o evento CurrentItemChanged. Esse evento permite que você atualize sua interface do usuário para refletir o item de mídia em reprodução no momento. Você também pode se registrar para o evento ItemOpen, que é gerado quando um item na lista é aberto com êxito, e o evento ItemFailed, que é gerado 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 de reproduzidos definindo a propriedade MaxPlayedItemsToKeepOpen. Quando um MediaPlaybackItem é mantido aberto, a reprodução do item pode ser iniciada instantaneamente quando o usuário alterna para esse item porque o item não precisa ser recarregado. Mas manter os itens abertos também aumenta o consumo de memória do seu aplicativo, portanto, você deve considerar o equilíbrio entre capacidade de resposta e uso de memória ao definir esse valor.

Para ativar a reprodução da sua lista, defina a origem de reprodução do MediaPlayer para a 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 sua interface do usuário para refletir o item em execução no momento, que pode ser recuperado usando a propriedade NewItem do objeto CurrentMediaPlaybackItemChangedEventArgs passado para o evento. Lembre-se de que, se você atualizar a interface do usuário a partir desse evento, deverá fazê-lo em uma chamada para 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 verificar a propriedade CurrentMediaPlaybackItemChangedEventArgs.Reason para obter um valor que indique o motivo pelo qual o item foi alterado, como o aplicativo alternando itens programaticamente, o item reproduzido anteriormente chegando ao fim ou a ocorrência de um erro.

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

Chame MovePrevious ou MoveNext para fazer com que o player de mídia reproduza o item anterior ou seguinte na sua 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 player de mídia 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 player de mídia deve reproduzir em loop sua 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;
    });
}

Manipular a falha de itens de mídia em uma lista de reprodução

O evento ItemFailed é gerado quando um item na lista não abrir. A propriedade ErrorCode do objeto MediaPlaybackItemError passado ao manipulador enumera a causa específica da falha quando possível, incluindo 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);
}

Desativar a reprodução de itens em uma lista de reprodução

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

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

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

No manipulador NetworkStatusChanged, verifique se GetInternetConnectionProfile retorna null, o que indica que a rede não está conectada. Se esse for o caso, faça um loop por todos os itens na lista de reprodução e, se o TotalDownloadProgress do item for menor que 1, o que significa que o item não foi totalmente baixado, desabilite o item. Se a conexão de rede estiver habilitada, faça um loop por todos os itens na lista de reprodução 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;
        }
    }
}

Adiar a vinculação de conteúdo de mídia para itens em uma lista de reprodução usando o MediaBinder

Nos exemplos anteriores, um MediaSource é criado a partir de um arquivo, URL ou fluxo, depois do qual um MediaPlaybackItem é criado e adicionado a um MediaPlaybackList. Para alguns cenários, como se o usuário estiver sendo cobrado pela exibição de conteúdo, convém adiar a recuperação do conteúdo de um MediaSource até que o item na lista de reprodução esteja pronto para ser realmente 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. Crie um MediaSource a partir do Binder chamando MediaSource.CreateFromMediaBinder. Em seguida, crie um MediaPlaybackItem a partir do MediaSource e adicione à lista de reprodução como usual.

_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 ao MediaBinder precisa ser recuperado, ele gerará o evento Binding. No manipulador desse evento, você pode recuperar a instância MediaBinder do MediaBindingEventArgs passado para o evento. Recupere a cadeia de caracteres especificada para a propriedade Token e use-a para determinar qual conteúdo deve ser recuperado. O MediaBindingEventArgs fornece métodos para definir o conteúdo vinculado em várias 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, chame o método MediaBindingEventArgs.GetDeferral para instruir o sistema a aguardar a conclusão da operação antes de continuar. Chame Deferral.Complete após a conclusão da operação para instruir o sistema a continuar.

A partir do Windows 10, versão 1703, você pode fornecer um AdaptiveMediaSource como conteúdo vinculado chamando SetAdaptiveMediaSource. Para obter mais informações sobre como usar streaming adaptável no seu aplicativo, consulte Streaming adaptável.