媒體傳播

本文介紹如何從通用 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。 呼叫檔案的 OpenAsync 方法以取得該檔案的 IRandomAccessStream。 最後,透過呼叫 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 控制項,用於在 XAML 頁面中轉譯 MediaPlayer 的內容。 MediaElement 控制項會繼續支援回溯相容性。 有關使用 MediaPlayerMediaPlayerElement 播放媒體內容的詳細資訊,請參閱使用 MediaPlayer 播放音訊和視訊。 有關使用 MediaSource 和相關 API 處理媒體內容的資訊,請參閱媒體項目、播放清單與曲目

使用 CastingDevicePicker 進行媒體轉換

將媒體轉換到裝置的第二種方法是使用 CastingDevicePicker。 若要使用此類別,請在專案中包含 Windows.Media.Casting 命名空間。

using Windows.Media.Casting;

宣告 CastingDevicePicker 物件的成員變數。

CastingDevicePicker castingPicker;

初始化頁面後,建立轉換選擇器的新執行個體,並將 Filter 設定為 SupportsVideo 屬性,以指示選擇器列出的轉換裝置應支援影片。 為 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方法,該屬性表示使用者選擇的轉換裝置。 註冊 ErrorOccurredStateChanged 事件的處理常式。 最後,呼叫 RequestStartCastingAsync 開始轉換,將結果傳遞給 MediaPlayerElement 控制項的 MediaPlayer 物件的 GetAsCastingSource 方法,以指定要轉換的媒體是與 MediaPlayerElement 相關的 MediaPlayer 的內容。

注意

轉換連線必須在 UI 執行緒上啟動。 由於不會在 UI 執行緒上呼叫 CastingDeviceSelected,因此您必須將這些呼叫放在對 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());
    });
}

ErrorOccurredStateChanged 事件處理常式中,您應該更新 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;

將下列控制項新增至 XAML 頁面,以實作此範例的初級 UI:

  • 用於啟動裝置監看員以尋找可用轉換裝置的按鈕。
  • ProgressRing 控制項,用於向使用者提供轉換列舉正在進行的反饋。
  • 用於列出發現的轉換裝置的 ListBox。 為控制項定義一個 ItemTemplate,以便我們可以將轉換裝置物件直接指派給控制項並且仍然顯示 FriendlyName 屬性。
  • 允許使用者中斷轉換裝置連線的按鈕。
<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 並傳入已發現的轉換裝置的 ID (包含在傳遞到處理程序的 DeviceInformation 物件中) 來建立一個新的 CastingDevice 物件。

CastingDevice 新增到轉換裝置 ListBox 中,以便使用者可以選擇它。 由於在 XAML 中定義了 ItemTemplateFriendlyName 屬性將用做清單方塊中的項目文字。 由於不在 UI 執行緒上呼叫此事件處理常式,因此您必須在對 CoreDispatcher.RunAsync 的呼叫中更新 UI。

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 來建立轉換連線。 為 StateChangedErrorOccurred 事件新增事件處理常式。

透過呼叫 RequestStartCastingAsync 開始媒體轉換,傳入透過呼叫 MediaPlayer 方法 GetAsCastingSource 傳回的轉換來源。 最後,讓使用者停止媒體轉換,讓中斷連線按鈕可見。

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

在狀態變更處理常式中,您採取的動作取決於轉換連線的新狀態:

  • 如果狀態為 ConnectedRendering,請確保 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();
    }
}