Abilitare la riproduzione audio da dispositivi remoti connessi mediante Bluetooth

Questo articolo illustra come usare AudioPlaybackConnection per abilitare i dispositivi remoti connessi bluetooth per riprodurre l'audio nel computer locale.

A partire da Windows 10, versione 2004 le origini audio remote possono trasmettere audio ai dispositivi Windows, consentendo scenari come la configurazione di un PC per comportarsi come un altoparlante Bluetooth e consentire agli utenti di ascoltare audio dal telefono. L'implementazione usa i componenti Bluetooth nel sistema operativo per elaborare i dati audio in ingresso e riprodurli negli endpoint audio del sistema nel sistema, ad esempio altoparlanti PC incorporati o cuffie cablate. L'abilitazione del sink A2DP Bluetooth sottostante viene gestita dalle app, responsabili dello scenario dell'utente finale, anziché dal sistema.

La classe AudioPlaybackConnection viene usata per abilitare e disabilitare le connessioni da un dispositivo remoto e per creare la connessione, consentendo la riproduzione audio remota per iniziare.

Aggiungere un'interfaccia utente

Per gli esempi in questo articolo, useremo la semplice interfaccia utente XAML seguente che definisce il controllo ListView per visualizzare i dispositivi remoti disponibili, un controllo TextBlock per visualizzare lo stato della connessione e tre pulsanti per abilitare, disabilitare e aprire le connessioni.

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

Usare DeviceWatcher per monitorare i dispositivi remoti

La classe DeviceWatcher consente di rilevare i dispositivi connessi. AudioPlaybackConnection. Il metodo GetDeviceSelector restituisce una stringa che indica al dispositivo watcher i tipi di dispositivi da controllare. Passare questa stringa al costruttore DeviceWatcher.

L'evento DeviceWatcher.Added viene generato per ogni dispositivo connesso all'avvio del watcher del dispositivo e per qualsiasi dispositivo connesso durante l'esecuzione del watcher del dispositivo. L'evento DeviceWatcher.Removed viene generato se un dispositivo connesso in precedenza si disconnette.

Chiamare DeviceWatcher.Start per iniziare a guardare i dispositivi connessi che supportano le connessioni di riproduzione audio. In questo esempio si avvia la gestione dispositivi quando viene caricato il controllo Griglia principale nell'interfaccia utente. Per ulteriori informazioni sull'uso di DeviceWatcher, vedere Enumerare i dispositivi.

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

Nel dispositivo Added di Device Watcher ogni dispositivo individuato è rappresentato da un oggetto DeviceInformation. Aggiungere ogni dispositivo individuato a una raccolta osservabile associata al controllo ListView nell'interfaccia utente.

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

Abilitare e rilasciare connessioni di riproduzione audio

Prima di aprire una connessione con un dispositivo, la connessione deve essere abilitata. In questo modo si informa il sistema che è presente una nuova applicazione che vuole riprodurre l'audio dal dispositivo remoto sul PC, ma l'audio non inizia la riproduzione finché non viene aperta la connessione, che viene visualizzata in un passaggio successivo.

Nel gestore clic per il pulsante Abilita riproduzione audio Connection ottenere l'ID dispositivo associato al dispositivo attualmente selezionato nel controllo ListView. In questo esempio viene mantenuto un dizionario di oggetti AudioPlayback Connection abilitati. Questo metodo verifica innanzitutto se è già presente una voce nel dizionario per il dispositivo selezionato. Il metodo tenta quindi di creare unoggetto AudioPlaybackConnection per il dispositivo selezionato chiamando TryCreateFromId e passando l'ID dispositivo selezionato.

Se la connessione viene creata correttamente, aggiungere il nuovo oggetto AudioPlaybackConnection al dizionario dell'app, registrare un gestore per l'evento StateChanged dell'oggetto e chiamare StartAsync per notificare al sistema che la nuova connessione è abilitata.

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

Aprire la connessione di riproduzione audio

Nel passaggio precedente è stata creata una connessione di riproduzione audio, ma il suono non inizia fino all'apertura della connessione chiamando Open o OpenAsync. Nel pulsante Apri riproduzione audio Connection fare clic sul gestore, ottenere il dispositivo attualmente selezionato e usare l'ID per recuperare AudioPlaybackConnection dal dizionario delle connessioni dell'app. Attendere una chiamata a OpenAsync e controllare il valore Status dell'oggetto AudioPlayback Connessione ionOpenResultStatus restituito per verificare se la connessione è stata aperta correttamente e, in tal caso, aggiornare la casella di testo dello stato della connessione.

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

Monitorare lo stato della connessione di riproduzione audio

L'evento AudioPlaybackConnection.ConnectionStateChanged viene generato ogni volta che lo stato della connessione cambia. In questo esempio, il gestore per questo evento aggiorna la casella di testo relativa allo stato. Ricordarsi di aggiornare l'interfaccia utente all'interno di una chiamata a Dispatcher.RunAsync per assicurarsi che l'aggiornamento venga eseguito nel thread dell'interfaccia utente.

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

Rilasciare le connessioni e gestire i dispositivi rimossi

Questo esempio fornisce un pulsante Rilascia riproduzione audio Connection per consentire all'utente di rilasciare una connessione di riproduzione audio. Nel gestore per questo evento si ottiene il dispositivo attualmente selezionato e si usa l'ID del dispositivo per cercare AudioPlaybackConnection nel dizionario. Chiamare Dispose per rilasciare il riferimento e liberare le risorse associate e rimuovere la connessione dal dizionario.


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

È consigliabile gestire il caso in cui un dispositivo venga rimosso mentre una connessione è abilitata o aperta. A tale scopo, implementare un gestore per l'evento DeviceWatcher.Removed del watcher del dispositivo. Prima di tutto, l'ID del dispositivo rimosso viene usato per rimuovere il dispositivo dalla raccolta osservabile associata al controllo ListView dell'app. Successivamente, se una connessione associata a questo dispositivo si trova nel dizionario dell'app, Dispose viene chiamata per liberare le risorse associate e quindi la connessione viene rimossa dal dizionario. Tutto questo avviene all'interno di una chiamata a Dispatcher.RunAsync per assicurarsi che gli aggiornamenti dell'interfaccia utente vengano eseguiti nel thread dell'interfaccia utente.

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

Riproduzione di contenuti multimediali