Compartilhar via


Reproduzir áudio e vídeo com o MediaPlayer

Este artigo mostra como reproduzir mídia em seu aplicativo Universal do Windows usando a classe MediaPlayer . Com Windows 10, versão 1607, melhorias significativas foram feitas nas APIs de reprodução de mídia, incluindo um design simplificado de processo único para áudio em segundo plano, integração automática com os SMTC (Controles de Transporte de Mídia do Sistema), a capacidade de sincronizar vários players de mídia, a capacidade de renderizar quadros de vídeo em uma superfície Windows.UI.Composition e uma interface fácil para criar e agendar quebras de mídia em seu conteúdo. Para aproveitar essas melhorias, a prática recomendada para reproduzir mídia é usar a classe MediaPlayer em vez de MediaElement para reprodução de mídia. O controle XAML leve, MediaPlayerElement, foi introduzido para permitir que você renderize conteúdo de mídia em uma página XAML. Muitas das APIs de status e controle de reprodução fornecidas pelo MediaElement agora estão disponíveis por meio do novo objeto MediaPlaybackSession . MediaElement continua a funcionar para dar suporte à compatibilidade com versões anteriores, mas nenhum recurso adicional será adicionado a essa classe.

Este artigo orientará você pelos recursos do MediaPlayer que um aplicativo de reprodução de mídia típico usará. Observe que o MediaPlayer usa a classe MediaSource como um contêiner para todos os itens de mídia. Essa classe permite que você carregue e reproduza mídia de várias fontes diferentes, incluindo arquivos locais, fluxos de memória e fontes de rede, todos usando a mesma interface. Também há classes de nível superior que funcionam com MediaSource, como MediaPlaybackItem e MediaPlaybackList, que fornecem recursos mais avançados, como listas de reprodução e a capacidade de gerenciar fontes de mídia com várias faixas de áudio, vídeo e metadados. Para obter mais informações sobre o MediaSource e APIs relacionadas, consulte Itens de mídia, listas de reprodução e faixas.

Observação

As edições Windows 10 N e Windows 10 KN não incluem os recursos de mídia necessários para usar o MediaPlayer para reprodução. Esses recursos podem ser instalados manualmente. Para obter mais informações, consulte Pacote de recursos de mídia para edições Windows 10 N e Windows 10 KN.

Reproduzir um arquivo de mídia com o MediaPlayer

A reprodução básica de mídia com o MediaPlayer é muito simples de implementar. Primeiro, crie uma nova instância da classe MediaPlayer . Seu aplicativo pode ter várias instâncias do MediaPlayer ativas ao mesmo tempo. Em seguida, defina a propriedade Source do player como um objeto que implemente o IMediaPlaybackSource, como um MediaSource, um MediaPlaybackItem ou um MediaPlaybackList. Neste exemplo, um MediaSource é criado a partir de um arquivo no armazenamento local do aplicativo e, em seguida, um MediaPlaybackItem é criado a partir da origem e, em seguida, atribuído à propriedade Source do player.

Ao contrário do MediaElement, o MediaPlayer não inicia automaticamente a reprodução por padrão. Você pode iniciar a reprodução chamando Play, definindo a propriedade AutoPlay como true ou aguardando que o usuário inicie a reprodução com os controles de mídia internos.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.Play();

Quando seu aplicativo terminar de usar um MediaPlayer, você deverá chamar o método Close (projetado para Dispose em C#) para limpar os recursos usados pelo player.

mediaPlayer.Dispose();

Usar MediaPlayerElement para renderizar vídeo em XAML

Você pode reproduzir mídia em um MediaPlayer sem exibi-la em XAML, mas muitos aplicativos de reprodução de mídia desejarão renderizar a mídia em uma página XAML. Para fazer isso, use o controle MediaPlayerElement leve. Assim como MediaElement, MediaPlayerElement permite que você especifique se os controles de transporte internos devem ser mostrados.

<MediaPlayerElement x:Name="_mediaPlayerElement" AreTransportControlsEnabled="False" HorizontalAlignment="Stretch"  Grid.Row="0"/>

Você pode definir a instância MediaPlayer à qual o elemento está associado chamando SetMediaPlayer.

_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

Você também pode definir a fonte de reprodução no MediaPlayerElement e o elemento criará automaticamente uma nova instância do MediaPlayer que você pode acessar usando a propriedade MediaPlayer.

Observação

A configuração das propriedades MediaPlayerElement definirá as propriedades correspondentes em seu MediaPlayer subjacente. Você tem a opção de usar o MediaPlayer subjacente diretamente em vez de usar as propriedades MediaPlayerElement. Lembre-se de que usar o MediaPlayer diretamente onde uma propriedade MediaPlayerElement equivalente poderia ser usada pode causar um comportamento inesperado. Isso ocorre porque o MediaPlayerElement não está ciente de tudo o que está acontecendo com seu MediaPlayer subjacente. Por exemplo, se você definir a origem diretamente no MediaPlayer, a propriedade MediaPlayerElement Source não refletirá a alteração. Por esse motivo, você deve ser consistente ao usar as propriedades MediaPlayerElement ou usar diretamente o MediaPlayer subjacente.

_mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = _mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();

Observação

Se você desabilitar o MediaPlaybackCommandManager do MediaPlayer definindo IsEnabled como false, ele interromperá o vínculo entre o MediaPlayer e os TransportControls fornecidos pelo MediaPlayerElement, de modo que os controles de transporte internos não controlarão mais automaticamente a reprodução do player. Em vez disso, você deve implementar seus próprios controles para controlar o MediaPlayer.

O MediaPlayer é desanexado de MediaPlayerElement quando o MediaPlayerElement é destruído ou quando um novo MediaPlayer é definido usando SetMediaPlayer. Quando desanexado, MediaPlayerElement trata o MediaPlayer subjacente de forma diferente, dependendo se ele foi criado por MediaPlayerElement ou definido usando SetMediaPlayer.

Se o MediaPlayer foi criado por MediaPlayerElement, ele fechará corretamente o MediaPlayer para você. Se o MediaPlayer foi definido em MediaPlayerElement usando SetMediaPlayer, você é responsável por garantir que o MediaPlayer esteja fechado corretamente. Não fazer isso pode resultar em erros fatais de reprodução no MediaPlayer. O snippet de código a seguir mostra como desanexar e fechar corretamente no código.

// Get a reference to the current media source.
IMediaPlaybackSource _mediaPlayerElement = _mediaPlayerElement.Source;

// Pause playback if able.
if (mediaPlayer.PlaybackSession.CanPause)
{
    mediaPlayer.Pause();
}

// Disconnect the MediaPlayer from its source. This can be done by setting 
// the MediaPlayerElement Source property to null or by directly setting the
// source to null on the underlying MediaPlayer.
_mediaPlayerElement.Source = null;

// Disconnect the MediaPlayer from MediaPlayerElement.
_mediaPlayerElement.SetMediaPlayer(null);

// Dispose of the MediaPlayer or Source if they're no longer needed.
if (source is MediaSource mediaSource)
{
    mediaSource.Dispose();
}

mediaPlayer.Dispose();

Tarefas comuns do MediaPlayer

Esta seção mostra como usar alguns dos recursos do MediaPlayer.

Definir a categoria de áudio

Defina a propriedade AudioCategory de um MediaPlayer como um dos valores da enumeração MediaPlayerAudioCategory para permitir que o sistema saiba que tipo de mídia você está reproduzindo. Os jogos devem categorizar seus fluxos de música como GameMedia para que a música do jogo seja silenciada automaticamente se outro aplicativo reproduzir música em segundo plano. Os aplicativos de música ou vídeo devem categorizar seus streams como Mídia ou Filme para que tenham prioridade sobre os streams do GameMedia .

mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;

Saída para um ponto de extremidade de áudio específico

Por padrão, a saída de áudio de um MediaPlayer é roteada para o ponto de extremidade de áudio padrão do sistema, mas você pode especificar um ponto de extremidade de áudio específico que o MediaPlayer deve usar para saída. No exemplo abaixo, MediaDevice.GetAudioRenderSelector retorna uma cadeia de caracteres que identifica exclusivamente a categoria de renderização de áudio dos dispositivos. Em seguida, o método DeviceInformation FindAllAsync é chamado para obter uma lista de todos os dispositivos disponíveis do tipo selecionado. Você pode determinar programaticamente qual dispositivo deseja usar ou adicionar os dispositivos retornados a um ComboBox para permitir que o usuário selecione um dispositivo.

string audioSelector = MediaDevice.GetAudioRenderSelector();
var outputDevices = await DeviceInformation.FindAllAsync(audioSelector);
foreach (var device in outputDevices)
{
    var deviceItem = new ComboBoxItem();
    deviceItem.Content = device.Name;
    deviceItem.Tag = device;
    _audioDeviceComboBox.Items.Add(deviceItem);
}

No evento SelectionChanged para a caixa de combinação de dispositivos, a propriedade AudioDevice do MediaPlayer é definida como o dispositivo selecionado, que foi armazenado na propriedade Tag do ComboBoxItem.

private void _audioDeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    DeviceInformation selectedDevice = (DeviceInformation)((ComboBoxItem)_audioDeviceComboBox.SelectedItem).Tag;
    if (selectedDevice != null)
    {
        mediaPlayer.AudioDevice = selectedDevice;
    }
}

Sessão de reprodução

Conforme descrito anteriormente neste artigo, muitas das funções expostas pela classe MediaElement foram movidas para a classe MediaPlaybackSession. Isso inclui informações sobre o estado de reprodução do player, como a posição de reprodução atual, se o player está pausado ou em reprodução e a velocidade de reprodução atual. MediaPlaybackSession também fornece vários eventos para notificá-lo quando o estado é alterado, incluindo o buffer atual e o status de download do conteúdo que está sendo reproduzido e o tamanho natural e a taxa de proporção do conteúdo de vídeo em reprodução no momento.

O exemplo a seguir mostra como implementar um manipulador de clique de botão que pula 10 segundos para frente no conteúdo. Primeiro, o objeto MediaPlaybackSession do player é recuperado com a propriedade PlaybackSession . Em seguida, a propriedade Position é definida como a posição de reprodução atual mais 10 segundos.

private void _skipForwardButton_Click(object sender, RoutedEventArgs e)
{
    var session = mediaPlayer.PlaybackSession;
    session.Position = session.Position + TimeSpan.FromSeconds(10);
}

O exemplo a seguir ilustra o uso de um botão de alternância para alternar entre a velocidade de reprodução normal e a velocidade 2X definindo a propriedade PlaybackRate da sessão.

private void _speedToggleButton_Checked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 2.0;
}
private void _speedToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 1.0;
}

A partir do Windows 10, versão 1803, você pode definir a rotação com a qual o vídeo é apresentado no MediaPlayer em incrementos de 90 graus.

mediaPlayer.PlaybackSession.PlaybackRotation = MediaRotation.Clockwise90Degrees;

Detectar buffer esperado e inesperado

O objeto MediaPlaybackSession descrito na seção anterior fornece dois eventos para detectar quando o arquivo de mídia em execução no momento começa e termina o buffer, BufferingStarted e BufferingEnded. Isso permite que você atualize sua interface do usuário para mostrar ao usuário que o buffer está ocorrendo. O buffer inicial é esperado quando um arquivo de mídia é aberto pela primeira vez ou quando o usuário alterna para um novo item em uma lista de reprodução. O buffer inesperado pode ocorrer quando a velocidade da rede diminui ou se o sistema de gerenciamento de conteúdo que fornece o conteúdo apresenta problemas técnicos. A partir do RS3, você pode usar o evento BufferingStarted para determinar se o evento de buffer é esperado ou se é inesperado e interrompe a reprodução. Você pode usar essas informações como dados de telemetria para seu aplicativo ou serviço de entrega de mídia.

Registre manipuladores para os eventos BufferingStarted e BufferingEnded para receber notificações de estado de buffer.

mediaPlayer.PlaybackSession.BufferingStarted += MediaPlaybackSession_BufferingStarted;
mediaPlayer.PlaybackSession.BufferingEnded += MediaPlaybackSession_BufferingEnded;

No manipulador de eventos BufferingStarted, converta os argumentos de evento passados para o evento em um objeto MediaPlaybackSessionBufferingStartedEventArgs e verifique a propriedade IsPlaybackInterruption. Se esse valor for true, o buffer que acionou o evento será inesperado e interromperá a reprodução. Caso contrário, espera-se o buffer inicial.

private void MediaPlaybackSession_BufferingStarted(MediaPlaybackSession sender, object args)
{
    MediaPlaybackSessionBufferingStartedEventArgs bufferingStartedEventArgs = args as MediaPlaybackSessionBufferingStartedEventArgs;
    if (bufferingStartedEventArgs != null && bufferingStartedEventArgs.IsPlaybackInterruption)
    {
        // update the playback quality telemetry report to indicate that
        // playback was interrupted
    }

    // update the UI to indicate that playback is buffering
}
private void MediaPlaybackSession_BufferingEnded(MediaPlaybackSession sender, object args)
{
    // update the UI to indicate that playback is no longer buffering
}

Aperte e amplie o vídeo

O MediaPlayer permite que você especifique o retângulo de origem no conteúdo de vídeo que deve ser renderizado, permitindo que você amplie efetivamente o vídeo. O retângulo especificado é relativo a um retângulo normalizado (0,0,1,1) em que 0,0 é a parte superior esquerda do quadro e 1,1 especifica a largura e a altura totais do quadro. Assim, por exemplo, para definir o retângulo de zoom para que o quadrante superior direito do vídeo seja renderizado, você especificaria o retângulo (.5,0,.5,.5). É importante que você verifique seus valores para garantir que o retângulo de origem esteja dentro do retângulo normalizado (0,0,1,1). A tentativa de definir um valor fora desse intervalo fará com que uma exceção seja lançada.

Para implementar o pinçamento e o zoom usando gestos multitoque, você deve primeiro especificar quais gestos deseja suportar. Neste exemplo, os gestos de escala e tradução são solicitados. O evento ManipulationDelta é gerado quando ocorre um dos gestos inscritos. O evento DoubleTapped será usado para redefinir o zoom para o quadro completo.

_mediaPlayerElement.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
_mediaPlayerElement.ManipulationDelta += _mediaPlayerElement_ManipulationDelta;
_mediaPlayerElement.DoubleTapped += _mediaPlayerElement_DoubleTapped;

Em seguida, declare um objeto Rect que armazenará o retângulo de origem do zoom atual.

Rect _sourceRect = new Rect(0, 0, 1, 1);

O manipulador ManipulationDelta ajusta a escala ou a tradução do retângulo de zoom. Se o valor da escala delta não for 1, significa que o usuário executou um gesto de pinçar. Se o valor for maior que 1, o retângulo de origem deverá ser menor para ampliar o conteúdo. Se o valor for menor que 1, o retângulo de origem deverá ser aumentado para diminuir o zoom. Antes de definir os novos valores de escala, o retângulo resultante é verificado para garantir que esteja totalmente dentro dos limites (0,0,1,1).

Se o valor da escala for 1, o gesto de tradução será manipulado. O retângulo é simplesmente traduzido pelo número de pixels no gesto dividido pela largura e altura do controle. Novamente, o retângulo resultante é verificado para garantir que ele esteja dentro dos limites (0,0,1,1).

Por fim, o NormalizedSourceRect do MediaPlaybackSession é definido como o retângulo recém-ajustado, especificando a área dentro do quadro de vídeo que deve ser renderizada.

private void _mediaPlayerElement_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{

    if (e.Delta.Scale != 1)
    {
        var halfWidth = _sourceRect.Width / 2;
        var halfHeight = _sourceRect.Height / 2;

        var centerX = _sourceRect.X + halfWidth;
        var centerY = _sourceRect.Y + halfHeight;

        var scale = e.Delta.Scale;
        var newHalfWidth = (_sourceRect.Width * e.Delta.Scale) / 2;
        var newHalfHeight = (_sourceRect.Height * e.Delta.Scale) / 2;

        if (centerX - newHalfWidth > 0 && centerX + newHalfWidth <= 1.0 &&
            centerY - newHalfHeight > 0 && centerY + newHalfHeight <= 1.0)
        {
            _sourceRect.X = centerX - newHalfWidth;
            _sourceRect.Y = centerY - newHalfHeight;
            _sourceRect.Width *= e.Delta.Scale;
            _sourceRect.Height *= e.Delta.Scale;
        }
    }
    else
    {
        var translateX = -1 * e.Delta.Translation.X / _mediaPlayerElement.ActualWidth;
        var translateY = -1 * e.Delta.Translation.Y / _mediaPlayerElement.ActualHeight;

        if (_sourceRect.X + translateX >= 0 && _sourceRect.X + _sourceRect.Width + translateX <= 1.0 &&
            _sourceRect.Y + translateY >= 0 && _sourceRect.Y + _sourceRect.Height + translateY <= 1.0)
        {
            _sourceRect.X += translateX;
            _sourceRect.Y += translateY;
        }
    }

    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

No manipulador de eventos DoubleTapped , o retângulo de origem é definido de volta como (0,0,1,1) para fazer com que todo o quadro de vídeo seja renderizado.

private void _mediaPlayerElement_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
    _sourceRect = new Rect(0, 0, 1, 1);
    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

NOTA: Esta seção descreve a entrada por toque. O touchpad envia eventos de ponteiro e não envia eventos de manipulação.

Manipulando a degradação da reprodução baseada em políticas

Em algumas circunstâncias, o sistema pode degradar a reprodução de um item de mídia, como reduzir a resolução (constrição), com base em uma política em vez de um problema de desempenho. Por exemplo, o vídeo pode ser degradado pelo sistema se estiver sendo reproduzido usando um driver de vídeo não assinado. Você pode chamar MediaPlaybackSession.GetOutputDegradationPolicyState para determinar se e por que essa degradação baseada em política está ocorrendo e alertar o usuário ou registrar o motivo para fins de telemetria.

O exemplo a seguir mostra uma implementação de um manipulador para o evento MediaPlayer.MediaOpened que é gerado quando o player abre um novo item de mídia. GetOutputDegradationPolicyState é chamado no MediaPlayer passado para o manipulador. O valor de VideoConstrictionReason indica o motivo da política para que o vídeo seja restrito. Se o valor não for Nenhum, este exemplo registrará o motivo da degradação para fins de telemetria. Este exemplo também mostra a configuração da taxa de bits do AdaptiveMediaSource que está sendo reproduzido no momento para a largura de banda mais baixa para economizar o uso de dados, já que o vídeo é restrito e não será exibido em alta resolução de qualquer maneira. Para obter mais informações sobre como usar AdaptiveMediaSource, consulte Streaming adaptável.

private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    MediaPlaybackSessionOutputDegradationPolicyState info = sender.PlaybackSession.GetOutputDegradationPolicyState();

    if (info.VideoConstrictionReason != MediaPlaybackSessionVideoConstrictionReason.None)
    {
        // Switch to lowest bitrate to save bandwidth
        adaptiveMediaSource.DesiredMaxBitrate = adaptiveMediaSource.AvailableBitrates[0];

        // Log the degradation reason or show a message to the user
        System.Diagnostics.Debug.WriteLine("Logging constriction reason: " + info.VideoConstrictionReason);
    }
}

Usar MediaPlayerSurface para renderizar vídeo em uma superfície Windows.UI.Composition

A partir do Windows 10, versão 1607, você pode usar o MediaPlayer para renderizar vídeo em um ICompositionSurface, o que permite que o player interopere com as APIs no namespace Windows.UI.Composition. A estrutura de composição permite que você trabalhe com gráficos na camada visual entre XAML e as APIs de gráficos DirectX de baixo nível. Isso permite cenários como renderizar vídeo em qualquer controle XAML. Para obter mais informações sobre como usar as APIs de composição, consulte Camada Visual.

O exemplo a seguir ilustra como renderizar o conteúdo do player de vídeo em um controle Canvas. As chamadas específicas do reprodutor de mídia neste exemplo são SetSurfaceSize e GetSurface. SetSurfaceSize informa ao sistema o tamanho do buffer que deve ser alocado para renderizar conteúdo. GetSurface usa um Compositor como argumento e recupera uma instância da classe MediaPlayerSurface. Essa classe fornece acesso ao MediaPlayer e ao Compositor usados para criar a superfície e expõe a própria superfície por meio da propriedade CompositionSurface.

O restante do código neste exemplo cria um SpriteVisual para o qual o vídeo é renderizado e define o tamanho como o tamanho do elemento canvas que exibirá o visual. Em seguida, um CompositionBrush é criado a partir do MediaPlayerSurface e atribuído à propriedade Brush do visual. Em seguida, um ContainerVisual é criado e o SpriteVisual é inserido na parte superior de sua árvore visual. Por fim, SetElementChildVisual é chamado para atribuir o visual de contêiner ao Canvas.

mediaPlayer.SetSurfaceSize(new Size(_compositionCanvas.ActualWidth, _compositionCanvas.ActualHeight));

var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
MediaPlayerSurface surface = mediaPlayer.GetSurface(compositor);

SpriteVisual spriteVisual = compositor.CreateSpriteVisual();
spriteVisual.Size =
    new System.Numerics.Vector2((float)_compositionCanvas.ActualWidth, (float)_compositionCanvas.ActualHeight);

CompositionBrush brush = compositor.CreateSurfaceBrush(surface.CompositionSurface);
spriteVisual.Brush = brush;

ContainerVisual container = compositor.CreateContainerVisual();
container.Children.InsertAtTop(spriteVisual);

ElementCompositionPreview.SetElementChildVisual(_compositionCanvas, container);

Use MediaTimelineController para sincronizar conteúdo em vários players.

Conforme discutido anteriormente neste artigo, seu aplicativo pode ter vários objetos MediaPlayer ativos ao mesmo tempo. Por padrão, cada MediaPlayer criado opera de forma independente. Para alguns cenários, como sincronizar uma faixa de comentários com um vídeo, talvez você queira sincronizar o estado do player, a posição de reprodução e a velocidade de reprodução de vários players. A partir do Windows 10, versão 1607, você pode implementar esse comportamento usando a classe MediaTimelineController.

Implementar controles de reprodução

O exemplo a seguir mostra como usar um MediaTimelineController para controlar duas instâncias do MediaPlayer. Primeiro, cada instância do MediaPlayer é instanciada e a Origem é definida como um arquivo de mídia. Em seguida, um novo MediaTimelineController é criado. Para cada MediaPlayer, o MediaPlaybackCommandManager associado a cada player é desabilitado definindo a propriedade IsEnabled como false. Em seguida, a propriedade TimelineController é definida como o objeto do controlador de linha do tempo.

MediaTimelineController _mediaTimelineController;
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);


_mediaPlayer2 = new MediaPlayer();
_mediaPlayer2.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_2.mkv"));
_mediaPlayerElement2.SetMediaPlayer(_mediaPlayer2);

_mediaTimelineController = new MediaTimelineController();

mediaPlayer.CommandManager.IsEnabled = false;
mediaPlayer.TimelineController = _mediaTimelineController;

_mediaPlayer2.CommandManager.IsEnabled = false;
_mediaPlayer2.TimelineController = _mediaTimelineController;

Cuidado O MediaPlaybackCommandManager fornece integração automática entre o MediaPlayer e o SMTC (Controles de Transporte de Mídia do Sistema), mas essa integração automática não pode ser usada com players de mídia controlados com um MediaTimelineController. Portanto, você deve desabilitar o gerenciador de comandos do reprodutor de mídia antes de definir o controlador de linha do tempo do reprodutor. Se isso não for feito, uma exceção será lançada com a seguinte mensagem: "A anexação do Controlador de Linha do Tempo de Mídia está bloqueada devido ao estado atual do objeto". Para obter mais informações sobre a integração do media player com o SMTC, consulte Integrar com os controles de transporte de mídia do sistema. Se você estiver usando um MediaTimelineController , ainda poderá controlar o SMTC manualmente. Para obter mais informações, consulte Controle manual dos controles de transporte de mídia do sistema.

Depois de anexar um MediaTimelineController a um ou mais players de mídia, você pode controlar o estado de reprodução usando os métodos expostos pelo controlador. O exemplo a seguir chama Start para iniciar a reprodução de todos os players de mídia associados no início da mídia.

private void PlayButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.Start();
}

Este exemplo ilustra a pausa e a retomada de todos os players de mídia conectados.

private void PauseButton_Click(object sender, RoutedEventArgs e)
{
    if(_mediaTimelineController.State == MediaTimelineControllerState.Running)
    {
        _mediaTimelineController.Pause();
        _pauseButton.Content = "Resume";
    }
    else
    {
        _mediaTimelineController.Resume();
        _pauseButton.Content = "Pause";
    }
}

Para avançar todos os reprodutores de mídia conectados, defina a velocidade de reprodução para um valor maior que 1.

private void FastForwardButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.ClockRate = 2.0;
}

O exemplo a seguir mostra como usar um controle Slider para mostrar a posição de reprodução atual do controlador de linha do tempo em relação à duração do conteúdo de um dos players de mídia conectados. Primeiro, um novo MediaSource é criado e um manipulador para o OpenOperationCompleted da fonte de mídia é registrado.

var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
mediaPlayer.Source = mediaSource;
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

O manipulador OpenOperationCompleted é usado como uma oportunidade para descobrir a duração do conteúdo da fonte de mídia. Depois que a duração é determinada, o valor máximo do controle Slider é definido como o número total de segundos do item de mídia. O valor é definido dentro de uma chamada para RunAsync para garantir que ele seja executado no thread da interface do usuário.

TimeSpan _duration;
private async void MediaSource_OpenOperationCompleted(MediaSource sender, MediaSourceOpenOperationCompletedEventArgs args)
{
    _duration = sender.Duration.GetValueOrDefault();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        _positionSlider.Minimum = 0;
        _positionSlider.Maximum = _duration.TotalSeconds;
        _positionSlider.StepFrequency = 1;
    }); 
}

Em seguida, um manipulador para o evento PositionChanged do controlador de linha do tempo é registrado. Isso é chamado periodicamente pelo sistema, aproximadamente 4 vezes por segundo.

_mediaTimelineController.PositionChanged += _mediaTimelineController_PositionChanged;

No manipulador de PositionChanged, o valor do controle deslizante é atualizado para refletir a posição atual do controlador de linha do tempo.

private async void _mediaTimelineController_PositionChanged(MediaTimelineController sender, object args)
{
    if (_duration != TimeSpan.Zero)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            _positionSlider.Value = sender.Position.TotalSeconds / (float)_duration.TotalSeconds;
        });
    }
}

Deslocar a posição de reprodução da posição da linha do tempo

Em alguns casos, você pode querer que a posição de reprodução de um ou mais players de mídia associados a um controlador de linha do tempo seja deslocada dos outros players. Você pode fazer isso definindo a propriedade TimelineControllerPositionOffset do objeto MediaPlayer que você deseja deslocar. O exemplo a seguir usa as durações do conteúdo de dois players de mídia para definir os valores mínimo e máximo de dois controles deslizantes para mais e para menos o comprimento do item.

_timelineOffsetSlider1.Minimum = -1 * _duration.TotalSeconds;
_timelineOffsetSlider1.Maximum = _duration.TotalSeconds;
_timelineOffsetSlider1.StepFrequency = 1;

_timelineOffsetSlider2.Minimum = -1 * _duration2.TotalSeconds;
_timelineOffsetSlider2.Maximum = _duration2.TotalSeconds;
_timelineOffsetSlider2.StepFrequency = 1;

No evento ValueChanged para cada controle deslizante, o TimelineControllerPositionOffset para cada jogador é definido como o valor correspondente.

private void _timelineOffsetSlider1_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    mediaPlayer.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider1.Value);
}

private void _timelineOffsetSlider2_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    _mediaPlayer2.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider2.Value);
}

Observe que, se o valor de deslocamento de um player for mapeado para uma posição de reprodução negativa, o clipe permanecerá pausado até que o deslocamento chegue a zero e, em seguida, a reprodução começará. Da mesma forma, se o valor de deslocamento for mapeado para uma posição de reprodução maior que a duração do item de mídia, o quadro final será mostrado, assim como acontece quando um único reprodutor de mídia atinge o final de seu conteúdo.

Reproduzir vídeo esférico com o MediaPlayer

A partir do Windows 10, versão 1703, o MediaPlayer dá suporte à projeção equirretangular para reprodução de vídeo esférico. O conteúdo de vídeo esférico não é diferente do vídeo simples e normal, pois o MediaPlayer renderizará o vídeo, desde que a codificação de vídeo seja suportada. Para vídeo esférico que contém uma marca de metadados que especifica que o vídeo usa projeção equirretangular, o MediaPlayer pode renderizar o vídeo usando um campo de visão e uma orientação de exibição especificados. Isso permite cenários como reprodução de vídeo de realidade virtual com um head-mounted display ou simplesmente permitindo que o usuário se mova dentro do conteúdo de vídeo esférico usando a entrada do mouse ou do teclado.

Para reproduzir vídeo esférico, use as etapas para reproduzir conteúdo de vídeo descritas anteriormente neste artigo. A única etapa adicional é registrar um manipulador para o evento MediaPlayer.MediaOpened. Esse evento oferece a oportunidade de habilitar e controlar os parâmetros de reprodução de vídeo esféricos.

mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += _mediaPlayer_MediaOpened;
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_spherical.mp4"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);
mediaPlayer.Play();

No manipulador MediaOpened, primeiro verifique o formato do quadro do item de mídia recém-aberto verificando a propriedade PlaybackSession.SphericalVideoProjection.FrameFormat. Se esse valor for SphericaVideoFrameFormat.Equirectangular, o sistema poderá projetar automaticamente o conteúdo do vídeo. Primeiro, defina a propriedade PlaybackSession.SphericalVideoProjection.IsEnabled como true. Você também pode ajustar propriedades como a orientação da exibição e o campo de visão que o reprodutor de mídia usará para projetar o conteúdo do vídeo. Neste exemplo, o campo de exibição é definido como um valor amplo de 120 graus definindo a propriedade HorizontalFieldOfViewInDegrees.

Se o conteúdo de vídeo for esférico, mas estiver em um formato diferente de equiretangular, você poderá implementar seu próprio algoritmo de projeção usando o modo de servidor de quadros do reprodutor de mídia para receber e processar quadros individuais.

private void _mediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Equirectangular)
    {
        sender.PlaybackSession.SphericalVideoProjection.IsEnabled = true;
        sender.PlaybackSession.SphericalVideoProjection.HorizontalFieldOfViewInDegrees = 120;

    }
    else if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Unsupported)
    {
        // If the spherical format is unsupported, you can use frame server mode to implement a custom projection
    }
}

O código de exemplo a seguir ilustra como ajustar a orientação de exibição de vídeo esférica usando as teclas de seta para a esquerda e para a direita.

protected override void OnKeyDown(KeyRoutedEventArgs e)
{
    if (mediaPlayer.PlaybackSession.SphericalVideoProjection.FrameFormat != SphericalVideoFrameFormat.Equirectangular)
    {
        return;
    }

    switch (e.Key)
    {
        case Windows.System.VirtualKey.Right:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(.1f, 0, 0);
            break;
        case Windows.System.VirtualKey.Left:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(-.1f, 0, 0);
            break;
    }
}

Se o seu aplicativo der suporte a listas de reprodução de vídeo, talvez você queira identificar itens de reprodução que contenham vídeo esférico em sua interface do usuário. As listas de reprodução de mídia são discutidas em detalhes no artigo, Itens de mídia, listas de reprodução e faixas. O exemplo a seguir mostra a criação de uma nova lista de reprodução, a adição de um item e o registro de um manipulador para o evento MediaPlaybackItem.VideoTracksChanged , que ocorre quando as faixas de vídeo de um item de mídia são resolvidas.

var playbackList = new MediaPlaybackList();
var item = new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/RIFTCOASTER HD_injected.mp4")));
item.VideoTracksChanged += Item_VideoTracksChanged;
playbackList.Items.Add(item);
mediaPlayer.Source = playbackList;

No manipulador de eventos VideoTracksChanged, obtenha as propriedades de codificação para todas as faixas de vídeo adicionadas chamando VideoTrack.GetEncodingProperties. Se a propriedade SphericalVideoFrameFormat das propriedades de codificação for um valor diferente de SphericaVideoFrameFormat.None, a faixa de vídeo conterá vídeo esférico e você poderá atualizar sua interface do usuário adequadamente, se desejar.

private void Item_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if (args.CollectionChange != CollectionChange.ItemInserted)
    {
        return;
    }
    foreach (var videoTrack in sender.VideoTracks)
    {
        if (videoTrack.GetEncodingProperties().SphericalVideoFrameFormat != SphericalVideoFrameFormat.None)
        {
            // Optionally indicate in the UI that this item contains spherical video
        }
    }
}

Usar o MediaPlayer no modo de servidor de quadros

A partir do Windows 10, versão 1703, você pode usar o MediaPlayer no modo de servidor de quadros. Nesse modo, o MediaPlayer não renderiza quadros automaticamente para um MediaPlayerElement associado. Em vez disso, seu aplicativo copia o quadro atual do MediaPlayer para um objeto que implementa IDirect3DSurface. O cenário principal que esse recurso permite é o uso de sombreadores de pixel para processar quadros de vídeo fornecidos pelo MediaPlayer. Seu aplicativo é responsável por exibir cada quadro após o processamento, por exemplo, mostrando o quadro em um controle de imagem XAML.

No exemplo a seguir, um novo MediaPlayer é inicializado e o conteúdo de vídeo é carregado. Em seguida, um manipulador para VideoFrameAvailable é registrado. O modo de servidor de quadros é habilitado definindo a propriedade IsVideoFrameServerEnabled do objeto MediaPlayer como true. Por fim, a reprodução de mídia é iniciada com uma chamada para Reproduzir.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable;
mediaPlayer.IsVideoFrameServerEnabled = true;
mediaPlayer.Play();

O exemplo a seguir mostra um manipulador para VideoFrameAvailable que usa Win2D para adicionar um efeito de desfoque simples a cada quadro de um vídeo e, em seguida, exibe os quadros processados em um controle de imagem XAML.

Sempre que o manipulador VideoFrameAvailable é chamado, o método CopyFrameToVideoSurface é usado para copiar o conteúdo do quadro para um IDirect3DSurface. Você também pode usar CopyFrameToStereoscopicVideoSurfaces para copiar conteúdo 3D em duas superfícies, para processar o conteúdo do olho esquerdo e do olho direito separadamente. Para obter um objeto que implementa IDirect3DSurface , este exemplo cria um SoftwareBitmap e, em seguida, usa esse objeto para criar um Win2D CanvasBitmap, que implementa a interface necessária. Um CanvasImageSource é um objeto Win2D que pode ser usado como a origem de um controle Image , portanto, um novo é criado e definido como a origem da Image na qual o conteúdo será exibido. Em seguida, um CanvasDrawingSession é criado. Isso é usado pelo Win2D para renderizar o efeito de desfoque.

Depois que todos os objetos necessários tiverem sido instanciados, CopyFrameToVideoSurface será chamado, o que copia o quadro atual do MediaPlayer para o CanvasBitmap. Em seguida, um Win2D GaussianBlurEffect é criado, com o CanvasBitmap definido como a origem da operação. Por fim, CanvasDrawingSession.DrawImage é chamado para desenhar a imagem de origem, com o efeito de desfoque aplicado, no CanvasImageSource que foi associado ao controle Image , fazendo com que ela seja desenhada na interface do usuário.

private async void mediaPlayer_VideoFrameAvailable(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if(frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if(canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
        {

            mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

            var gaussianBlurEffect = new GaussianBlurEffect
            {
                Source = inputBitmap,
                BlurAmount = 5f,
                Optimization = EffectOptimization.Speed
            };

            ds.DrawImage(gaussianBlurEffect);

        }
    });
}

private void FrameServerSubtitlesButton_Click(object sender, RoutedEventArgs e)
{

    mediaPlayer = new MediaPlayer();
    var source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
    var item = new MediaPlaybackItem(source);

    item.TimedMetadataTracksChanged += Item_TimedMetadataTracksChanged;


    mediaPlayer.Source = item;
    mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable_Subtitle;
    mediaPlayer.IsVideoFrameServerEnabled = true;
    mediaPlayer.Play();

    mediaPlayer.IsMuted = true;

}

private void Item_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if(sender.TimedMetadataTracks.Count > 0)
    {
        sender.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
    }
}

private async void mediaPlayer_VideoFrameAvailable_Subtitle(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if (frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if (canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        {
            using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
            {

                mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

                //Rect subtitleTargetRect = new Rect(0, 0, inputBitmap.Bounds.Width, inputBitmap.Bounds.Bottom * .1);
                Rect subtitleTargetRect = new Rect(0, 0, 100, 100);

                mediaPlayer.RenderSubtitlesToSurface(inputBitmap);//, subtitleTargetRect);

                //var gaussianBlurEffect = new GaussianBlurEffect
                //{
                //    Source = inputBitmap,
                //    BlurAmount = 5f,
                //    Optimization = EffectOptimization.Speed
                //};

                //ds.DrawImage(gaussianBlurEffect);

                ds.DrawImage(inputBitmap);
            }
        }
    });
}

Para obter mais informações sobre o Win2D, consulte o repositório GitHub do Win2D. Para experimentar o código de exemplo mostrado acima, você precisará adicionar o pacote NuGet do Win2D ao seu projeto com as instruções a seguir.

Para adicionar o pacote NuGet do Win2D ao seu projeto de efeito

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Gerenciar Pacotes NuGet.
  2. Na parte superior da janela, selecione a guia Procurar .
  3. Na caixa de pesquisa, insira Win2D.
  4. Selecione Win2D.uwp e, em seguida, selecione Instalar no painel direito.
  5. A caixa de diálogo Revisar alterações mostra o pacote a ser instalado. Clique em OK.
  6. Aceite a licença do pacote.

Detectar e responder a alterações de nível de áudio pelo sistema

A partir do Windows 10, versão 1803, seu aplicativo pode detectar quando o sistema reduz ou silencia o nível de áudio de um MediaPlayer em reprodução no momento. Por exemplo, o sistema pode diminuir ou "diminuir" o nível de reprodução de áudio quando um alarme está tocando. O sistema silenciará seu aplicativo quando ele for para o segundo plano se o aplicativo não tiver declarado a funcionalidade backgroundMediaPlayback no manifesto do aplicativo. A classe AudioStateMonitor permite que você registre para receber um evento quando o sistema modifica o volume de um fluxo de áudio. Acesse a propriedade AudioStateMonitor de um MediaPlayer e registre um manipulador para o evento SoundLevelChanged a ser notificado quando o nível de áudio desse MediaPlayer for alterado pelo sistema.

mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;

Ao manipular o evento SoundLevelChanged , você pode executar ações diferentes, dependendo do tipo de conteúdo que está sendo reproduzido. Se você estiver tocando música no momento, talvez queira permitir que a música continue a tocar enquanto o volume está reduzido. Se você estiver reproduzindo um podcast, no entanto, provavelmente desejará pausar a reprodução enquanto o áudio é reduzido para que o usuário não perca nenhum conteúdo.

Este exemplo declara uma variável para rastrear se o conteúdo em reprodução no momento é um podcast, supõe-se que você defina isso como o valor apropriado ao selecionar o conteúdo para o MediaPlayer. Também criamos uma variável de classe para rastrear quando pausamos a reprodução programaticamente quando o nível de áudio muda.

bool isPodcast;
bool isPausedDueToAudioStateMonitor;

No manipulador de eventos SoundLevelChanged, verifique a propriedade SoundLevel do remetente AudioStateMonitor para determinar o novo nível de som. Este exemplo verifica se o novo nível de som está no volume máximo, o que significa que o sistema parou de silenciar ou reduzir o volume, ou se o nível de som foi reduzido, mas está reproduzindo conteúdo que não seja podcast. Se qualquer uma dessas opções for verdadeira e o conteúdo tiver sido pausado programaticamente anteriormente, a reprodução será retomada. Se o novo nível de som estiver mudo ou se o conteúdo atual for um podcast e o nível de som estiver baixo, a reprodução será pausada e a variável será definida para rastrear que a pausa foi iniciada programaticamente.

private void AudioStateMonitor_SoundLevelChanged(Windows.Media.Audio.AudioStateMonitor sender, object args)
{
    if ((sender.SoundLevel == SoundLevel.Full) || (sender.SoundLevel == SoundLevel.Low && !isPodcast))
    {
        if (isPausedDueToAudioStateMonitor)
        {
            mediaPlayer.Play();
            isPausedDueToAudioStateMonitor = false;
        }
    }
    else if ((sender.SoundLevel == SoundLevel.Muted) ||
         (sender.SoundLevel == SoundLevel.Low && isPodcast))
    {
        if (mediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.Playing)
        {
            mediaPlayer.Pause();
            isPausedDueToAudioStateMonitor = true;
        }
    }

}

O usuário pode decidir que deseja pausar ou continuar a reprodução, mesmo que o áudio seja reduzido pelo sistema. Este exemplo mostra manipuladores de eventos para um botão de reprodução e pausa. No botão de pausa o manipulador de cliques está pausado, se a reprodução já tiver sido pausada programaticamente, atualizaremos a variável para indicar que o usuário pausou o conteúdo. No manipulador de cliques do botão play, retomamos a reprodução e limpamos nossa variável de rastreamento.

private void PauseButton_User_Click(object sender, RoutedEventArgs e)
{
    if (isPausedDueToAudioStateMonitor)
    {
        isPausedDueToAudioStateMonitor = false;
    }
    else
    {
        mediaPlayer.Pause();
    }
}

public void PlayButton_User_Click()
{
    isPausedDueToAudioStateMonitor = false;
    mediaPlayer.Play();
}