Activer la lecture audio à partir d’appareils connectés en Bluetooth à distance

Cet article vous montre comment utiliser AudioPlaybackConnection pour permettre aux appareils distants connectés à Bluetooth de lire de l’audio sur l’ordinateur local.

À compter de Windows 10, les sources audio distantes de la version 2004 peuvent diffuser de l’audio sur des appareils Windows, ce qui permet de configurer un PC pour qu’il se comporte comme un haut-parleur Bluetooth et de permettre aux utilisateurs d’entendre de l’audio à partir de leur téléphone. L’implémentation utilise les composants Bluetooth du système d’exploitation pour traiter les données audio entrantes et les lire sur les points de terminaison audio du système sur le système, tels que les haut-parleurs de PC intégrés ou les écouteurs câblés. L’activation du récepteur Bluetooth A2DP sous-jacent est gérée par les applications, qui sont responsables du scénario de l’utilisateur final, plutôt que par le système.

La classe AudioPlaybackConnection est utilisée pour activer et désactiver les connexions à partir d’un appareil distant, ainsi que pour créer la connexion, ce qui permet à la lecture audio à distance de commencer.

Ajouter une interface utilisateur

Pour les exemples de cet article, nous allons utiliser l’interface utilisateur XAML simple suivante qui définit le contrôle ListView pour afficher les appareils distants disponibles, un TextBlock pour afficher les status de connexion et trois boutons pour activer, désactiver et ouvrir des connexions.

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

Utiliser DeviceWatcher pour surveiller les appareils distants

La classe DeviceWatcher vous permet de détecter les appareils connectés. La méthode AudioPlaybackConnection.GetDeviceSelector retourne une chaîne qui indique à l’observateur d’appareil les types d’appareils pour 20 watch. Passez cette chaîne dans le constructeur DeviceWatcher .

L’événement DeviceWatcher.Added est déclenché pour chaque appareil connecté au démarrage de l’observateur d’appareil, ainsi que pour tout appareil connecté pendant l’exécution de l’observateur d’appareil. L’événement DeviceWatcher.Removed est déclenché si un appareil précédemment connecté se déconnecte.

Appelez DeviceWatcher.Start pour commencer à surveiller les appareils connectés qui prennent en charge les connexions de lecture audio. Dans cet exemple, nous allons démarrer le gestionnaire de périphériques lorsque le contrôle main Grid dans l’interface utilisateur est chargé. Pour plus d’informations sur l’utilisation de DeviceWatcher, consultez Énumérer des appareils.

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

Dans l’événement Added de l’observateur d’appareil, chaque appareil découvert est représenté par un objet DeviceInformation . Ajoutez chaque appareil découvert à une collection observable liée au contrôle ListView dans l’interface utilisateur.

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

Activer et libérer les connexions de lecture audio

Avant d’ouvrir une connexion avec un appareil, la connexion doit être activée. Cela informe le système qu’il existe une nouvelle application qui souhaite que l’audio de l’appareil distant soit lu sur le PC, mais que l’audio ne commence pas à lire tant que la connexion n’est pas ouverte, ce qui est illustré dans une étape ultérieure.

Dans le gestionnaire de clic du bouton Activer la connexion de lecture audio , obtenez l’ID d’appareil associé à l’appareil actuellement sélectionné dans le contrôle ListView . Cet exemple montre comment gérer un dictionnaire d’objets AudioPlaybackConnection qui ont été activés. Cette méthode vérifie d’abord s’il existe déjà une entrée dans le dictionnaire pour l’appareil sélectionné. Ensuite, la méthode tente de créer un AudioPlaybackConnection pour l’appareil sélectionné en appelant TryCreateFromId et en transmettant l’ID de l’appareil sélectionné.

Si la connexion est correctement créée, ajoutez le nouvel objet AudioPlaybackConnection au dictionnaire de l’application, inscrivez un gestionnaire pour l’événement StateChanged de l’objet et appelezStartAsync pour informer le système que la nouvelle connexion est activée.

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

Ouvrir la connexion de lecture audio

À l’étape précédente, une connexion de lecture audio a été créée, mais le son ne commence pas à être lu tant que la connexion n’est pas ouverte en appelant Open ou OpenAsync. Dans le bouton Ouvrir une connexion de lecture audio , cliquez sur gestionnaire, récupérez l’appareil actuellement sélectionné et utilisez l’ID pour récupérer audioPlaybackConnection à partir du dictionnaire de connexions de l’application. Attendez un appel à OpenAsync et case activée la valeur Status de l’objet AudioPlaybackConnectionOpenResultStatus retourné pour voir si la connexion a été ouverte correctement et, le cas échéant, mettre à jour la zone de texte État de la connexion.

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

Surveiller l’état de connexion de lecture audio

L’événement AudioPlaybackConnection.ConnectionStateChanged est déclenché chaque fois que l’état de la connexion change. Dans cet exemple, le gestionnaire de cet événement met à jour la zone de texte status. N’oubliez pas de mettre à jour l’interface utilisateur dans un appel à Dispatcher.RunAsync pour vous assurer que la mise à jour est effectuée sur le thread d’interface utilisateur.

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

Libérer les connexions et gérer les appareils supprimés

Cet exemple fournit un bouton Libérer la connexion de lecture audio pour permettre à l’utilisateur de libérer une connexion de lecture audio. Dans le gestionnaire de cet événement, nous obtenons l’appareil actuellement sélectionné et nous utilisons l’ID de l’appareil pour rechercher audioPlaybackConnection dans le dictionnaire. Appelez Disposer pour libérer la référence et libérer toutes les ressources associées et supprimer la connexion du dictionnaire.


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

Vous devez gérer le cas où un appareil est supprimé lorsqu’une connexion est activée ou ouverte. Pour ce faire, implémentez un gestionnaire pour l’événement DeviceWatcher.Removed de l’observateur d’appareil. Tout d’abord, l’ID de l’appareil supprimé est utilisé pour supprimer l’appareil de la collection observable liée au contrôle ListView de l’application. Ensuite, si une connexion associée à cet appareil se trouve dans le dictionnaire de l’application, Dispose est appelé pour libérer les ressources associées, puis la connexion est supprimée du dictionnaire. Tout cela est effectué dans un appel à Dispatcher.RunAsync pour vérifier que les mises à jour de l’interface utilisateur sont effectuées sur le thread d’interface utilisateur.

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

Lecture multimédia