Transmissão de mídia

Este artigo mostra como converter mídia em dispositivos remotos de um aplicativo Universal do Windows.

Transmissão de mídia integrada com o MediaPlayerElement

A maneira mais simples para converter a mídia de um Aplicativo Universal do Windows é usar a funcionalidade de transmissão interna do controle do MediaPlayerElement.

Para permitir que o usuário abra um arquivo de vídeo a ser reproduzido no controle do MediaPlayerElement, adicione os seguintes namespaces ao seu projeto.

using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Media.Core;

No arquivo XAML do seu aplicativo, adicione um MediaPlayerElement e defina AreTransportControlsEnabled como verdadeiro.

<MediaPlayerElement Name="mediaPlayerElement"  MinHeight="100" MaxWidth="600" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>

Adicione um botão para permitir que o usuário inicie selecionando um arquivo.

<Button x:Name="openButton" Click="openButton_Click" Content="Open"/>

No manipulador de eventos Click para o botão, crie uma nova instância do FileOpenPicker, adicione tipos de arquivo de vídeo à coleção do FileTypeFilter e defina o local inicial para a biblioteca de vídeos do usuário.

Chame PickSingleFileAsync para iniciar a caixa de diálogo do seletor de arquivos. Quando esse método retorna, o resultado é um objeto StorageFile que representa o arquivo de vídeo. Verifique se o arquivo não é nulo, o que acontecerá se o usuário cancelar a operação de seleção. Chame o método OpenAsync do arquivo para obter um IRandomAccessStream para o arquivo. Por fim, crie um novo objeto MediaSource com base no arquivo selecionado chamando CreateFromStorageFile e o atribua ao objeto MediaPlayerElement, propriedade Source, para fazer do arquivo de vídeo a origem do controle.

private async void openButton_Click(object sender, RoutedEventArgs e)
{
    //Create a new picker
    FileOpenPicker filePicker = new FileOpenPicker();

    //Add filetype filters.  In this case wmv and mp4.
    filePicker.FileTypeFilter.Add(".wmv");
    filePicker.FileTypeFilter.Add(".mp4");

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

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

    //If we got a file, load it into the media lement
    if (file != null)
    {
        mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(file);
        mediaPlayerElement.MediaPlayer.Play();
    }
}

Depois que o vídeo é carregado no MediaPlayerElement, o usuário pode simplesmente pressionar o botão de transmissão nos controles de transporte para iniciar uma caixa de diálogo interna que permita a eles escolher um dispositivo para o qual a mídia carregada será convertida.

Botão de transmissão mediaelement

Observação

A partir do Windows 10, versão 1607, é recomendável que você use a classe MediaPlayer para reproduzir itens de mídia. O MediaPlayerElement é um controle XAML leve que é usado para renderizar o conteúdo de um MediaPlayer em uma página XAML. O controle MediaElement continua a ser suportado para compatibilidade com versões anteriores. Para obter mais informações sobre como usar o MediaPlayer e o MediaPlayerElement para reproduzir conteúdo de mídia, consulte Reproduzir áudio e vídeo com o MediaPlayer. Para obter informações sobre como usar o MediaSource e as APIs relacionadas para trabalhar com conteúdo de mídia, consulte Itens de mídia, playlists e faixas.

Transmissão de mídia com o CastingDevicePicker

Uma segunda maneira de converter mídia em um dispositivo é usar o CastingDevicePicker. Para usar essa classe, inclua o namespace Windows.Media.Casting em seu projeto.

using Windows.Media.Casting;

Declare uma variável do membro para o objeto CastingDevicePicker.

CastingDevicePicker castingPicker;

Quando sua página for inicializada, crie uma nova instância do seletor de transmissão e defina o Filter como a propriedade SupportsVideo para indicar que os dispositivos de transmissão listados pelo seletor devem dar suporte a vídeo. Registre um manipulador para o evento CastingDeviceSelected, que é acionado quando o usuário seleciona um dispositivo para transmissão.

//Initialize our picker object
castingPicker = new CastingDevicePicker();

//Set the picker to filter to video capable casting devices
castingPicker.Filter.SupportsVideo = true;

//Hook up device selected event
castingPicker.CastingDeviceSelected += CastingPicker_CastingDeviceSelected;

No seu arquivo XAML, adicione um botão para permitir que o usuário inicie o seletor.

<Button x:Name="castPickerButton" Content="Cast Button" Click="castPickerButton_Click"/>

No manipulador de eventos Click para o botão, chame TransformToVisual para obter a transformação de um elemento de interface do usuário em relação a outro. Neste exemplo, a transformação é a posição do botão do seletor de conversão em relação à raiz visual da janela do aplicativo. Chame o método Show do objeto CastingDevicePicker para iniciar a caixa de diálogo do seletor de transmissão. Especifique o local e as dimensões do botão do seletor de conversão para que o sistema possa fazer a caixa de diálogo sair do botão que o usuário pressionou.

private void castPickerButton_Click(object sender, RoutedEventArgs e)
{
    //Retrieve the location of the casting button
    GeneralTransform transform = castPickerButton.TransformToVisual(Window.Current.Content as UIElement);
    Point pt = transform.TransformPoint(new Point(0, 0));

    //Show the picker above our casting button
    castingPicker.Show(new Rect(pt.X, pt.Y, castPickerButton.ActualWidth, castPickerButton.ActualHeight),
        Windows.UI.Popups.Placement.Above);
}

No manipulador de eventos do CastingDeviceSelected, chame o método CreateCastingConnection da propriedade SelectedCastingDevice dos argumentos do evento, que representa o dispositivo de transmissão selecionado pelo usuário. Registre manipuladores para os eventos ErrorOccurred e StateChanged. Por fim, chame RequestStartCastingAsync para começar a conversão, passando o resultado para o controle MediaPlayerElement, objeto MediaPlayer, método GetAsCastingSource, para especificar que a mídia a ser convertida é o conteúdo do MediaPlayer associado ao MediaPlayerElement.

Observação

A conexão de transmissão deve ser iniciada no thread da interface do usuário. Como o CastingDeviceSelected não é chamado no thread da interface do usuário, você deve colocar essas chamadas dentro de uma chamada para CoreDispatcher.RunAsync, o que faz com que elas sejam chamadas no thread da interface do usuário.

private async void CastingPicker_CastingDeviceSelected(CastingDevicePicker sender, CastingDeviceSelectedEventArgs args)
{
    //Casting must occur from the UI thread.  This dispatches the casting calls to the UI thread.
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
    {
        //Create a casting conneciton from our selected casting device
        CastingConnection connection = args.SelectedCastingDevice.CreateCastingConnection();

        //Hook up the casting events
        connection.ErrorOccurred += Connection_ErrorOccurred;
        connection.StateChanged += Connection_StateChanged;

        //Cast the content loaded in the media element to the selected casting device
        await connection.RequestStartCastingAsync(mediaPlayerElement.MediaPlayer.GetAsCastingSource());
    });
}

Nos manipuladores de eventos ErrorOccurred e StateChanged, você deve atualizar sua interface do usuário para informar o usuário sobre o status atual da transmissão. Esses eventos são abordados em detalhes na seção a seguir sobre a criação de um seletor de dispositivo personalizado de transmissão.

private async void Connection_StateChanged(CastingConnection sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

private async void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

Transmissão de mídia com um seletor de dispositivo personalizado

A seção a seguir descreve como criar seu próprio seletor de dispositivo de transmissão da interface do usuário, enumerar os dispositivos de transmissão e inicializar a conexão do seu código.

Para enumerar os dispositivos de transmissão disponíveis, inclua o namespace do Windows.Devices.Enumeration em seu projeto.

using Windows.Devices.Enumeration;

Adicione os seguintes controles à página XAML para implementar a interface do usuário rudimentar para este exemplo:

  • Um botão para iniciar o observador de dispositivo que procura por dispositivos de transmissão disponíveis.
  • Um controle ProgressRing para fornecer comentários ao usuário os quais a transmissão já está em andamento.
  • Um ListBox para listar os dispositivos de transmissão descobertos. Defina um ItemTemplate para o controle para que possamos atribuir os objetos de dispositivos de transmissão diretamente para o controle e ainda exibir a propriedade FriendlyName.
  • Um botão para permitir que o usuário desconecte o dispositivo de transmissão.
<Button x:Name="startWatcherButton" Content="Watcher Button" Click="startWatcherButton_Click"/>
<ProgressRing x:Name="watcherProgressRing" IsActive="False"/>
<ListBox x:Name="castingDevicesListBox" MaxWidth="300" HorizontalAlignment="Left" SelectionChanged="castingDevicesListBox_SelectionChanged">
    <!--Listbox content is bound to the FriendlyName field of our casting devices-->
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=FriendlyName}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
<Button x:Name="disconnectButton" Content="Disconnect" Click="disconnectButton_Click" Visibility="Collapsed"/>

Em seu code-behind, declare variáveis de membro para o DeviceWatcher e o CastingConnection.

DeviceWatcher deviceWatcher;
CastingConnection castingConnection;

No manipulador Click para o startWatcherButton, primeiro atualize a interface do usuário, desativando o botão e tornando o anel de progresso ativo enquanto a enumeração do dispositivo está em andamento. Desmarque a caixa de listagem dos dispositivos de transmissão.

Em seguida, crie um inspetor de dispositivos chamando DeviceInformation.CreateWatcher. Esse método pode ser usado para assistir a muitos tipos diferentes de dispositivos. Especificar que você deseja ver dispositivos que suportam transmissão de vídeo usando a cadeia de caracteres do seletor de dispositivo retornado como CastingDevice.GetDeviceSelector.

Por fim, registre manipuladores de eventos para os eventos Added, Removed, EnumerationCompleted e Stopped.

private void startWatcherButton_Click(object sender, RoutedEventArgs e)
{
    startWatcherButton.IsEnabled = false;
    watcherProgressRing.IsActive = true;

    castingDevicesListBox.Items.Clear();

    //Create our watcher and have it find casting devices capable of video casting
    deviceWatcher = DeviceInformation.CreateWatcher(CastingDevice.GetDeviceSelector(CastingPlaybackTypes.Video));

    //Register for watcher events
    deviceWatcher.Added += DeviceWatcher_Added;
    deviceWatcher.Removed += DeviceWatcher_Removed;
    deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
    deviceWatcher.Stopped += DeviceWatcher_Stopped;

    //Start the watcher
    deviceWatcher.Start();
}

O evento Added é acionado quando um novo dispositivo é descoberto pelo inspetor. No manipulador para este evento, crie um novo objeto CastingDevice chamando CastingDevice.FromIdAsync e passando a ID do dispositivo descoberto para transmissão, que está contido no objeto DeviceInformation passado para o manipulador.

Adicione o CastingDevice para o dispositivo de transmissão ListBox para que o usuário possa selecioná-lo. Devido ao ItemTemplate definido em XAML, a propriedade FriendlyName será usada como o texto do item na caixa de listagem. Como esse manipulador de eventos não é chamado no thread da interface do usuário, você deve atualizar a interface do usuário de dentro de uma chamada para CoreDispatcher.RunAsync.

private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
    {
        //Add each discovered device to our listbox
        CastingDevice addedDevice = await CastingDevice.FromIdAsync(args.Id);
        castingDevicesListBox.Items.Add(addedDevice);
    });
}

O evento Removed é acionado quando o inspetor detecta que um dispositivo de transmissão não está mais presente. Compare a propriedade do ID do objeto Added passado para o manipulador para o ID de cada Added na coleção da caixa de listagem Items. Se a ID corresponde, remova esse objeto da coleção. Novamente, como a interface do usuário está sendo atualizada, essa chamada deve ser feita de dentro do RunAsync.

private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        foreach (CastingDevice currentDevice in castingDevicesListBox.Items)
        {
            if (currentDevice.Id == args.Id)
            {
                castingDevicesListBox.Items.Remove(currentDevice);
            }
        }
    });
}

O evento EnumerationCompleted é acionado quando o inspetor concluiu a detecção de dispositivos. No manipulador para esse evento, atualize a interface do usuário para permitir que o usuário saiba que a enumeração do dispositivo foi concluída e pare o observador de dispositivo chamando Stop.

private async void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //If enumeration completes, update UI and transition watcher to the stopped state
        ShowMessageToUser("Watcher completed enumeration of devices");
        deviceWatcher.Stop();
    });
}

O evento Stopped é gerado quando o inspetor de dispositivo tiver terminado de parar. No manipulador para este evento, interrompa o controle do ProgressRing e reabilite o startWatcherButton para que o usuário possa reiniciar o processo de enumeração do dispositivo.

private async void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Update UX when the watcher stops
        startWatcherButton.IsEnabled = true;
        watcherProgressRing.IsActive = false;
    });
}

Quando o usuário seleciona um dos dispositivos de transmissão na caixa de listagem, o evento SelectionChanged é acionado. É dentro desse manipulador que a conexão de transmissão será criada e a transmissão será iniciada.

Primeiro, verifique se que o inspetor de dispositivos é interrompido para que a enumeração de dispositivo não esteja interferindo na transmissão de mídia. Crie uma conexão de transmissão chamando CreateCastingConnection sobre o objeto CastingDevice selecionado pelo usuário. Adicione os manipuladores de eventos para os eventos StateChanged e ErrorOccurred.

Inicie a transmissão de mídia chamando RequestStartCastingAsync, passando na origem da transmissão retornada pela chamada do método MediaPlayerGetAsCastingSource. Por fim, torne o botão desconectar visível para permitir que o usuário interrompa a transmissão de mídia.

private async void castingDevicesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (castingDevicesListBox.SelectedItem != null)
    {
        //When a device is selected, first thing we do is stop the watcher so it's search doesn't conflict with streaming
        if (deviceWatcher.Status != DeviceWatcherStatus.Stopped)
        {
            deviceWatcher.Stop();
        }

        //Create a new casting connection to the device that's been selected
        castingConnection = ((CastingDevice)castingDevicesListBox.SelectedItem).CreateCastingConnection();

        //Register for events
        castingConnection.ErrorOccurred += Connection_ErrorOccurred;
        castingConnection.StateChanged += Connection_StateChanged;

        //Cast the loaded video to the selected casting device.
        await castingConnection.RequestStartCastingAsync(mediaPlayerElement.MediaPlayer.GetAsCastingSource());
        disconnectButton.Visibility = Visibility.Visible;
    }
}

No manipulador de alteração de estado, a ação executada depende do novo estado da conexão da transmissão:

  • Se o estado for Connected ou Rendering, verifique se o controle do ProgressRing está inativo e o botão desconectar está visível.
  • Se o estado for Disconnected, desmarque o dispositivo de transmissão atual na caixa de listagem, deixe o controle do ProgressRing inativo e oculte o botão desconectar.
  • Se o estado for Connecting, deixe o controle do ProgressRing ativo e oculte o botão desconectar.
  • Se o estado for Disconnecting, deixe o controle do ProgressRing ativo e oculte o botão desconectar.
private async void Connection_StateChanged(CastingConnection sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Update the UX based on the casting state
        if (sender.State == CastingConnectionState.Connected || sender.State == CastingConnectionState.Rendering)
        {
            disconnectButton.Visibility = Visibility.Visible;
            watcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Disconnected)
        {
            disconnectButton.Visibility = Visibility.Collapsed;
            castingDevicesListBox.SelectedItem = null;
            watcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Connecting)
        {
            disconnectButton.Visibility = Visibility.Collapsed;
            ShowMessageToUser("Connecting");
            watcherProgressRing.IsActive = true;
        }
        else
        {
            //Disconnecting is the remaining state
            disconnectButton.Visibility = Visibility.Collapsed;
            watcherProgressRing.IsActive = true;
        }
    });
}

No manipulador para o evento ErrorOccurred, atualize sua interface do usuário para permitir que o usuário saiba que ocorreu um erro de transmissão e desmarque o objeto CastingDevice atual na caixa de listagem.

private async void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Clear the selection in the listbox on an error
        ShowMessageToUser("Casting Error: " + args.Message);
        castingDevicesListBox.SelectedItem = null;
    });
}

Por fim, implemente o manipulador para o botão desconectar. Pare a transmissão de mídia e desconecte o dispositivo de transmissão chamando o método CastingConnection do objeto DisconnectAsync. Essa chamada deve ser enviada para o thread da interface do usuário chamando CoreDispatcher.RunAsync.

private async void disconnectButton_Click(object sender, RoutedEventArgs e)
{
    if (castingConnection != null)
    {
        //When disconnect is clicked, the casting conneciton is disconnected.  The video should return locally to the media element.
        await castingConnection.DisconnectAsync();
    }
}