Habilitar reprodução de áudio de dispositivos conectados remotamente por Bluetooth
Este artigo mostra como usar AudioPlaybackConnection para permitir que dispositivos remotos conectados por Bluetooth reproduzam áudio no computador local.
A partir do Windows 10, as fontes de áudio remotas da versão 2004 podem transmitir áudio para dispositivos Windows, permitindo cenários como configurar um computador para se comportar como um alto-falante Bluetooth e permitir que os usuários ouçam o áudio de seus telefones. A implementação usa os componentes Bluetooth no sistema operacional para processar dados de áudio de entrada e reproduzi-los nos pontos de extremidade de áudio do sistema, como alto-falantes integrados do PC ou fones de ouvido com fio. A habilitação do coletor Bluetooth A2DP subjacente é gerenciada por aplicativos, que são responsáveis pelo cenário do usuário final, e não pelo sistema.
A classe AudioPlaybackConnection é usada para habilitar e desabilitar conexões de um dispositivo remoto, bem como para criar a conexão, permitindo que a reprodução remota de áudio comece.
Adicionar uma interface do usuário
Para os exemplos neste artigo, usaremos a seguinte interface do usuário XAML simples que define o controle ListView para exibir dispositivos remotos disponíveis, um TextBlock para exibir o status da conexão e três botões para habilitar, desabilitar e abrir conexões.
<Grid x:Name="MainGrid" Loaded="MainGrid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Connection state: "/>
<TextBlock x:Name="ConnectionState" Grid.Row="0" Text="Disconnected."/>
</StackPanel>
<ListView x:Name="DeviceListView" ItemsSource="{x:Bind devices}" Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate x:DataType="enumeration:DeviceInformation">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind Name}" FontWeight="Bold"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Vertical" Grid.Row="2">
<Button x:Name="EnableAudioPlaybackConnectionButton" Content="Enable Audio Playback Connection" Click="EnableAudioPlaybackConnectionButton_Click"/>
<Button x:Name="ReleaseAudioPlaybackConnectionButton" Content="Release Audio Playback Connection" Click="ReleaseAudioPlaybackConnectionButton_Click"/>
<Button x:Name="OpenAudioPlaybackConnectionButtonButton" Content="Open Connection" Click="OpenAudioPlaybackConnectionButtonButton_Click" IsEnabled="False"/>
</StackPanel>
</Grid>
Usar o DeviceWatcher para monitorar dispositivos remotos
A classe DeviceWatcher permite detectar dispositivos conectados. O método AudioPlaybackConnection.GetDeviceSelector retorna uma cadeia de caracteres que informa ao observador do dispositivo quais tipos de dispositivos observar. Passe essa cadeia de caracteres para o construtor DeviceWatcher .
O evento DeviceWatcher.Added é gerado para cada dispositivo conectado quando o inspetor de dispositivo é iniciado, bem como para qualquer dispositivo conectado enquanto o inspetor de dispositivo está em execução. O evento DeviceWatcher.Removed será gerado se um dispositivo conectado anteriormente for desconectado.
Chame DeviceWatcher.Start para começar a observar os dispositivos conectados que dão suporte a conexões de reprodução de áudio. Neste exemplo, iniciaremos o gerenciador de dispositivos quando o controle Grid principal na interface do usuário for carregado. Para obter mais informações sobre como usar o DeviceWatcher, consulte Enumerar dispositivos.
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
audioPlaybackConnections = new Dictionary<string, AudioPlaybackConnection>();
// Start watching for paired Bluetooth devices.
this.deviceWatcher = DeviceInformation.CreateWatcher(AudioPlaybackConnection.GetDeviceSelector());
// Register event handlers before starting the watcher.
this.deviceWatcher.Added += this.DeviceWatcher_Added;
this.deviceWatcher.Removed += this.DeviceWatcher_Removed;
this.deviceWatcher.Start();
}
No evento Added do observador do dispositivo, cada dispositivo descoberto é representado por um objeto DeviceInformation. Adicione cada dispositivo descoberto a uma coleção observável associada ao controle ListView na interface do usuário.
private ObservableCollection<Windows.Devices.Enumeration.DeviceInformation> devices =
new ObservableCollection<Windows.Devices.Enumeration.DeviceInformation>();
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
{
// Collections bound to the UI are updated in the UI thread.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
this.devices.Add(deviceInfo);
});
}
Ativar e liberar conexões de reprodução de áudio
Antes de abrir uma conexão com um dispositivo, a conexão deve ser habilitada. Isso informa ao sistema que há um novo aplicativo que deseja que o áudio do dispositivo remoto seja reproduzido no PC, mas o áudio não começa a ser reproduzido até que a conexão seja aberta, o que é mostrado em uma etapa posterior.
No manipulador de cliques do botão Habilitar Conexão de Reprodução de Áudio , obtenha a ID do dispositivo associada ao dispositivo selecionado no momento no controle ListView . Este exemplo mantém um dicionário de objetos AudioPlaybackConnection que foram habilitados. Esse método primeiro verifica se já existe uma entrada no dicionário para o dispositivo selecionado. Em seguida, o método tenta criar um AudioPlaybackConnection para o dispositivo selecionado chamando TryCreateFromId e passando a ID do dispositivo selecionado.
Se a conexão for criada com êxito, adicione o novo objeto AudioPlaybackConnection ao dicionário do aplicativo, registre um manipulador para o evento StateChanged do objeto e chameStartAsync para notificar o sistema de que a nova conexão está habilitada.
private Dictionary<String, AudioPlaybackConnection> audioPlaybackConnections;
private async void EnableAudioPlaybackConnectionButton_Click(object sender, RoutedEventArgs e)
{
if (! (DeviceListView.SelectedItem is null))
{
var selectedDeviceId = (DeviceListView.SelectedItem as DeviceInformation).Id;
if (!this.audioPlaybackConnections.ContainsKey(selectedDeviceId))
{
// Create the audio playback connection from the selected device id and add it to the dictionary.
// This will result in allowing incoming connections from the remote device.
var playbackConnection = AudioPlaybackConnection.TryCreateFromId(selectedDeviceId);
if (playbackConnection != null)
{
// The device has an available audio playback connection.
playbackConnection.StateChanged += this.AudioPlaybackConnection_ConnectionStateChanged;
this.audioPlaybackConnections.Add(selectedDeviceId, playbackConnection);
await playbackConnection.StartAsync();
OpenAudioPlaybackConnectionButtonButton.IsEnabled = true;
}
}
}
}
Abra a conexão de reprodução de áudio
Na etapa anterior, uma conexão de reprodução de áudio foi criada, mas o som não começa a ser reproduzido até que a conexão seja aberta chamando Open ou OpenAsync. No manipulador de cliques do botão Abrir Conexão de Reprodução de Áudio , obtenha o dispositivo selecionado no momento e use a ID para recuperar o AudioPlaybackConnection do dicionário de conexões do aplicativo. Aguarde uma chamada para OpenAsync e verifique o valor Status do objeto AudioPlaybackConnectionOpenResultStatus retornado para ver se a conexão foi aberta com êxito e, em caso afirmativo, atualize a caixa de texto do estado da conexão.
private async void OpenAudioPlaybackConnectionButtonButton_Click(object sender, RoutedEventArgs e)
{
var selectedDevice = (DeviceListView.SelectedItem as DeviceInformation).Id;
AudioPlaybackConnection selectedConnection;
if (this.audioPlaybackConnections.TryGetValue(selectedDevice, out selectedConnection))
{
if ((await selectedConnection.OpenAsync()).Status == AudioPlaybackConnectionOpenResultStatus.Success)
{
// Notify that the AudioPlaybackConnection is connected.
ConnectionState.Text = "Connected";
}
else
{
// Notify that the connection attempt did not succeed.
ConnectionState.Text = "Disconnected (attempt failed)";
}
}
}
Monitorar o estado da conexão de reprodução de áudio
O evento AudioPlaybackConnection.ConnectionStateChanged é gerado sempre que o estado da conexão é alterado. Neste exemplo, o manipulador desse evento atualiza a caixa de texto de status. Lembre-se de atualizar a interface do usuário dentro de uma chamada para Dispatcher.RunAsync para garantir que a atualização seja feita no thread da interface do usuário.
private async void AudioPlaybackConnection_ConnectionStateChanged(AudioPlaybackConnection sender, object args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if (sender.State == AudioPlaybackConnectionState.Closed)
{
ConnectionState.Text = "Disconnected";
}
else if (sender.State == AudioPlaybackConnectionState.Opened)
{
ConnectionState.Text = "Connected";
}
else
{
ConnectionState.Text = "Unknown";
}
});
}
Libere as conexões e manuseie os dispositivos removidos
Este exemplo fornece um botão Liberar Conexão de Reprodução de Áudio para permitir que o usuário libere uma conexão de reprodução de áudio. No manipulador desse evento, obtemos o dispositivo selecionado no momento e usamos a ID do dispositivo para pesquisar o AudioPlaybackConnection no dicionário. Chame Dispose para liberar a referência e liberar todos os recursos associados e remover a conexão do dicionário.
private void ReleaseAudioPlaybackConnectionButton_Click(object sender, RoutedEventArgs e)
{
// Check if an audio playback connection was already created for the selected device Id. If it was then release its reference to deactivate it.
// The underlying transport is deactivated when all references are released.
if (!(DeviceListView.SelectedItem is null))
{
var selectedDeviceId = (DeviceListView.SelectedItem as DeviceInformation).Id;
if (audioPlaybackConnections.ContainsKey(selectedDeviceId))
{
AudioPlaybackConnection connectionToRemove = audioPlaybackConnections[selectedDeviceId];
connectionToRemove.Dispose();
this.audioPlaybackConnections.Remove(selectedDeviceId);
// Notify that the media device has been deactivated.
ConnectionState.Text = "Disconnected";
OpenAudioPlaybackConnectionButtonButton.IsEnabled = false;
}
}
}
Você deve lidar com o caso em que um dispositivo é removido enquanto uma conexão está ativada ou aberta. Para fazer isso, implemente um manipulador para o evento DeviceWatcher.Removed do observador do dispositivo. Primeiro, a ID do dispositivo removido é usada para remover o dispositivo da coleção observável associada ao controle ListView do aplicativo. Em seguida, se uma conexão associada a esse dispositivo estiver no dicionário do aplicativo, Dispose será chamado para liberar os recursos associados e, em seguida, a conexão será removida do dicionário. Tudo isso é feito em uma chamada para Dispatcher.RunAsync para garantir que as atualizações da interface do usuário sejam executadas no thread da interface do usuário.
private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
{
// Collections bound to the UI are updated in the UI thread.
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Find the device for the given id and remove it from the list.
foreach (DeviceInformation device in this.devices)
{
if (device.Id == deviceInfoUpdate.Id)
{
this.devices.Remove(device);
break;
}
}
if (audioPlaybackConnections.ContainsKey(deviceInfoUpdate.Id))
{
AudioPlaybackConnection connectionToRemove = audioPlaybackConnections[deviceInfoUpdate.Id];
connectionToRemove.Dispose();
this.audioPlaybackConnections.Remove(deviceInfoUpdate.Id);
}
});
}
Tópicos relacionados