다음을 통해 공유


원격 Bluetooth 연결 디바이스에서 오디오 재생 사용

이 문서에서는 AudioPlaybackConnection 을 사용하여 Bluetooth 연결 원격 디바이스가 로컬 컴퓨터에서 오디오를 재생할 수 있도록 하는 방법을 보여 줍니다.

오디오 원본은 오디오를 Windows 디바이스로 스트리밍할 수 있으므로 PC가 Bluetooth 스피커처럼 작동하도록 구성하고 사용자가 휴대폰에서 오디오를 들을 수 있도록 하는 등의 시나리오를 사용할 수 있습니다. 구현은 OS의 Bluetooth 구성 요소를 사용하여 들어오는 오디오 데이터를 처리하고 기본 제공 PC 스피커 또는 유선 헤드폰과 같은 시스템의 오디오 엔드포인트에서 재생합니다. 기본 Bluetooth A2DP 싱크의 사용은 시스템이 아닌 최종 사용자 시나리오를 담당하는 앱에서 관리됩니다.

AudioPlaybackConnection 클래스는 원격 디바이스에서 연결을 사용하거나 사용하지 않도록 설정하고 연결을 만드는 데 사용되므로 원격 오디오 재생을 시작할 수 있습니다.

사용자 인터페이스 추가

이 문서의 예제에서는 사용 가능한 원격 디바이스를 표시하도록 ListView 컨트롤을 정의하는 다음과 같은 간단한 XAML UI, 연결 상태를 표시하는 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 메서드는 디바이스 감시자에게 어떤 종류의 디바이스를 감시해야 하는지 알려주는 문자열을 반환합니다. DeviceWatcher 생성자에 이 문자열을 전달합니다.

DeviceWatcher.Added 이벤트는 디바이스 감시자가 시작될 때 연결된 각 디바이스와 디바이스 감시자가 실행되는 동안 연결된 모든 디바이스에 대해 발생합니다. DeviceWatcher.Removed 이벤트는 이전에 연결된 디바이스의 연결이 끊어지면 발생합니다.

DeviceWatcher.Start를 호출하여 오디오 재생 연결을 지원하는 연결된 디바이스에 대한 감시를 시작합니다. 이 예제에서는 UI의 기본 그리드 컨트롤이 로드될 때 디바이스 관리자를 시작합니다. DeviceWatcher 사용에 대한 자세한 내용은 디바이스 열거를 참조하세요.

private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
    audioPlaybackConnections = new Dictionary<string, AudioPlaybackConnection>();

    // Start watching for paired Bluetooth devices. 
    deviceWatcher = DeviceInformation.CreateWatcher(AudioPlaybackConnection.GetDeviceSelector());

    // Register event handlers before starting the watcher. 
    deviceWatcher.Added += DeviceWatcher_Added;
    deviceWatcher.Removed += DeviceWatcher_Removed;

    deviceWatcher.Start();
}

디바이스 감시자의 추가 된 이벤트에서 검색된 각 디바이스는 DeviceInformation 개체로 표시됩니다. 검색된 각 디바이스를 UI의 ListView 컨트롤에 바인딩된 관찰 가능한 컬렉션에 추가합니다.

private ObservableCollection<Windows.Devices.Enumeration.DeviceInformation> devices =
    new ObservableCollection<Windows.Devices.Enumeration.DeviceInformation>();
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    // Collections bound to the UI are updated in the UI thread. 
    DispatcherQueue.TryEnqueue( () =>
    {
        this.devices.Add(args);
    });
}

오디오 재생 연결 사용 및 해제

디바이스와 연결을 시작하기 전에 연결을 활성화해야 합니다. 이렇게 하면 원격 디바이스의 오디오를 PC에서 재생하려는 새 애플리케이션이 있지만 연결이 열릴 때까지 오디오 재생이 시작되지 않으며 이후 단계에서 표시됩니다.

오디오 재생 연결 사용 단추의 클릭 처리기에서 ListView 컨트롤에서 현재 선택한 디바이스와 연결된 디바이스 ID를 가져옵니다. 이 예제에서는 사용하도록 설정된 AudioPlaybackConnection 개체의 사전을 유지 관리합니다. 이 메서드는 먼저 선택한 디바이스에 대한 사전에 이미 항목이 있는지 확인합니다. 다음으로, 메서드는 TryCreateFromId를 호출하고 선택한 디바이스 ID를 전달하여 선택한 디바이스에 대한 AudioPlaybackConnection을 만들려고 시도합니다.

연결이 성공적으로 만들어지면 앱의 사전에 새 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 (!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 += PlaybackConnection_StateChanged; ;
                audioPlaybackConnections.Add(selectedDeviceId, playbackConnection);
                await playbackConnection.StartAsync();
                OpenAudioPlaybackConnectionButtonButton.IsEnabled = true;
            }
        }
    }
}

오디오 재생 연결 열기

이전 단계에서 오디오 재생 연결이 만들어졌지만 Open 또는 OpenAsync를 호출하여 연결이 열릴 때까지 소리가 재생되지 않습니다. 오디오 재생 연결 열기 단추 클릭 처리기에서 현재 선택한 디바이스를 가져오고 ID를 사용하여 앱의 연결 사전에서 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 이벤트는 연결 상태가 변경될 때마다 발생합니다. 이 예제에서 이 이벤트의 처리기는 상태 텍스트 상자를 업데이트합니다. UI 스레드에서 업데이트가 이루어지도록 DispatcherQueue.TryEnqueue 호출 내에서 UI를 업데이트해야 합니다.

private void PlaybackConnection_StateChanged(AudioPlaybackConnection sender, object args)
{
    DispatcherQueue.TryEnqueue( () =>
    {
        if (sender.State == AudioPlaybackConnectionState.Closed)
        {
            ConnectionState.Text = "Disconnected";
        }
        else if (sender.State == AudioPlaybackConnectionState.Opened)
        {
            ConnectionState.Text = "Connected";
        }
        else
        {
            ConnectionState.Text = "Unknown";
        }
    });
}

연결 해제 및 제거된 디바이스 처리

이 예제에서는 사용자가 오디오 재생 연결을 해제 할 수 있도록 오디오 재생 연결 해제 단추를 제공합니다. 이 이벤트의 처리기에서 현재 선택한 디바이스를 가져오고 디바이스의 ID를 사용하여 사전에서 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 이벤트에 대한 처리기를 구현합니다. 먼저 제거된 디바이스의 ID를 사용하여 앱의 ListView 컨트롤에 바인딩된 관찰 가능한 컬렉션에서 디바이스를 제거합니다. 다음으로, 이 디바이스와 연결된 연결이 앱의 사전에 있는 경우 Dispose 가 호출되어 연결된 리소스를 해제한 다음 사전에서 연결이 제거됩니다. 이 모든 작업은 UI 스레드에서 UI 업데이트가 수행되도록 DispatcherQueue.TryEnqueue 호출 내에서 수행됩니다.

private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
    // Collections bound to the UI are updated in the UI thread. 
    DispatcherQueue.TryEnqueue( () =>
    {
        // Find the device for the given id and remove it from the list. 
        foreach (DeviceInformation device in this.devices)
        {
            if (device.Id == args.Id)
            {
                devices.Remove(device);
                break;
            }
        }

        if (audioPlaybackConnections.ContainsKey(args.Id))
        {
            AudioPlaybackConnection connectionToRemove = audioPlaybackConnections[args.Id];
            connectionToRemove.Dispose();
            audioPlaybackConnections.Remove(args.Id);
        }
    });
}

미디어 재생