Přehrávání médií

Tento článek vás naučí, jak přenášet média do vzdálených zařízení z aplikace WinUI.

Vestavěné streamování médií pomocí MediaPlayerElement

Nejjednodušší způsob, jak přenášet média z aplikace WinUI, je použít integrovanou funkci přenášení ovládacího prvku MediaPlayerElement.

V souboru XAML aplikace přidejte MediaPlayerElement a nastavte AreTransportControlsEnabled na true.

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

Přidejte tlačítko, které uživateli umožní zahájit výběr souboru.

<Button x:Name="bOpenButton" Click="bOpenButton_Click" Content="Open"/>

V obslužné rutině události Click pro tlačítko vytvořte novou instanci FileOpenPickeru, přidejte typy videosouborů do kolekce FileTypeFilter a nastavte počáteční umístění do knihovny videí uživatele.

Volání PickSingleFileAsync ke spuštění dialogového okna pro výběr souboru. Když tato metoda vrátí, výsledek je StorageFile objekt představující videosoubor. Zkontrolujte, jestli soubor nemá hodnotu null, což bude v případě, že uživatel zruší operaci výběru. Voláním metody OpenAsync souboru získáte IRandomAccessStream pro soubor. Nakonec vytvořte nový objekt MediaSource ze zvoleného souboru pomocí CreateFromStorageFile a přiřaďte ho k vlastnosti Source objektu MediaPlayerElement, aby se videosoubor stal zdrojem videa pro ovládací prvek.

private async void bOpenButton_Click(object sender, RoutedEventArgs e)
{
    //Create a new picker
    var filePicker = new Microsoft.Windows.Storage.Pickers.FileOpenPicker(this.AppWindow.Id)
    {
        SuggestedStartLocation = PickerLocationId.VideosLibrary,
        FileTypeFilter = { ".wmv", ".mp4", ".mkv" },
    };

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

    if (result is not null)
    {
        var storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(result.Path);
        mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(storageFile);
        mediaPlayerElement.MediaPlayer.Play();
    }
    
}

Jakmile se video načte do MediaPlayerElement, uživatel může jednoduše stisknout tlačítko přetypování na ovládacích prvcích přenosu a spustit integrovaný dialog, který jim umožní zvolit zařízení, na které se načtené médium přetypuje.

tlačítko vysílání mediaelementu

Přehrávání médií pomocí zařízení CastingDevicePicker

Druhým způsobem, jak přenést média do zařízení, je použít CastingDevicePicker. Nejprve deklarujte členovou proměnnou pro objekt Windows.Media.Casting.Casting.CastingDevicePicker .

CastingDevicePicker castingPicker;

Když je vaše okno inicializováno, vytvořte novou instanci výběru zařízení pro přenos obsahu a nastavte vlastnost Filter na SupportsVideo, aby bylo jasné, že zařízení nabízená tímto výběrem by měla podporovat video. Zaregistrujte obslužnou rutinu události CastingDeviceSelected, která se vyvolá, když uživatel vybere zařízení pro vysílá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;

V souboru XAML přidejte tlačítko, které uživateli umožní spustit výběr.

<Button x:Name="bCastPickerButton" Content="Cast Button" Click="bCastPickerButton_Click"/>

V obslužné rutině události Click zavolejte pro tlačítko TransformToVisual, abyste získali transformaci prvku uživatelského rozhraní vzhledem k jinému prvku. V tomto příkladu je transformací pozice tlačítka pro výběr zařízení pro přehrávání vzhledem k vizuálnímu kořeni okna aplikace. Volat metodu Show objektu CastingDevicePicker pro spuštění dialogového okna pro výběr zařízení pro streamování. Zadejte umístění a rozměry tlačítka pro vysílání, aby systém mohl zobrazit dialogové okno z tlačítka stisknutého uživatelem.

private void bCastPickerButton_Click(object sender, RoutedEventArgs e)
{
    //Retrieve the location of the casting button
    GeneralTransform transform = bCastPickerButton.TransformToVisual(this.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, bCastPickerButton.ActualWidth, bCastPickerButton.ActualHeight),
        Windows.UI.Popups.Placement.Above);
}

V obslužné rutině události CastingDeviceSelected zavolejte metodu CreateCastingConnection vlastnosti SelectedCastingDevice argumentu události, která představuje zařízení pro přenos médií vybrané uživatelem. Zaregistrujte obslužné rutiny pro události Došlo k chybě a StateChanged . Nakonec zavolejte RequestStartCastingAsync pro zahájení přenášení, předáním výsledku ovládacímu prvku MediaPlayerElement pomocí metody GetAsCastingSource, abyste určili, že médium, které se má přenášet, je obsah MediaPlayer přidružený k MediaPlayerElement.

Poznámka:

Připojení pro přenos musí být inicializováno v hlavním vlákně uživatelského rozhraní. Vzhledem k tomu, že CastingDeviceSelected není volána ve vlákně uživatelského rozhraní, je nutné umístit tato volání do volání DispatcherQueue.TryEnqueue, což zajistí, že budou volána ve vlákně uživatelského rozhraní.

private void CastingPicker_CastingDeviceSelected(CastingDevicePicker sender, CastingDeviceSelectedEventArgs args)
{
    //Casting must occur from the UI thread.  This dispatches the casting calls to the UI thread.
   DispatcherQueue.TryEnqueue( 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());
    });
}

V obslužných rutinách událostí Došlo k chybě a StateChanged byste měli aktualizovat uživatelské rozhraní, abyste informovali uživatele o aktuálním stavu přenášení obsahu. Tyto události jsou podrobně popsány v následující části o vytvoření vlastního výběru zařízení pro vysílání.

private void Connection_StateChanged(CastingConnection sender, object args)
{
    DispatcherQueue.TryEnqueue( () =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

private void Connection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        ShowMessageToUser("Casting Connection State Changed: " + sender.State);
    });
}

Streamování médií pomocí vlastního vyhledávače zařízení

Následující část popisuje, jak vytvořit vlastní uživatelské rozhraní pro výběr castingových zařízení tím, že provedete výčet těchto zařízení a zahájíte připojení z vašeho kódu.

Na stránku XAML přidejte následující ovládací prvky pro implementaci základního uživatelského rozhraní pro tento příklad:

  • Tlačítko pro spuštění sledování zařízení, které hledá dostupné zařízení pro streamování.
  • Ovládací prvek ProgressRing , který uživateli poskytne zpětnou vazbu, že probíhá přetypování výčtu.
  • ListBox k výpisu zjištěných zařízení přetypování. Definujte ItemTemplate pro ovládací prvek, abychom mohli přiřadit objekty zařízení přímo k ovládacímu prvku a stále zobrazit vlastnost FriendlyName.
  • Tlačítko, které uživateli umožní odpojit zařízení pro streamování.
<Button x:Name="bStartWatcherButton" Content="Watcher Button" Click="bStartWatcherButton_Click"/>
<ProgressRing x:Name="prWatcherProgressRing" IsActive="False"/>
<ListBox x:Name="lbCastingDevicesListBox" MaxWidth="300" HorizontalAlignment="Left" SelectionChanged="lbCastingDevicesListBox_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="bDisconnectButton" Content="Disconnect" Click="bDisconnectButton_Click" Visibility="Collapsed"/>

V zákulisí kódu deklarujte členské proměnné pro DeviceWatcher a CastingConnection.

DeviceWatcher deviceWatcher;
CastingConnection castingConnection;

V obslužné rutině Click pro startWatcherButton nejprve aktualizujte uživatelské rozhraní deaktivací tlačítka a aktivací průběhového kroužku během výčtu zařízení. Vyprázdněte seznam zařízení pro streamování.

Dále vytvořte sledovací proces zařízení voláním DeviceInformation.CreateWatcher. Tuto metodu lze použít ke sledování mnoha různých typů zařízení. Určete, že chcete vyhledávat zařízení, která podporují přenos videa, pomocí řetězce selektoru zařízení vráceného CastingDevice.GetDeviceSelector.

Nakonec zaregistrujte obslužné rutiny událostí pro události Added, Removed, EnumerationCompleted a Stopped .

private void bStartWatcherButton_Click(object sender, RoutedEventArgs e)
{
    bStartWatcherButton.IsEnabled = false;
    prWatcherProgressRing.IsActive = true;

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

Událost Added se vyvolá, když sledovač zjistí nové zařízení. V obslužné rutině pro tuto událost vytvořte nový CastingDevice objekt voláním CastingDevice.FromIdAsync a předáním ID zjištěného zařízení pro přenos, který je obsažen v objektu DeviceInformation předanému obslužné rutině.

Přidejte CastingDevice do seznamu zařízení ListBox, aby ho uživatel mohl vybrat. Díky ItemTemplate definované v XAML se vlastnost FriendlyName použije jako text položky v seznamu. Protože tato obslužná rutina události není volána na vlákně uživatelského rozhraní, je nutné aktualizovat uživatelské rozhraní z volání DispatcherQueue.TryEnqueue.

private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    DispatcherQueue.TryEnqueue(async () =>
    {
        //Add each discovered device to our listbox
        CastingDevice addedDevice = await CastingDevice.FromIdAsync(args.Id);
        lbCastingDevicesListBox.Items.Add(addedDevice);
    });
}

Událost Removed je vyvolána, když monitorovací program zjistí, že zařízení pro přenos již není přítomno. Porovnejte vlastnost ID přidaného objektu předaného do obslužné rutiny s ID každého přidaného v kolekci Položek seznamu. Pokud se ID shoduje, odeberte tento objekt z kolekce. Znovu platí, že uživatelské rozhraní se aktualizuje, a proto je nutné toto volání provést z volání RunAsync .

private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
    DispatcherQueue.TryEnqueue( () =>
    {
        foreach (CastingDevice currentDevice in lbCastingDevicesListBox.Items)
        {
            if (currentDevice.Id == args.Id)
            {
                lbCastingDevicesListBox.Items.Remove(currentDevice);
            }
        }
    });
}

Událost EnumerationCompleted je vyvolána, když sledovač dokončil detekci zařízení. V obslužné rutině pro tuto událost aktualizujte uživatelské rozhraní, aby uživatel věděl, že výčet zařízení se dokončil a zastavil sledovací proces zařízení voláním Stop.

private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        //If enumeration completes, update UI and transition watcher to the stopped state
        ShowMessageToUser("Watcher completed enumeration of devices");
        deviceWatcher.Stop();
    });
}

Zastavená událost se vyvolá, když sledovací proces zařízení dokončí zastavení. V obslužné rutině pro tuto událost zastavte ovládací prvek ProgressRing a znovu spustit startWatcherButton , aby uživatel mohl restartovat proces výčtu zařízení.


private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
   DispatcherQueue.TryEnqueue( () =>
    {
        //Update UX when the watcher stops
        bStartWatcherButton.IsEnabled = true;
        prWatcherProgressRing.IsActive = false;
    });
}

Když uživatel vybere jedno ze zařízení pro vysílání ze seznamového pole, vyvolá se událost SelectionChanged. V rámci této obslužné rutiny se vytvoří připojení pro vysílání a zahájí se vysílání.

Nejprve se ujistěte, že je sledování zařízení zastaveno, aby výčty zařízení nepřerušovaly vysílání médií. Vytvořte připojení pro odesílání voláním CreateCastingConnection na zařízení CastingDevice vybrané uživatelem. Přidejte obslužné rutiny událostí pro události StateChanged a Došlo k chybě .

Spusťte přenášení médií voláním RequestStartCastingAsync a předáním zdroje vysílání vráceného voláním metody MediaPlayerGetAsCastingSource. Nakonec zviditelněte tlačítko pro odpojení, aby uživatel mohl zastavit přenášení médií.

private async void lbCastingDevicesListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (lbCastingDevicesListBox.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)lbCastingDevicesListBox.SelectedItem).CreateCastingConnection();

        //Register for events
        castingConnection.ErrorOccurred += CastingConnection_ErrorOccurred; ;
        castingConnection.StateChanged += CastingConnection_StateChanged; ;

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

V obslužné rutině změny stavu závisí akce, kterou provedete, na novém stavu připojení streamování.

  • Pokud je stav Připojeno nebo Vykreslování, ujistěte se, že je ovládací prvek ProgressRing neaktivní a tlačítko odpojit je viditelné.
  • Pokud je stav Odpojeno, odznačte aktuální zařízení pro přenos v seznamu, deaktivujte kontrolku ProgressRing a skryjte tlačítko pro odpojení.
  • Pokud je stav Připojení, aktivujte ovládací prvek ProgressRing a skryjte tlačítko pro odpojení.
  • Pokud je stav Odpojeno, aktivujte ovládací prvek ProgressRing a skryjte tlačítko pro odpojení.
private void CastingConnection_StateChanged(CastingConnection sender, object args)
{
    DispatcherQueue.TryEnqueue( () =>
    {
        //Update the UX based on the casting state
        if (sender.State == CastingConnectionState.Connected || sender.State == CastingConnectionState.Rendering)
        {
            bDisconnectButton.Visibility = Visibility.Visible;
            prWatcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Disconnected)
        {
            bDisconnectButton.Visibility = Visibility.Collapsed;
            lbCastingDevicesListBox.SelectedItem = null;
            prWatcherProgressRing.IsActive = false;
        }
        else if (sender.State == CastingConnectionState.Connecting)
        {
            bDisconnectButton.Visibility = Visibility.Collapsed;
            ShowMessageToUser("Connecting");
            prWatcherProgressRing.IsActive = true;
        }
        else
        {
            //Disconnecting is the remaining state
            bDisconnectButton.Visibility = Visibility.Collapsed;
            prWatcherProgressRing.IsActive = true;
        }
    });
}

V obslužné rutině události ErrorOccurred aktualizujte uživatelské rozhraní, aby uživatel věděl, že došlo k chybě při odesílání, a zrušte výběr aktuálního objektu CastingDevice v seznamu.

private void CastingConnection_ErrorOccurred(CastingConnection sender, CastingConnectionErrorOccurredEventArgs args)
{
    DispatcherQueue.TryEnqueue( () =>
    {
        //Clear the selection in the listbox on an error
        ShowMessageToUser("Casting Error: " + args.Message);
        lbCastingDevicesListBox.SelectedItem = null;
    });
}

Nakonec implementujte obslužnou rutinu tlačítka pro odpojení. Zastavte přetypování médií a odpojte se od zařízení přetypování voláním metody DisconnectAsyncobjektu CastingConnection. Toto volání musí být odesláno do vlákna uživatelského rozhraní voláním DispatcherQueue.TryEnqueue.

private async void bDisconnectButton_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();
    }
}