Partager via


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

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

À compter de Windows 10, la version 2004 des sources audio distantes peut diffuser du contenu audio sur des appareils Windows, ce qui permet aux scénarios tels que la configuration d’un PC pour se comporter comme un haut-parleur Bluetooth et 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 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 distante de commencer.

Ajouter une interface utilisateur

Pour obtenir 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 l’état de la 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’appareils quels types d’appareils surveiller. Transmettez cette chaîne au constructeur DeviceWatcher .

L’événement DeviceWatcher.Added est déclenché pour chaque appareil connecté lorsque l’observateur d’appareil est démarré, 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 à regarder les appareils connectés qui prennent en charge les connexions de lecture audio. Dans cet exemple, nous allons démarrer le gestionnaire d’appareils lorsque le contrôle Grid principal dans l’interface utilisateur est chargé. Pour plus d’informations sur l’utilisation de DeviceWatcher, consultez Énumérer les 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 des 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 clics pour le 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 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 passant l’ID d’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 avertir 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 à lire tant que la connexion n’est pas ouverte en appelant Open ou OpenAsync. Dans le gestionnaire de clic du bouton Ouvrir la connexion de lecture audio, 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 vérifiez la valeur Status de l’objet AudioPlaybackConnectionOpenResultStatus retourné pour voir si la connexion a été ouverte correctement et, le cas échéant, mettez à jour la zone de texte de l’état de 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 la 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 d’état. N’oubliez pas de mettre à jour l’interface utilisateur à l’intérieur d’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 utilisons l’ID de l’appareil pour rechercher AudioPlaybackConnection dans le dictionnaire. Appelez Dispose 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é pendant qu’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ée 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 vous assurer 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