Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo mostra-lhe como transmitir media para dispositivos remotos a partir de uma aplicação WinUI.
Casting multimédia integrado com MediaPlayerElement
A forma mais simples de transmitir media a partir de uma aplicação WinUI é usar a capacidade de casting integrada do controlo MediaPlayerElement .
No ficheiro XAML da sua aplicação, adicione um MediaPlayerElement e defina o AreTransportControlsEnabled como true.
<MediaPlayerElement Name="mediaPlayerElement" MinHeight="100" MaxWidth="600" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>
Adicione um botão para permitir que o utilizador inicie a escolha de um ficheiro.
<Button x:Name="bOpenButton" Click="bOpenButton_Click" Content="Open"/>
No handler de eventos Click para o botão, crie uma nova instância do FileOpenPicker, adicione tipos de ficheiros de vídeo à coleção FileTypeFilter e defina a localização inicial para a biblioteca de vídeos do utilizador.
Chame PickSingleFileAsync para lançar o diálogo do seletor de ficheiros. Quando este método retorna, o resultado é um objeto StorageFile que representa o ficheiro de vídeo. Verifica se o ficheiro não é nulo, o que acontecerá se o utilizador cancelar a operação de seleção. Chame o método OpenAsync do ficheiro para obter um IRandomAccessStream correspondente ao ficheiro. Finalmente, crie um novo objeto MediaSource a partir do ficheiro selecionado chamando o CreateFromStorageFile e atribua-o à propriedade Source do objeto MediaPlayerElement para tornar o ficheiro de vídeo a fonte de vídeo do controlo.
private async void bOpenButton_Click(object sender, RoutedEventArgs e)
{
//Create a new picker
var filePicker = new Microsoft.Windows.Storage.Pickers.FileOpenPicker(this.AppWindow.Id)
{
SuggestedStartLocation = PickerLocationId.VideosLibrary,
FileTypeFilter = { ".wmv", ".mp4", ".mkv" },
};
//Retrieve file from picker
var result = await filePicker.PickSingleFileAsync();
if (result is not null)
{
var storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(result.Path);
mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(storageFile);
mediaPlayerElement.MediaPlayer.Play();
}
}
Depois de o vídeo ser carregado no MediaPlayerElement, o utilizador pode simplesmente pressionar o botão de casting nos controlos de transporte para iniciar um diálogo incorporado que lhe permite escolher o dispositivo para onde o media carregado será transmitido.
Casting de media com o CastingDevicePicker
Uma segunda forma de transmitir media para um dispositivo é usar o CastingDevicePicker. Primeiro, declare uma variável membro para o objeto Windows.Media.Casting.CastingDevicePicker .
CastingDevicePicker castingPicker;
Quando a sua janela for inicializada, crie uma nova instância do seletor de casting e defina a propriedade Filter para SupportsVideo para indicar que os dispositivos de casting listados pelo picker devem suportar vídeo. Registar um manipulador para o evento CastingDeviceSelected, que é ativado quando o utilizador escolhe 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 teu ficheiro XAML, adiciona um botão para permitir que o utilizador inicie o seletor.
<Button x:Name="bCastPickerButton" Content="Cast Button" Click="bCastPickerButton_Click"/>
No manipulador de eventos Click para o botão, chamar TransformToVisual para obter a transformação de um elemento de interface do utilizador em relação a outro elemento. Neste exemplo, a transformação é a posição do botão de seleção de cast em relação à raiz visual da janela da aplicação. Chame o método Show do objeto CastingDevicePicker para iniciar o diálogo do casting picker. Especifique a localização e as dimensões do botão de seleção de fundição para que o sistema possa fazer o diálogo sair do botão que o utilizador pressionou.
private void bCastPickerButton_Click(object sender, RoutedEventArgs e)
{
//Retrieve the location of the casting button
GeneralTransform transform = bCastPickerButton.TransformToVisual(this.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, bCastPickerButton.ActualWidth, bCastPickerButton.ActualHeight),
Windows.UI.Popups.Placement.Above);
}
No gestor de eventos CastingDeviceSelected, chame o método CreateCastingConnection da propriedade SelectedCastingDevice dos argumentos do evento, que representa o dispositivo de casting selecionado pelo utilizador. Registar manipuladores para os eventos ErrorOccurred e StateChanged. Finalmente, chame RequestStartCastingAsync para iniciar a transmissão, passando o resultado para o método GetAsCastingSource do objeto MediaPlayer do controlo MediaPlayerElement para especificar que o media a ser transmitido é o conteúdo do MediaPlayer associado ao MediaPlayerElement.
Observação
A ligação de casting deve ser iniciada no thread da interface. Como o CastingDeviceSelected não é chamado no thread da UI, deve colocar estas invocações dentro de uma chamada para DispatcherQueue.TryEnqueue, o que faz com que sejam executadas no thread da UI.
private void CastingPicker_CastingDeviceSelected(CastingDevicePicker sender, CastingDeviceSelectedEventArgs args)
{
//Casting must occur from the UI thread. This dispatches the casting calls to the UI thread.
DispatcherQueue.TryEnqueue( 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 gestores de eventos ErrorOccurred e StateChanged, deve atualizar a interface de utilizador para informar o utilizador do estado atual do casting. Estes eventos são discutidos em detalhe na secção seguinte sobre a criação de um seletor personalizado de dispositivos de fundição.
private void Connection_StateChanged(CastingConnection sender, object args)
{
DispatcherQueue.TryEnqueue( () =>
{
ShowMessageToUser("Casting Connection State Changed: " + sender.State);
});
}
private void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
DispatcherQueue.TryEnqueue(() =>
{
ShowMessageToUser("Casting Connection State Changed: " + sender.State);
});
}
Transmissão de mídia com um seletor de dispositivo personalizado
A secção seguinte descreve como criar a sua própria interface seletora de dispositivos de casting, enumerando os dispositivos de casting e iniciando a ligação a partir do seu código.
Adicione os seguintes controlos à sua página XAML para implementar a interface rudimentar deste exemplo:
- Um botão para iniciar o observador de dispositivos que procura dispositivos de casting disponíveis.
- Um controlo ProgressRing para indicar ao utilizador que a enumeração de transmissão está em curso.
- Uma ListBox para listar os dispositivos de casting descobertos. Definir um ItemTemplate para o controlo para que possamos associar os objetos do dispositivo de casting diretamente ao controlo e seguir a mostrar a propriedade FriendlyName.
- Um botão que permite ao utilizador desligar o dispositivo de casting.
<Button x:Name="bStartWatcherButton" Content="Watcher Button" Click="bStartWatcherButton_Click"/>
<ProgressRing x:Name="prWatcherProgressRing" IsActive="False"/>
<ListBox x:Name="lbCastingDevicesListBox" MaxWidth="300" HorizontalAlignment="Left" SelectionChanged="lbCastingDevicesListBox_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="bDisconnectButton" Content="Disconnect" Click="bDisconnectButton_Click" Visibility="Collapsed"/>
No código por detrás, declare variáveis de membro do DeviceWatcher e do CastingConnection.
DeviceWatcher deviceWatcher;
CastingConnection castingConnection;
No manipulador de cliques do startWatcherButton, primeiro, atualize a interface desativando o botão e ativando o anel de progresso enquanto a enumeração de dispositivos está em curso. Limpa a caixa da lista de dispositivos de lançamento.
De seguida, crie um observador de dispositivos chamando DeviceInformation.CreateWatcher. Este método pode ser usado para observar muitos tipos diferentes de dispositivos. Especifique que deseja monitorizar dispositivos que suportem casting de vídeo usando a string seletora de dispositivos devolvida pelo CastingDevice.GetDeviceSelector.
Por fim, regista os manipuladores de eventos para os eventos Adicionado, Removido, EnumeraçãoConcluída e Parado.
private void bStartWatcherButton_Click(object sender, RoutedEventArgs e)
{
bStartWatcherButton.IsEnabled = false;
prWatcherProgressRing.IsActive = true;
lbCastingDevicesListBox.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 Adicionado é ativado quando um novo dispositivo é descoberto pelo observador. No handler deste evento, cria um novo objeto CastingDevice chamando CastingDevice.FromIdAsync e passando o ID do dispositivo de casting descoberto, que está contido no objeto DeviceInformation passado para o handler.
Adicione o CastingDevice à ListBox do dispositivo de casting para que o utilizador possa selecioná-lo. Devido ao ItemTemplate definido no XAML, a propriedade FriendlyName será usada como texto do item na caixa da lista. Como este gestor de eventos não é chamado na thread da interface, deve atualizar a interface a partir de uma chamada para DispatcherQueue.TryEnqueue.
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
DispatcherQueue.TryEnqueue(async () =>
{
//Add each discovered device to our listbox
CastingDevice addedDevice = await CastingDevice.FromIdAsync(args.Id);
lbCastingDevicesListBox.Items.Add(addedDevice);
});
}
O evento Removido é ativado quando o observador deteta que um dispositivo de casting já não está presente. Compare a propriedade ID do objeto Adicionado passado para o handler com o ID de cada Adicionado na coleção de Itens da caixa de lista. Se o ID coincidir, remova esse objeto da coleção. Mais uma vez, como a interface está a ser atualizada, esta chamada tem de ser feita dentro de uma chamada RunAsync .
private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
DispatcherQueue.TryEnqueue( () =>
{
foreach (CastingDevice currentDevice in lbCastingDevicesListBox.Items)
{
if (currentDevice.Id == args.Id)
{
lbCastingDevicesListBox.Items.Remove(currentDevice);
}
}
});
}
O evento EnumerationCompleted é ativado quando o observador termina de detetar dispositivos. No handler deste evento, atualize a interface para informar o utilizador que a enumeração do dispositivo foi concluída e pare o observador do dispositivo chamando Stop.
private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
DispatcherQueue.TryEnqueue(() =>
{
//If enumeration completes, update UI and transition watcher to the stopped state
ShowMessageToUser("Watcher completed enumeration of devices");
deviceWatcher.Stop();
});
}
O evento Stopped é ativado quando o observador do dispositivo termina de parar. No handler deste evento, parar o controlo ProgressRing e reativar o startWatcherButton para que o utilizador possa reiniciar o processo de enumeração do dispositivo.
private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
DispatcherQueue.TryEnqueue( () =>
{
//Update UX when the watcher stops
bStartWatcherButton.IsEnabled = true;
prWatcherProgressRing.IsActive = false;
});
}
Quando o utilizador seleciona um dos dispositivos de casting da caixa de lista, o evento SelectionChanged é disparado. É dentro deste manipulador que a ligação de transmissão será criada e a transmissão será iniciada.
Primeiro, certifique-se de que o observador de dispositivos está parado para evitar que a enumeração de dispositivos interfira com a transmissão de mídia. Crie uma ligação de casting chamando CreateCastingConnection no objeto CastingDevice selecionado pelo utilizador. Adicionar manipuladores de eventos para os StateChanged e ErrorOccurred.
Inicie a transmissão de media chamando RequestStartCastingAsync, fornecendo a fonte de casting devolvida pelo método MediaPlayer GetAsCastingSource. Por fim, torne o botão de desligar visível para permitir ao utilizador parar a transmissão de media.
private async void lbCastingDevicesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lbCastingDevicesListBox.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)lbCastingDevicesListBox.SelectedItem).CreateCastingConnection();
//Register for events
castingConnection.ErrorOccurred += CastingConnection_ErrorOccurred; ;
castingConnection.StateChanged += CastingConnection_StateChanged; ;
//Cast the loaded video to the selected casting device.
await castingConnection.RequestStartCastingAsync(mediaPlayerElement.MediaPlayer.GetAsCastingSource());
bDisconnectButton.Visibility = Visibility.Visible;
}
}
No gestor de mudança de estado, a ação a tomar depende do novo estado da ligação de transmissão:
- Se o estado for Connected ou Rendering, certifique-se de que o controlo ProgressRing está inativo e o botão de desconexão está visível.
- Se o estado estiver Desligado, desmarque o dispositivo de casting atual na caixa da lista, torne o controlo ProgressRing inativo e oculte o botão de desconexão.
- Se o estado estiver a Conectar, ativa o controlo ProgressRing e oculta o botão de desconectar.
- Se o estado for Desligando, ative o controlo ProgressRing e oculte o botão de desconexão.
private void CastingConnection_StateChanged(CastingConnection sender, object args)
{
DispatcherQueue.TryEnqueue( () =>
{
//Update the UX based on the casting state
if (sender.State == CastingConnectionState.Connected || sender.State == CastingConnectionState.Rendering)
{
bDisconnectButton.Visibility = Visibility.Visible;
prWatcherProgressRing.IsActive = false;
}
else if (sender.State == CastingConnectionState.Disconnected)
{
bDisconnectButton.Visibility = Visibility.Collapsed;
lbCastingDevicesListBox.SelectedItem = null;
prWatcherProgressRing.IsActive = false;
}
else if (sender.State == CastingConnectionState.Connecting)
{
bDisconnectButton.Visibility = Visibility.Collapsed;
ShowMessageToUser("Connecting");
prWatcherProgressRing.IsActive = true;
}
else
{
//Disconnecting is the remaining state
bDisconnectButton.Visibility = Visibility.Collapsed;
prWatcherProgressRing.IsActive = true;
}
});
}
No handler do evento ErrorOccurred, atualize a sua interface para informar o utilizador que ocorreu um erro de transmissão e desmarque o objeto CastingDevice atual na caixa de lista.
private void CastingConnection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
DispatcherQueue.TryEnqueue( () =>
{
//Clear the selection in the listbox on an error
ShowMessageToUser("Casting Error: " + args.Message);
lbCastingDevicesListBox.SelectedItem = null;
});
}
Por fim, implementa o handler para o botão de desligação. Pare a transmissão de media e desligue-se do dispositivo de casting chamando o método DisconnectAsync do objeto CastingConnection. Esta chamada deve ser despachada para o thread da interface chamando DispatcherQueue.TryEnqueue.
private async void bDisconnectButton_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();
}
}
Windows developer