Трансляция мультимедиа

В этой статье показано, как транслировать мультимедиа в удаленные устройства из универсального приложения для 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, представляющий видеофайл. Проверьте, что файл не пустой (это произойдет, если пользователь отменит операцию выбора). Вызовите метод OpenAsync этого файла, чтобы получить IRandomAccessStream файла. Создайте новый объект MediaSource из выбранного файла, вызвав метод CreateFromStorageFile, и назначьте его свойству Источник объекта MediaPlayerElement, чтобы сделать видеофайл источником видео для элемента управления.

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

Примечание

Начиная с Windows 10 версии 1607 рекомендуется использовать класс MediaPlayer, чтобы воспроизводить элементы мультимедиа. MediaPlayerElement — это облегченный элемент управления XAML, который используется для отображения содержимого MediaPlayer на странице XAML. Элемент управления MediaElement продолжает поддерживаться для обратной совместимости. Дополнительные сведения об использовании объектов MediaPlayer и MediaPlayerElement для воспроизведения мультимедиа см. в разделе Воспроизведение аудио и видео с помощью 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, чтобы получить преобразование элемента пользовательского интерфейса по отношению к другому элементу. В данном примере преобразованием является положение кнопки средства выбора трансляции относительно визуального корня окна приложения. Вызовите метод Show объекта CastingDevicePicker, чтобы открыть диалоговое окно средства выбора трансляции. Укажите расположение и размеры кнопки средства выбора трансляции, чтобы система могла создать всплывающее диалоговое окно, когда пользователь нажмет на эту кнопку.

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 вызовите метод CreateCastingConnection свойства SelectedCastingDevice аргументов события, которое обозначает выбранное пользователем транслирующее устройство. Зарегистрируйте обработчики для событий ErrorOccurred и StateChanged. Вызовите метод RequestStartCastingAsync, чтобы начать трансляцию, передавая результат методу GetAsCastingSource объекта MediaPlayer элемента управления MediaPlayerElement, чтобы указать, что транслируемое мультимедиа — это содержимое объекта MediaPlayer, связанного с объектом MediaPlayerElement.

Примечание

Подключение к трансляции должно быть инициировано в потоке пользовательского интерфейса. Так как поток пользовательского интерфейса не вызывает CastingDeviceSelected, вам необходимо разместить эти вызовы внутри вызова CoreDispatcher.RunAsync, который вызовет их в потоке пользовательского интерфейса.

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. Более подробно эти события рассматриваются в следующем разделе по созданию настраиваемого средства выбора транслирующего устройства.

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

Трансляция мультимедиа с помощью настраиваемого средства выбора устройства

В следующем разделе описывается, как создать собственный пользовательский интерфейс для средства выбора транслирующего устройства с помощью перечисления транслирующих устройств и инициации подключения из своего кода.

Для перечисления доступных транслирующих устройств включите в свой проект пространство имен Windows.Devices.Enumeration.

using Windows.Devices.Enumeration;

Чтобы реализовать элементарный пользовательский интерфейс для данного примера, добавьте на свою страницу XAML следующие элементы управления:

  • Кнопку для запуска наблюдателя устройств, который осуществляет поиск доступных транслирующих устройств.
  • Элемент управления 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"/>

В своем коде программной части объявите переменные-члены для DeviceWatcher и CastingConnection.

DeviceWatcher deviceWatcher;
CastingConnection castingConnection;

Сначала обновите пользовательский интерфейс в обработчике Click для startWatcherButton, отключив кнопку и активировав кольцевой индикатор выполнения во время перечисления устройств. Очистите список транслирующих устройств.

Затем создайте наблюдатель устройств, вызвав DeviceInformation.CreateWatcher. Этот метод можно использовать для отслеживания различных типов устройств. Укажите с помощью строки селектора устройств, которую возвращает CastingDevice.GetDeviceSelector, что необходимо отслеживать устройства, поддерживающие трансляцию видео.

И, наконец, зарегистрируйте обработчики событий для Added, Removed, EnumerationCompleted и Stopped.

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, вызвав метод CastingDevice.FromIdAsync и передав идентификатор обнаруженного транслирующего устройства, который содержится в переданном обработчику объекте DeviceInformation.

Добавьте CastingDevice в транслирующее устройство ListBox, чтобы пользователь мог его выбрать. Из-за шаблона ItemTemplate, определенного в XAML, свойство FriendlyName будет использоваться в качестве текста элемента в списке. Поскольку этот обработчик событий не вызывается из потока пользовательского интерфейса, необходимо обновить пользовательский интерфейс внутри вызова 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 с идентификатором каждого элемента Added в коллекции Items списка. Если идентификатор совпадает, удалите этот объект из коллекции. И вновь, так как пользовательский интерфейс был обновлен, этот вызов должен выполняться из вызова 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 возникает, когда наблюдатель завершает поиск устройств. В обработчике для этого события обновите пользовательский интерфейс, чтобы пользователь знал, что процесс перечисления устройств завершен, и остановите наблюдателя устройств, вызвав 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. В рамках именно этого обработчика и будет создано подключение к трансляции, а трансляция — запущена.

Для начала убедитесь, что наблюдатель устройств остановлен, чтобы перечисление устройств не помешало трансляции мультимедиа. Создайте подключение к трансляции, вызвав CreateCastingConnection в выбранном пользователем объекте CastingDevice. Добавьте обработчиков событий для StateChanged и ErrorOccurred.

Запустите трансляцию мультимедиа, вызвав RequestStartCastingAsync, передав источник трансляции, который возвратился при вызове метода GetAsCastingSource объекта MediaPlayer. Наконец, сделайте видимой кнопку отключения, чтобы позволить пользователю остановить трансляцию мультимедиа.

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

В состоянии, измененном обработчиком, выполняемое вами действие зависит от нового состояния подключения к трансляции.

  • Если состояние равно 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 обновите свой пользовательский интерфейс, чтобы пользователь знал о возникновении ошибки при трансляции, и отмените выбор текущего объекта 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.

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