Compartir a través de


Habilitar la reproducción de audio desde dispositivos conectados por Bluetooth remotos

En este artículo se muestra cómo usar AudioPlaybackConnection para habilitar dispositivos remotos conectados a Bluetooth para reproducir audio en la máquina local.

A partir de Windows 10, versión 2004, los orígenes de audio remotos pueden transmitir audio a dispositivos Windows, lo que permite escenarios como configurar un equipo para que se comporte como un altavoz Bluetooth y permitir que los usuarios escuchen audio desde su teléfono. La implementación usa los componentes bluetooth del sistema operativo para procesar los datos de audio entrantes y reproducirlos en los puntos de conexión de audio del sistema en el sistema, como altavoces de PC integrados o auriculares cableados. La habilitación del receptor Bluetooth A2DP subyacente se administra mediante aplicaciones, que son responsables del escenario del usuario final, en lugar de por el sistema.

La clase AudioPlaybackConnection se usa para habilitar y deshabilitar conexiones desde un dispositivo remoto, así como para crear la conexión, lo que permite que comience la reproducción remota de audio.

Agregar una interfaz de usuario

Para ver los ejemplos de este artículo, usaremos la siguiente interfaz de usuario XAML sencilla que define el control ListView para mostrar los dispositivos remotos disponibles, un TextBlock para mostrar el estado de conexión y tres botones para habilitar, deshabilitar y abrir conexiones.

<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>

Uso de DeviceWatcher para supervisar dispositivos remotos

La clase DeviceWatcher permite detectar dispositivos conectados. El método AudioPlaybackConnection.GetDeviceSelector devuelve una cadena que indica al monitor de dispositivo qué tipos de dispositivos se van a inspeccionar. Pase esta cadena al constructor DeviceWatcher .

El evento DeviceWatcher.Added se genera para cada dispositivo que está conectado cuando se inicia el monitor de dispositivo, así como para cualquier dispositivo que esté conectado mientras se ejecuta el monitor de dispositivo. El evento DeviceWatcher.Removed se genera si un dispositivo conectado previamente se desconecta.

Llame a DeviceWatcher.Start para empezar a ver los dispositivos conectados que admiten conexiones de reproducción de audio. En este ejemplo, iniciaremos el administrador de dispositivos cuando se cargue el control Grid principal de la interfaz de usuario. Para obtener más información sobre el uso de 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();
}

En el evento Agregado del monitor de dispositivo, cada dispositivo detectado se representa mediante un objeto DeviceInformation. Agregue cada dispositivo detectado a una colección observable enlazada al control ListView en la interfaz de usuario.

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);
    });
}

Habilitación y liberación de conexiones de reproducción de audio

Antes de abrir una conexión con un dispositivo, la conexión debe estar habilitada. Esto informa al sistema de que hay una nueva aplicación que quiere que el audio del dispositivo remoto se reproduzca en el equipo, pero el audio no comienza a reproducirse hasta que se abra la conexión, que se muestra en un paso posterior.

En el controlador de clics del botón Habilitar conexión de reproducción de audio, obtenga el identificador de dispositivo asociado al dispositivo seleccionado actualmente en el control ListView . En este ejemplo se mantiene un diccionario de objetos AudioPlaybackConnection que se han habilitado. Este método comprueba primero si ya hay una entrada en el diccionario del dispositivo seleccionado. A continuación, el método intenta crear un audioPlaybackConnection para el dispositivo seleccionado llamando a TryCreateFromId y pasando el identificador de dispositivo seleccionado.

Si la conexión se crea correctamente, agregue el nuevo objeto AudioPlaybackConnection al diccionario de la aplicación, registre un controlador para el evento StateChanged del objeto y llame aStartAsync para notificar al sistema que la nueva conexión 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;
            }
        }
    }
}

Apertura de la conexión de reproducción de audio

En el paso anterior, se creó una conexión de reproducción de audio, pero el sonido no comienza a reproducirse hasta que se abre la conexión llamando a Open o OpenAsync. En el botón Abrir conexión de reproducción de audio, haga clic en el controlador, obtenga el dispositivo seleccionado actualmente y use el identificador para recuperar AudioPlaybackConnection del diccionario de conexiones de la aplicación. Espere una llamada a OpenAsync y compruebe el valor Status del objeto AudioPlaybackConnectionOpenResultStatus devuelto para ver si la conexión se abrió correctamente y, si es así, actualice el cuadro de texto Estado de conexión.

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)";
        }
    }
}

Supervisión del estado de conexión de reproducción de audio

El evento AudioPlaybackConnection.ConnectionStateChanged se genera cada vez que cambia el estado de la conexión. En este ejemplo, el controlador de este evento actualiza el cuadro de texto de estado. Recuerde actualizar la interfaz de usuario dentro de una llamada a Dispatcher.RunAsync para asegurarse de que la actualización se realiza en el subproceso de la interfaz de usuario.

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";
        }
    });
}

Liberar conexiones y controlar dispositivos eliminados

En este ejemplo se proporciona un botón Liberar conexión de reproducción de audio para permitir al usuario liberar una conexión de reproducción de audio. En el controlador de este evento, obtenemos el dispositivo seleccionado actualmente y usamos el identificador del dispositivo para buscar audioPlaybackConnection en el diccionario. Llame a Dispose para liberar la referencia y liberar los recursos asociados y quitar la conexión del diccionario.


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;
        }
    }
}

Debe controlar el caso en el que se quita un dispositivo mientras una conexión está habilitada o abierta. Para ello, implemente un controlador para el evento DeviceWatcher.Removed del monitor de dispositivo. En primer lugar, el identificador del dispositivo quitado se usa para quitar el dispositivo de la colección observable enlazada al control ListView de la aplicación. A continuación, si una conexión asociada a este dispositivo está en el diccionario de la aplicación, se llama a Dispose para liberar los recursos asociados y, a continuación, se quita la conexión del diccionario. Todo esto se realiza dentro de una llamada a Dispatcher.RunAsync para asegurarse de que las actualizaciones de la interfaz de usuario se realizan en el subproceso de la interfaz de usuario.

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);
        }
    });
}

Reproducción multimedia