メディアのキャスト

この記事では、ユニバーサル Windows アプリからリモート デバイスにメディアをキャストする方法について説明します。

MediaPlayerElement を使った組み込みのメディア キャスト機能

ユニバーサル Windows アプリからメディアをキャストする方法としては、MediaPlayerElement コントロールの組み込みのキャスト機能を使うのが最も簡単です。

MediaPlayerElement コントロールで再生するビデオ ファイルをユーザーが開くことができるようにするには、以下の名前空間をプロジェクトに追加します。

using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Media.Core;

アプリの XAML ファイルに MediaPlayerElement を追加し、AreTransportControlsEnabled を true に設定します。

<MediaPlayerElement Name="mediaPlayerElement"  MinHeight="100" MaxWidth="600" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>

ユーザーがファイルを選択するときに使うボタンを追加します。

<Button x:Name="openButton" Click="openButton_Click" Content="Open"/>

このボタンの Click イベント ハンドラーで FileOpenPicker の新しいインスタンスを作成し、ビデオ ファイルの種類を FileTypeFilter コレクションに追加して、開始位置をユーザーのビデオ ライブラリに設定します。

PickSingleFileAsync を呼び出して、ファイル ピッカー ダイアログを起動します。 このメソッドからは、ビデオ ファイルを表す StorageFile オブジェクトが返されます。 ファイル選択操作をユーザーが取り消した場合は null が返されます。返されたファイルが null ではないことを確認してください。 ファイルの IRandomAccessStream を取得するには、そのファイルの OpenAsync メソッドを呼び出します。 最後に、CreateFromStorageFile を呼び出して選択したファイルから新しい MediaSource オブジェクトを作成し、そのオブジェクトを MediaPlayerElement オブジェクトの Source プロパティに割り当てて、そのビデオ ファイルをコントロールのビデオ ソースに設定します。

private async void openButton_Click(object sender, RoutedEventArgs e)
{
    //Create a new picker
    FileOpenPicker filePicker = new FileOpenPicker();

    //Add filetype filters.  In this case wmv and mp4.
    filePicker.FileTypeFilter.Add(".wmv");
    filePicker.FileTypeFilter.Add(".mp4");

    //Set picker start location to the video library
    filePicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;

    //Retrieve file from picker
    StorageFile file = await filePicker.PickSingleFileAsync();

    //If we got a file, load it into the media lement
    if (file != null)
    {
        mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(file);
        mediaPlayerElement.MediaPlayer.Play();
    }
}

MediaPlayerElement にビデオを読み込んだら、ユーザーがトランスポート コントロールのキャスト ボタンを押すだけで、組み込みのダイアログを起動し、読み込まれているメディアのキャスト先となるデバイスを選択できます。

mediaelement casting button

注意

Windows 10 バージョン 1607 以降、メディア項目の再生には、MediaPlayer クラスを使うことをお勧めします。 MediaPlayerElement は、XAML ページの MediaPlayer コンテンツをレンダリングするために使われる軽量の XAML コントロールです。 下位互換性を確保するため、MediaElementコントロールも引き続きサポートされています。 MediaPlayerMediaPlayerElement を使ってメディア コンテンツを再生する方法について詳しくは、「MediaPlayer を使ったオーディオとビデオの再生」をご覧ください。 MediaSource と関連の API を使ってメディア コンテンツを操作する方法について詳しくは、「メディア項目、プレイリスト、トラック」をご覧ください。

CastingDevicePicker を使ったメディアのキャスト

メディアをデバイスにキャストするもう 1 つの方法は、CastingDevicePicker を使うことです。 このクラスを使うには、プロジェクトに Windows.Media.Casting 名前空間を追加します。

using Windows.Media.Casting;

CastingDevicePicker オブジェクトのメンバー変数を宣言します。

CastingDevicePicker castingPicker;

対象のページが初期化されたタイミングで CastingDevicePicker の新しいインスタンスを作成します。さらに、ビデオをサポートするデバイスだけをキャスト先としてピッカーに一覧表示するために、FilterSupportsVideo プロパティに設定します。 CastingDeviceSelected イベントのハンドラーを登録します。これは、ユーザーがキャスト先のデバイスを選んだときに発生するイベントです。

//Initialize our picker object
castingPicker = new CastingDevicePicker();

//Set the picker to filter to video capable casting devices
castingPicker.Filter.SupportsVideo = true;

//Hook up device selected event
castingPicker.CastingDeviceSelected += CastingPicker_CastingDeviceSelected;

ユーザーがピッカーを起動するためのボタンを XAML ファイルに追加します。

<Button x:Name="castPickerButton" Content="Cast Button" Click="castPickerButton_Click"/>

ボタンの Click イベント ハンドラーで TransformToVisual を呼び出し、別の要素を基準とした UI 要素の変換を取得します。 この例の変換は、アプリケーション ウィンドウの表示ルートを基準としたキャスト ピッカー ボタンの位置です。 CastingDevicePicker オブジェクトの Show メソッドを呼び出して、キャスト先の選択ダイアログを起動します。 ユーザーによって押されたボタンから飛び出すようにダイアログを表示させるため、キャスト ピッカー ボタンの位置とサイズを指定します。

private void castPickerButton_Click(object sender, RoutedEventArgs e)
{
    //Retrieve the location of the casting button
    GeneralTransform transform = castPickerButton.TransformToVisual(Window.Current.Content as UIElement);
    Point pt = transform.TransformPoint(new Point(0, 0));

    //Show the picker above our casting button
    castingPicker.Show(new Rect(pt.X, pt.Y, castPickerButton.ActualWidth, castPickerButton.ActualHeight),
        Windows.UI.Popups.Placement.Above);
}

CastingDeviceSelected イベント ハンドラーで、イベント引数の SelectedCastingDevice プロパティ (ユーザーが選択したキャスト先デバイスを表す) の CreateCastingConnection メソッドを呼び出します。 ErrorOccurred イベントと StateChanged イベントのハンドラーを登録します。 最後に、RequestStartCastingAsync を呼び出すとキャストが開始されます。キャストするメディアが MediaPlayerElement に関連付けられた MediaPlayer のコンテンツであることを指定するために、このメソッドの引数には、MediaPlayerElement コントロールの MediaPlayer オブジェクトの GetAsCastingSource メソッドの結果を渡します。

注意

キャスト接続は UI スレッドで開始する必要があります。 CastingDeviceSelected は UI スレッドでは呼び出されないため、上に挙げた一連の呼び出しを、CoreDispatcher.RunAsync の呼び出しの中に置いて、UI スレッドで呼び出されるようにする必要があります。

private async void CastingPicker_CastingDeviceSelected(CastingDevicePicker sender, CastingDeviceSelectedEventArgs args)
{
    //Casting must occur from the UI thread.  This dispatches the casting calls to the UI thread.
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
    {
        //Create a casting conneciton from our selected casting device
        CastingConnection connection = args.SelectedCastingDevice.CreateCastingConnection();

        //Hook up the casting events
        connection.ErrorOccurred += Connection_ErrorOccurred;
        connection.StateChanged += Connection_StateChanged;

        //Cast the content loaded in the media element to the selected casting device
        await connection.RequestStartCastingAsync(mediaPlayerElement.MediaPlayer.GetAsCastingSource());
    });
}

ErrorOccurred イベントと StateChanged イベントのハンドラーでは、UI を更新して、現在のキャスト ステータスをユーザーに通知する必要があります。 これらのイベントについては、次のセクションで、カスタムのキャスト先デバイス ピッカーを作成する方法の中で詳しく説明します。

private async void Connection_StateChanged(CastingConnection sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

private async void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

カスタム デバイス ピッカーを使ったメディアのキャスト

次のセクションでは、独自のコードでキャスト先デバイスを列挙し、接続を開始することによって、キャスト先デバイス ピッカーの UI を作成する方法を説明します。

利用可能なキャスト先デバイスを列挙するには、Windows.Devices.Enumeration 名前空間をプロジェクトに追加します。

using Windows.Devices.Enumeration;

この例の基本的な UI を実装するために、以下のコントロールを XAML ページに追加します。

  • 利用可能なキャスト先デバイスを探すデバイス ウォッチャーの起動ボタン。
  • キャスト先を列挙する処理の進行状況をユーザーにフィードバックする ProgressRing コントロール。
  • 検出されたキャスト先デバイスを一覧表示する ListBox。 キャスト先デバイス オブジェクトを直接コントロールに割り当てたうえで、さらに FriendlyName プロパティを表示できるようにコントロールの ItemTemplate を定義します。
  • キャスト先デバイスとの接続をユーザーが切断するためのボタン。
<Button x:Name="startWatcherButton" Content="Watcher Button" Click="startWatcherButton_Click"/>
<ProgressRing x:Name="watcherProgressRing" IsActive="False"/>
<ListBox x:Name="castingDevicesListBox" MaxWidth="300" HorizontalAlignment="Left" SelectionChanged="castingDevicesListBox_SelectionChanged">
    <!--Listbox content is bound to the FriendlyName field of our casting devices-->
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=FriendlyName}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
<Button x:Name="disconnectButton" Content="Disconnect" Click="disconnectButton_Click" Visibility="Collapsed"/>

分離コードには、DeviceWatcherCastingConnection のメンバー変数を宣言します。

DeviceWatcher deviceWatcher;
CastingConnection castingConnection;

startWatcherButtonClick ハンドラーでまず行うのは UI の更新です。デバイスの列挙中はボタンを無効にし、プログレス リングをアクティブにします。 キャスト先デバイスのリスト ボックスをクリアします。

次に、DeviceInformation.CreateWatcher を呼び出してデバイス ウォッチャーを作成します。 このメソッドを使うと、さまざまな種類のデバイスを監視することができます。 ビデオ キャストに対応しているデバイスを監視対象として指定するには、CastingDevice.GetDeviceSelector から返されるデバイスのセレクター文字列を使います。

最後に、AddedRemovedEnumerationCompletedStopped の各イベントのハンドラーを登録します。

private void startWatcherButton_Click(object sender, RoutedEventArgs e)
{
    startWatcherButton.IsEnabled = false;
    watcherProgressRing.IsActive = true;

    castingDevicesListBox.Items.Clear();

    //Create our watcher and have it find casting devices capable of video casting
    deviceWatcher = DeviceInformation.CreateWatcher(CastingDevice.GetDeviceSelector(CastingPlaybackTypes.Video));

    //Register for watcher events
    deviceWatcher.Added += DeviceWatcher_Added;
    deviceWatcher.Removed += DeviceWatcher_Removed;
    deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
    deviceWatcher.Stopped += DeviceWatcher_Stopped;

    //Start the watcher
    deviceWatcher.Start();
}

Added は、ウォッチャーで新しいデバイスが検出されたときに発生するイベントです。 このイベントのハンドラーで CastingDevice.FromIdAsync を呼び出して新しい CastingDevice オブジェクトを作成します。このメソッドの引数には、検出されたキャスト先デバイスの ID を指定します。ID は、ハンドラーに渡された DeviceInformation オブジェクトに格納されています。

この CastingDevice をキャスト先デバイスの ListBox に追加して、ユーザーが選択できるようにします。 リスト ボックスの項目テキストには、XAML で定義した ItemTemplate により、FriendlyName プロパティが使われます。 このイベント ハンドラーは UI スレッドで呼び出されないため、UI の更新は、CoreDispatcher.RunAsync の呼び出しの中で行う必要があります。

private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
    {
        //Add each discovered device to our listbox
        CastingDevice addedDevice = await CastingDevice.FromIdAsync(args.Id);
        castingDevicesListBox.Items.Add(addedDevice);
    });
}

キャスト先デバイスがもう存在しないことをウォッチャーが検出すると、Removed イベントが発生します。 ハンドラーに渡された Added オブジェクトの ID プロパティと、リスト ボックスの Items コレクションに含まれている各 Added の ID とを比較します。 ID が一致する場合は、そのオブジェクトをコレクションから削除します。 UI が更新中であるため、先ほどと同様、この呼び出しは RunAsync の呼び出しの中で行う必要があります。

private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        foreach (CastingDevice currentDevice in castingDevicesListBox.Items)
        {
            if (currentDevice.Id == args.Id)
            {
                castingDevicesListBox.Items.Remove(currentDevice);
            }
        }
    });
}

ウォッチャーがデバイスの検出を完了すると、EnumerationCompleted イベントが発生します。 このイベントのハンドラーで UI を更新して、デバイスの列挙処理が完了したことをユーザーに知らせ、Stop を呼び出してデバイス ウォッチャーを停止します。

private async void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //If enumeration completes, update UI and transition watcher to the stopped state
        ShowMessageToUser("Watcher completed enumeration of devices");
        deviceWatcher.Stop();
    });
}

デバイス ウォッチャーの停止処理が完了すると Stopped イベントが発生します。 このイベントのハンドラーで ProgressRing コントロールを停止したうえで、デバイス列挙処理をユーザーが再開できるように startWatcherButton を再び有効にします。

private async void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Update UX when the watcher stops
        startWatcherButton.IsEnabled = true;
        watcherProgressRing.IsActive = false;
    });
}

リスト ボックスからいずれかのキャスト先デバイスをユーザーが選択すると、SelectionChanged イベントが発生します。 このハンドラーの中でキャスト接続を作成し、キャストを開始することになります。

まず、デバイスの列挙処理がメディアのキャスト処理に干渉しないよう、デバイス ウォッチャーが停止していることを確認します。 キャスト接続を作成するには、ユーザーが選択した CastingDevice オブジェクトの CreateCastingConnection を呼び出します。 StateChanged イベントおよび ErrorOccurred イベントに対応するイベント ハンドラーを追加します。

メディアのキャストを開始するには、RequestStartCastingAsync を呼び出します。その際、引数には、MediaPlayerGetAsCastingSource メソッドを呼び出して取得したキャスト ソースを指定します。 最後に、メディアのキャストをユーザーが停止するための切断ボタンを表示状態にします。

private async void castingDevicesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (castingDevicesListBox.SelectedItem != null)
    {
        //When a device is selected, first thing we do is stop the watcher so it's search doesn't conflict with streaming
        if (deviceWatcher.Status != DeviceWatcherStatus.Stopped)
        {
            deviceWatcher.Stop();
        }

        //Create a new casting connection to the device that's been selected
        castingConnection = ((CastingDevice)castingDevicesListBox.SelectedItem).CreateCastingConnection();

        //Register for events
        castingConnection.ErrorOccurred += Connection_ErrorOccurred;
        castingConnection.StateChanged += Connection_StateChanged;

        //Cast the loaded video to the selected casting device.
        await castingConnection.RequestStartCastingAsync(mediaPlayerElement.MediaPlayer.GetAsCastingSource());
        disconnectButton.Visibility = Visibility.Visible;
    }
}

StateChanged ハンドラーで実行する操作は、キャスト接続の新しい状態によって異なります。

  • 状態が Connected または Rendering である場合は、ProgressRing コントロールを非アクティブにし、切断ボタンを表示します。
  • 状態が Disconnected である場合は、現在のキャスト先デバイスの選択をリスト ボックスで解除し、ProgressRing コントロールを非アクティブにして、切断ボタンを非表示にします。
  • 状態が Connecting である場合は、ProgressRing コントロールをアクティブにして、切断ボタンを非表示にします。
  • 状態が Disconnecting である場合は、ProgressRing コントロールをアクティブにして、切断ボタンを非表示にします。
private async void Connection_StateChanged(CastingConnection sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Update the UX based on the casting state
        if (sender.State == CastingConnectionState.Connected || sender.State == CastingConnectionState.Rendering)
        {
            disconnectButton.Visibility = Visibility.Visible;
            watcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Disconnected)
        {
            disconnectButton.Visibility = Visibility.Collapsed;
            castingDevicesListBox.SelectedItem = null;
            watcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Connecting)
        {
            disconnectButton.Visibility = Visibility.Collapsed;
            ShowMessageToUser("Connecting");
            watcherProgressRing.IsActive = true;
        }
        else
        {
            //Disconnecting is the remaining state
            disconnectButton.Visibility = Visibility.Collapsed;
            watcherProgressRing.IsActive = true;
        }
    });
}

ErrorOccurred イベントのハンドラーでは、UI を更新してキャスト エラーの発生をユーザーに知らせると共に、リスト ボックスで現在の CastingDevice オブジェクトの選択を解除します。

private async void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        //Clear the selection in the listbox on an error
        ShowMessageToUser("Casting Error: " + args.Message);
        castingDevicesListBox.SelectedItem = null;
    });
}

最後に、切断ボタンのハンドラーを実装します。 メディアのキャストを停止してキャスト先デバイスから切断するために、CastingConnection オブジェクトの DisconnectAsync メソッドを呼び出します。 この呼び出しは、CoreDispatcher.RunAsync を呼び出して UI スレッドにディスパッチする必要があります。

private async void disconnectButton_Click(object sender, RoutedEventArgs e)
{
    if (castingConnection != null)
    {
        //When disconnect is clicked, the casting conneciton is disconnected.  The video should return locally to the media element.
        await castingConnection.DisconnectAsync();
    }
}