Включение воспроизведения звука с устройств, подключенных удаленно по Bluetooth
В этой статье показано, как использовать AudioPlaybackConnection , чтобы разрешить удаленным устройствам, подключенным по Bluetooth, воспроизводить звук на локальном компьютере.
Начиная с Windows 10, удаленные источники звука версии 2004 могут передавать звук на устройства Windows, что позволяет выполнять такие сценарии, как настройка компьютера для работы как динамик Bluetooth и предоставление пользователям возможности слышать звук со своего телефона. Реализация использует компоненты Bluetooth в ОПЕРАЦИОННОй системе для обработки входящих звуковых данных и воспроизведения их на конечных точках аудио системы в системе, таких как встроенные динамики пк или проводные наушники. Включение базового приемника Bluetooth A2DP управляется приложениями, которые отвечают за сценарий конечного пользователя, а не системой.
Класс AudioPlaybackConnection используется для включения и отключения подключений с удаленного устройства, а также для создания подключения, что позволяет начать удаленное воспроизведение звука.
Добавление пользовательского интерфейса
В примерах в этой статье мы будем использовать следующий простой пользовательский интерфейс XAML, который определяет элемент управления ListView для отображения доступных удаленных устройств, TextBlock для отображения состояния подключения и три кнопки для включения, отключения и открытия подключений.
<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>
Мониторинг удаленных устройств с помощью DeviceWatcher
Класс DeviceWatcher позволяет обнаруживать подключенные устройства. Метод AudioPlaybackConnection.GetDeviceSelector возвращает строку, которая сообщает наблюдателю за устройствами, для каких типов устройств следует watch. Передайте эту строку в конструктор DeviceWatcher .
Событие DeviceWatcher.Added возникает для каждого устройства, подключенного при запуске наблюдателя за устройствами, а также для любого устройства, подключенного во время работы наблюдателя за устройствами. Событие DeviceWatcher.Removed возникает, если ранее подключенное устройство отключается.
Вызовите DeviceWatcher.Start , чтобы начать наблюдение за подключенными устройствами, поддерживающими подключения воспроизведения звука. В этом примере мы запустим диспетчер устройств при загрузке элемента управления "Сетка main" в пользовательском интерфейсе. Дополнительные сведения об использовании DeviceWatcher см. в разделе Перечисление устройств.
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();
}
В событии Добавлено наблюдателя за устройствами каждое обнаруженное устройство представлено объектом DeviceInformation . Добавьте каждое обнаруженное устройство в наблюдаемую коллекцию, привязанную к элементу управления ListView в пользовательском интерфейсе.
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);
});
}
Включение и освобождение подключений воспроизведения звука
Перед открытием подключения к устройству его необходимо включить. Это сообщает системе о том, что существует новое приложение, которое хочет воспроизводить звук с удаленного устройства на компьютере, но звук не начнет воспроизводиться до тех пор, пока подключение не будет открыто, как показано на следующем шаге.
В обработчике нажатия кнопки Включить подключение к воспроизведению звука получите идентификатор устройства, связанный с текущим выбранным устройством в элементе управления ListView . В этом примере ведется словарь включенных объектов AudioPlaybackConnection . Этот метод сначала проверяет наличие записи в словаре для выбранного устройства. Затем метод пытается создать AudioPlaybackConnection для выбранного устройства путем вызова TryCreateFromId и передачи выбранного идентификатора устройства.
Если подключение успешно создано, добавьте новый объект AudioPlaybackConnection в словарь приложения, зарегистрируйте обработчик для события StateChanged объекта и вызовитеStartAsync , чтобы уведомить систему о том, что новое подключение включено.
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;
}
}
}
}
Открытие подключения к воспроизведению звука
На предыдущем шаге было создано подключение к воспроизведению звука, но звук не начинает воспроизводиться, пока подключение не будет открыто путем вызова Open или OpenAsync. В обработчике нажатия кнопки Открыть подключение воспроизведения звука получите текущее выбранное устройство и используйте идентификатор, чтобы получить AudioPlaybackConnection из словаря подключений приложения. Дождитесь вызова OpenAsync и проверка значение состояния возвращенного объекта AudioPlaybackConnectionOpenResultStatus, чтобы узнать, было ли подключение успешно открыто, и, если да, обновите текстовое поле состояния подключения.
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)";
}
}
}
Мониторинг состояния подключения воспроизведения звука
Событие AudioPlaybackConnection.ConnectionStateChanged возникает при каждом изменении состояния подключения. В этом примере обработчик этого события обновляет текстовое поле состояния. Не забудьте обновить пользовательский интерфейс внутри вызова Dispatcher.RunAsync , чтобы убедиться, что обновление выполняется в потоке пользовательского интерфейса.
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";
}
});
}
Освобождение подключений и обработка удаленных устройств
В этом примере предоставляется кнопка "Освободить подключение к воспроизведению звука ", позволяющая пользователю освободить подключение к воспроизведению звука. В обработчике для этого события мы получаем текущее выбранное устройство и используем идентификатор устройства для поиска AudioPlaybackConnection в словаре. Вызовите Метод Dispose , чтобы освободить ссылку, освободить все связанные ресурсы и удалить подключение из словаря.
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;
}
}
}
Следует обработать ситуацию, когда устройство удаляется при включении или открытии подключения. Для этого реализуйте обработчик для события DeviceWatcher.Removed наблюдателя за устройством . Во-первых, идентификатор удаленного устройства используется для удаления устройства из наблюдаемой коллекции, привязанной к элементу управления ListView приложения. Затем, если подключение, связанное с этим устройством, находится в словаре приложения, вызывается Dispose , чтобы освободить связанные ресурсы, а затем подключение удаляется из словаря. Все это выполняется в вызове Dispatcher.RunAsync , чтобы убедиться, что обновления пользовательского интерфейса выполняются в потоке пользовательского интерфейса.
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);
}
});
}
Связанные темы
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по