Transmitir contenido multimedia

En este artículo te mostramos cómo transmitir contenido multimedia a dispositivos remotos desde una aplicación universal de Windows.

Transmisión de contenido multimedia integrada con MediaPlayerElement

La forma más sencilla de transmitir contenido multimedia desde una aplicación universal de Windows es usar la característica de transmisión integrada del control MediaPlayerElement.

Para permitir que el usuario abra un archivo de vídeo para reproducirlo en el control MediaPlayerElement, agrega los siguientes espacios de nombres al proyecto.

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

En el archivo XAML de la aplicación, agrega una clase MediaPlayerElement y establece AreTransportControlsEnabled como true.

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

Agrega un botón para permitir al usuario que inicie seleccionando un archivo.

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

En el controlador de eventos Click para el botón, crea una nueva instancia de la FileOpenPicker, agrega tipos de archivo de vídeo a la colección FileTypeFilter y establece la ubicación inicial para la biblioteca de vídeos del usuario.

Llama a PickSingleFileAsync para iniciar el cuadro de diálogo del selector de archivos. Cuando se devuelve este método, el resultado es un objeto StorageFile que representa el archivo de vídeo. Comprueba que el archivo no es null, que sí lo será si el usuario cancela la operación de selección. Llama al método OpenAsync del archivo para obtener una interfaz IRandomAccessStream para el archivo. Por último, crea un nuevo objeto MediaSource a partir del archivo seleccionado mediante una llamada a CreateFromStorageFile y asígnalo a la propiedad Source del objeto MediaPlayerElement para que el archivo de vídeo sea el origen de vídeo para el control.

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

Una vez cargado el vídeo en MediaPlayerElement, el usuario simplemente puede presionar el botón de transmisión en los controles de transporte para iniciar un cuadro de diálogo integrado que le permite elegir un dispositivo al que se transmitirá el contenido multimedia cargado.

botón de transmisión de mediaelement

Nota:

A partir de Windows 10, versión 1607, se recomienda usar la clase MediaPlayer para reproducir elementos multimedia. MediaPlayerElement es un control XAML ligero que se usa para representar el contenido de un objeto MediaPlayer en una página XAML. El control MediaElement se sigue admitiendo para la compatibilidad con versiones anteriores. Para obtener más información sobre el uso de MediaPlayer y MediaPlayerElement para reproducir contenido multimedia, consulta Reproducir audio y vídeo con MediaPlayer. Para obtener información sobre el uso de MediaSource y las API relacionadas para trabajar con contenido multimedia, consulta Elementos multimedia, listas de reproducción y pistas.

Transmisión de contenido multimedia con el CastingDevicePicker

Una segunda forma de transmitir contenido multimedia a un dispositivo es usar la clase CastingDevicePicker. Para usar esta clase, se incluye el espacio de nombres de Windows.Media.Casting en el proyecto.

using Windows.Media.Casting;

Declarar una variable de miembro para el objeto CastingDevicePicker.

CastingDevicePicker castingPicker;

Cuando tu página se inicia, crea una nueva instancia del selector de conversión y establece el Filter a la propiedad SupportsVideo para indicar que los dispositivos de conversión enumerados por el selector deben admitir el vídeo. Registrar un controlador para el evento CastingDeviceSelected, que se genera cuando el usuario elige un dispositivo de conversión.

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

Agrega un botón en el archivo XAML para permitir al usuario iniciar el selector.

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

En el controlador de eventos Click para el botón, llama a TransformToVisual para obtener la transformación de un elemento de interfaz de usuario con respecto a otro elemento. En este ejemplo, la transformación es la posición del botón Selector en relación a la raíz visual de la ventana de la aplicación. Llama al método Show del objeto CastingDevicePicker para iniciar el cuadro de diálogo del selector de conversión. Especifica la ubicación y las dimensiones del botón Selector de conversión de tipos para que el sistema pueda hacer que el cuadro de diálogo salga del botón que el usuario ha presionado.

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

En el controlador de eventos CastingDeviceSelected llama al método CreateCastingConnection de la propiedad de los argumentos del evento SelectedCastingDevice, que representa el dispositivo de conversión seleccionado por el usuario. Registrar controladores para los eventos ErrorOccurred y StateChanged. Por último, llama a RequestStartCastingAsync para comenzar la transmisión al pasar el resultado al método GetAsCastingSource del objeto MediaPlayer del control MediaPlayerElement para especificar que el contenido multimedia que se va a transmitir es el contenido del objeto MediaPlayer asociado al control MediaPlayerElement.

Nota

La conexión de la transmisión se debe iniciar en el subproceso de interfaz de usuario. Dado que no se llama a CastingDeviceSelected desde el subproceso de interfaz de usuario, debes realizar estas llamadas dentro de una llamada a CoreDispatcher.RunAsync, lo que hace que se les llame en el subproceso de interfaz de usuario.

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

En los controladores de eventos ErrorOccurred y StateChanged, debes actualizar la interfaz de usuario para informar al usuario del estado actual de conversión. Estos eventos se explican a detalle en la siguiente sección sobre la creación de un selector de dispositivos de conversión personalizadas.

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

Conversión de multimedia con un selector de dispositivos personalizados

La siguiente sección describe cómo crear tu propio selector de dispositivos de conversión interfaz de usuario enumerando los dispositivos de conversión e iniciando la conexión desde el código.

Para enumerar los dispositivos de conversión disponibles, incluye el espacio de nombres Windows.Devices.Enumeration en el proyecto.

using Windows.Devices.Enumeration;

Agrega los siguientes controles a la página XAML para implementar la interfaz de usuario rudimentaria para este ejemplo:

  • Un botón para iniciar el observador de dispositivo que busca dispositivos de conversión disponibles.
  • Un control ProgressRing para proporcionar información al usuario de que la enumeración de conversión está en curso.
  • Una clase ListBox para enumerar los dispositivos de conversión detectados. Definir una ItemTemplate para el control, de modo que podamos asignar los objetos de dispositivo de conversión directamente al control y seguir mostrando la propiedad FriendlyName .
  • Un botón para permitir que el usuario desconecte el dispositivo de conversión.
<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"/>

En tu código subyacente, declara variables de miembro para la DeviceWatcher y la CastingConnection.

DeviceWatcher deviceWatcher;
CastingConnection castingConnection;

En el controlador Click para el startWatcherButton, primero se actualiza la interfaz de usuario deshabilitando el botón y activando el círculo de progreso mientras la enumeración de dispositivos esté en curso. Desactiva la casilla de la lista de dispositivos de conversión.

A continuación, crea un observador de dispositivo mediante una llamada a DeviceInformation.CreateWatcher. Este método se puede usar para inspeccionar los diferentes tipos de dispositivos. Especifica lo que quieras observar en los dispositivos que admiten la conversión de vídeo mediante el uso de la cadena de selector de dispositivo devuelta por CastingDevice.GetDeviceSelector.

Por último, registra controladores de eventos para los eventos Added, Removed, EnumerationCompleted y 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();
}

El evento Added se genera cuando el observador detecta un nuevo dispositivo. En el controlador para este evento, crea un nuevo objeto CastingDevice llamando a CastingDevice.FromIdAsync y pasando el identificador del dispositivo de conversión detectado, que está incluido en el objeto DeviceInformation pasado al controlador.

Agregar el CastingDevice al dispositivo de conversión ListBox para que el usuario pueda seleccionarlo. Debido a la propiedad ItemTemplate definida en el XAML, la propiedad FriendlyName se usará como el texto del elemento en el cuadro de lista. Dado que no se llama a este controlador de eventos en el subproceso de interfaz de usuario, debes actualizar la interfaz de usuario desde una llamada a 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);
    });
}

El evento Removed se genera cuando el observador detecta que un dispositivo de conversión ya no está presente. Compara la propiedad del identificador de objeto Added pasado al controlador para el identificador de cada Added en la colección Items del cuadro de lista. Si coincide con el identificador, quita ese objeto de la colección. De nuevo, debido a que la interfaz de usuario se está actualizando, esta llamada debe realizarse desde dentro de una llamada de 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);
            }
        }
    });
}

El evento EnumerationCompleted se genera cuando el observador finaliza la detección de dispositivos. En el controlador para este evento, actualiza la interfaz de usuario para permitir al usuario saber que se ha completado la enumeración de dispositivos y detener el observador de dispositivo mediante una llamada a 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();
    });
}

Cuando el observador de dispositivo acaba de detenerse, se genera el evento Stopped. En el controlador para este evento, detén el control ProgressRing y vuelve a habilitar el startWatcherButton para que el usuario pueda reiniciar el proceso de enumeración de dispositivos.

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

Cuando el usuario selecciona uno de los dispositivos de conversión en el cuadro de lista, se genera el evento SelectionChanged. Es dentro de este controlador que se creará la conexión de conversión y se iniciará la conversión.

Primero, asegúrate de que el observador de dispositivo se detenga para que la enumeración de dispositivos no interfiera con la conversión de multimedia. Crea una conexión de conversión llamando a CreateCastingConnection en el objeto CastingDevice seleccionado por el usuario. Añade los controladores de eventos para los eventos StateChanged y ErrorOccurred .

Inicia la transmisión de contenido multimedia con una llamada a RequestStartCastingAsync, al pasar el origen de transmisión devuelto mediante una llamada al método GetAsCastingSource de MediaPlayer. Por último, haz que el botón de desconexión esté visible para permitir al usuario detener la transmisión multimedia.

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

En el controlador de cambio de estado, la acción que realices depende el nuevo estado de la conexión de conversión:

  • Si el estado es Connected o Rendering, asegúrate de que el control ProgressRing esté inactivo y de que el botón desconectar esté visible.
  • Si el estado es Disconnected, desactiva el dispositivo de conversión actual en el cuadro de lista, desactiva el control ProgressRing y oculta el botón de desconexión.
  • Si el estado es Connecting, activa el control de ProgressRing y oculta el botón desconectar.
  • Si el estado es Disconnecting, activa el control de ProgressRing y oculta el botón desconectar.
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;
        }
    });
}

En el controlador para el evento ErrorOccurred, actualiza la interfaz de usuario para hacer saber al usuario que se ha producido un error de conversión y que anule la selección del objeto CastingDevice actual en el cuadro de lista.

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

Por último, implementa el controlador para el botón Desconectar. Detén la conversión de tipos de medios y desconecta el dispositivo de conversión mediante una llamada al método DisconnectAsync del objeto CastingConnection. Esta llamada se debe enviar al subproceso de interfaz de usuario mediante una llamada a 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();
    }
}