Udostępnij przez


Wyświetlanie podglądu aparatu w aplikacji WinUI 3

W tym szybkim przewodniku dowiesz się, jak utworzyć podstawową aplikację kamery WinUI 3 wyświetlającą podgląd kamery. W aplikacji WinUI 3 użyjesz kontrolki MediaPlayerElement w przestrzeni nazw Microsoft.UI.Xaml.Controls w celu renderowania podglądu aparatu i klasy WinRT MediaCapture w celu uzyskania dostępu do strumienia podglądu aparatu urządzenia. Aplikacja MediaCapture udostępnia interfejsy API do wykonywania szerokiej gamy zadań związanych z aparatem, takich jak przechwytywanie zdjęć i wideo oraz konfigurowanie sterownika urządzenia aparatu. Zobacz inne artykuły w tej sekcji, aby uzyskać szczegółowe informacje na temat innych funkcji MediaCapture .

Kod w tym przewodniku został dostosowany z przykładu MediaCapture WinUI 3 na GitHubie.

Wskazówka

Aby zapoznać się z wersją artykułu dla platformy UWP, zobacz Wyświetlanie podglądu aparatu w dokumentacji platformy UWP.

Wymagania wstępne

  • Urządzenie musi mieć włączony tryb dewelopera. Aby uzyskać więcej informacji, zobacz Ustawienia dla deweloperów.
  • Program Visual Studio 2022 lub nowszy z obciążeniem tworzenia aplikacji WinUI .

Tworzenie nowej aplikacji WinUI 3

W programie Visual Studio utwórz nowy projekt. W oknie dialogowym Tworzenie nowego projektu ustaw filtr języka na "C#" i filtr platformy na "Windows", a następnie wybierz szablon projektu "Pusta aplikacja, spakowana (WinUI 3 w komputerze stacjonarnym)."

Tworzenie interfejsu użytkownika

Prosty interfejs użytkownika dla tego przykładu obejmuje kontrolkę MediaPlayerElement do wyświetlania podglądu aparatu, ComboBox umożliwiający wybranie kamery z urządzenia oraz przyciski do inicjowania klasy MediaCapture, uruchamiania i zatrzymywania podglądu aparatu oraz resetowania przykładu. Uwzględniamy również element TextBlock do wyświetlania komunikatów o stanie.

W pliku MainWindow.xml projektu zastąp domyślną kontrolkę StackPanel następującym kodem XAML.

<Grid ColumnDefinitions="4*,*" ColumnSpacing="4">
    <MediaPlayerElement x:Name="mpePreview" Grid.Row="0" Grid.Column="0"  AreTransportControlsEnabled="False" ManipulationMode="None"/>
    <StackPanel Orientation="Vertical"  Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch"  VerticalAlignment="Top">
        <TextBlock Text="Status:" Margin="0,0,10,0"/>
        <TextBlock x:Name="tbStatus" Text=""/>
        <TextBlock Text="Preview Source:" Margin="0,0,10,0"/>
        <ComboBox x:Name="cbDeviceList" HorizontalAlignment="Stretch" SelectionChanged="cbDeviceList_SelectionChanged"></ComboBox>
        <Button x:Name="bStartMediaCapture" Content="Initialize MediaCapture" IsEnabled="False" Click="bStartMediaCapture_Click"/>
        <Button x:Name="bStartPreview" Content="Start preview" IsEnabled="False" Click="bStartPreview_Click"/>
        <Button x:Name="bStopPreview" Content="Stop preview" IsEnabled="False" Click="bStopPreview_Click"/>
        <Button x:Name="bReset" Content="Reset" Click="bReset_Click" />
    </StackPanel>
</Grid>

Aktualizowanie definicji klasy MainWindow

Pozostała część kodu w tym artykule zostanie dodana do definicji klasy MainWindow w pliku MainWindow.xaml.cs projektu. Najpierw dodaj kilka zmiennych klasy, które będą utrwalane przez cały okres istnienia okna. Te zmienne obejmują:

  • DeviceInformationCollection, który będzie przechowywać obiekt DeviceInformation dla każdego dostępnego aparatu. Obiekt DeviceInformation przekazuje informacje, takie jak unikatowy identyfikator i przyjazna nazwa aparatu.
  • Obiekt MediaCapture , który obsługuje interakcje ze sterownikiem wybranego aparatu i umożliwia pobranie strumienia wideo aparatu.
  • Obiekt MediaFrameSource reprezentujący źródło ramek multimedialnych, takich jak strumień wideo.
  • Wartość logiczna do śledzenia, kiedy podgląd aparatu jest włączony. Niektórych ustawień aparatu nie można zmienić podczas uruchamiania podglądu, dlatego dobrym rozwiązaniem jest śledzenie stanu podglądu aparatu.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Wypełnij listę dostępnych kamer

Następnie utworzymy metodę pomocnika do wykrywania kamer znajdujących się na bieżącym urządzeniu i wypełnienia pola ComboBox w interfejsie użytkownika nazwami aparatów, umożliwiając użytkownikowi wybranie aparatu do podglądu. Funkcja DeviceInformation.FindAllAsync umożliwia wykonywanie zapytań dotyczących wielu różnych rodzajów urządzeń. Używamy elementu MediaDevice.GetVideoCaptureSelector , aby pobrać identyfikator określający, że chcemy pobrać tylko urządzenia do przechwytywania wideo.

private async void PopulateCameraList()
{
    cbDeviceList.Items.Clear();

    m_deviceList = await DeviceInformation.FindAllAsync(MediaDevice.GetVideoCaptureSelector());

    if(m_deviceList.Count == 0)
    {
        tbStatus.Text = "No video capture devices found.";
        return;
    } 

    foreach (var device in m_deviceList)
    {
        cbDeviceList.Items.Add(device.Name);
        bStartMediaCapture.IsEnabled = true;
    }
}

Dodaj wywołanie tej metody pomocniczej do konstruktora klasy MainWindow , aby pole ComboBox było wypełniane po załadowaniu okna.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Inicjowanie obiektu MediaCapture

Zainicjuj obiekt MediaCapture , wywołując metodę InitializeAsync, przekazując obiekt MediaCaptureInitializationSettings zawierający żądane parametry inicjowania. Istnieje wiele opcjonalnych parametrów inicjowania, które umożliwiają różne scenariusze. Aby uzyskać pełną listę, zobacz stronę dokumentacji interfejsu API. W tym prostym przykładzie określimy kilka podstawowych ustawień, w tym:

  • Właściwość VideoDeviceId określa unikatowy identyfikator aparatu, do którego zostanie dołączony element MediaCapture . Uzyskujemy identyfikator urządzenia z DeviceInformationCollection, używając wybranego indeksu ComboBox.
  • Właściwość SharingMode określa, czy aplikacja żąda udostępnionego, tylko do odczytu dostępu do aparatu, co umożliwia wyświetlanie i przechwytywanie ze strumienia wideo, czy wyłączną kontrolę nad aparatem, co umożliwia zmianę konfiguracji aparatu. Wiele aplikacji może jednocześnie odczytywać z aparatu, ale tylko jedna aplikacja jednocześnie może mieć wyłączną kontrolę.
  • Właściwość StreamingCaptureMode określa, czy chcemy przechwytywać wideo, audio, czy audio i wideo.
  • MediaCaptureMemoryPreference umożliwia nam zażądanie użycia pamięci CPU na potrzeby klatek wideo. Wartość Auto umożliwia systemowi użycie pamięci procesora GPU, jeśli jest dostępna.

Przed zainicjowaniem obiektu MediaCapture wywołujemy metodę AppCapability.CheckAccess , aby określić, czy użytkownik odmówił dostępu aplikacji do aparatu w ustawieniach systemu Windows.

Uwaga / Notatka

System Windows umożliwia użytkownikom udzielanie lub odmawianie dostępu do aparatu urządzenia w ustawieniach systemu Windows w obszarze Prywatność i zabezpieczenia —> aparat fotograficzny. Podczas inicjowania urządzenia do przechwytywania aplikacje powinny sprawdzać, czy mają dostęp do aparatu, i obsługiwać przypadek odmowy dostępu przez użytkownika. Aby uzyskać więcej informacji, zobacz Handle the Windows camera privacy setting (Obsługa ustawienia prywatności aparatu systemu Windows).

Wywołanie InitializeAsync jest wykonywane z wewnątrz bloku try , abyśmy mogli odzyskać, jeśli inicjowanie zakończy się niepowodzeniem. Aplikacje powinny płynnie obsługiwać niepowodzenie inicjowania. W tym prostym przykładzie po prostu wyświetlimy komunikat o błędzie po niepowodzeniu.

private async void bStartMediaCapture_Click(object sender, RoutedEventArgs e)
{
    if (m_mediaCapture != null)
    {
        tbStatus.Text = "MediaCapture already initialized.";
        return;
    }

    // Supported in Windows Build 18362 and later
    if(AppCapability.Create("Webcam").CheckAccess() != AppCapabilityAccessStatus.Allowed)
    {
        tbStatus.Text = "Camera access denied. Launching settings.";

        bool result = await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-webcam"));

        if (AppCapability.Create("Webcam").CheckAccess() != AppCapabilityAccessStatus.Allowed)
        {
            tbStatus.Text = "Camera access denied in privacy settings.";
            return;
        }
    }

    try
    {  
        m_mediaCapture = new MediaCapture();
        var mediaCaptureInitializationSettings = new MediaCaptureInitializationSettings()
        {
            VideoDeviceId = m_deviceList[cbDeviceList.SelectedIndex].Id,
            SharingMode = MediaCaptureSharingMode.ExclusiveControl,
            StreamingCaptureMode = StreamingCaptureMode.Video,
            MemoryPreference = MediaCaptureMemoryPreference.Auto
        };

        await m_mediaCapture.InitializeAsync(mediaCaptureInitializationSettings);

        tbStatus.Text = "MediaCapture initialized successfully.";

        bStartPreview.IsEnabled = true;
    }
    catch (Exception ex)
    {
        tbStatus.Text = "Initialize media capture failed: " + ex.Message;
    }
}

Inicjowanie podglądu kamery

Gdy użytkownik kliknie przycisk Rozpocznij podgląd, podejmiemy próbę utworzenia elementu MediaFrameSource dla strumienia wideo z urządzenia aparatu, za pomocą którego zainicjowano obiekt MediaCapture . Dostępne źródła ramek są udostępniane przez właściwość MediaCapture.FrameSources .

Aby znaleźć źródło ramki będące kolorowymi danymi wideo, w odróżnieniu od kamery głębi, szukamy źródła ramki o SourceKind jako Color. Niektóre sterowniki kamer zapewniają dedykowany strumień podglądu, który jest oddzielony od strumienia nagrywania. Aby uzyskać podgląd strumienia wideo, spróbujemy wybrać źródło ramki z MediaStreamType ustawionym na VideoPreview. Jeśli nie znaleziono żadnych strumieni podglądu, możemy pobrać strumień wideo nagrywania, wybierając MediaStreamTypeVideoRecord. Jeśli żadne z tych źródeł klatek nie jest dostępne, nie można użyć tego urządzenia przechwytywania do podglądu wideo.

Po wybraniu źródła ramki utworzymy nowy obiekt MediaPlayer , który będzie renderowany przez element MediaPlayerElement w interfejsie użytkownika. Ustawiliśmy właściwość Source elementu MediaPlayer na nowy obiekt MediaSource tworzony na podstawie wybranego elementu MediaFrameSource.

Wywołaj Play na obiekcie MediaPlayer, aby rozpocząć renderowanie strumienia wideo.

private void bStartPreview_Click(object sender, RoutedEventArgs e)
{
    
    m_frameSource = null;

    // Find preview source.
    // The preferred preview stream from a camera is defined by MediaStreamType.VideoPreview on the RGB camera (SourceKind == color).
    var previewSource = m_mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoPreview
                                                                                && source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;

    if (previewSource != null)
    {
        m_frameSource = previewSource;
    }
    else
    {
        var recordSource = m_mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoRecord
                                                                                   && source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;
        if (recordSource != null)
        {
            m_frameSource = recordSource;
        }
    }

    if (m_frameSource == null)
    {
        tbStatus.Text = "No video preview or record stream found.";
        return;
    }



    // Create MediaPlayer with the preview source
    m_mediaPlayer = new MediaPlayer();
    m_mediaPlayer.RealTimePlayback = true;
    m_mediaPlayer.AutoPlay = false;
    m_mediaPlayer.Source = MediaSource.CreateFromMediaFrameSource(m_frameSource);
    m_mediaPlayer.MediaFailed += MediaPlayer_MediaFailed; ;

    // Set the mediaPlayer on the MediaPlayerElement
    mpePreview.SetMediaPlayer(m_mediaPlayer);

    // Start preview
    m_mediaPlayer.Play();


    tbStatus.Text = "Start preview succeeded!";
    m_isPreviewing = true;
    bStartPreview.IsEnabled = false;
    bStopPreview.IsEnabled = true;
}

Zaimplementuj procedurę dla zdarzenia MediaFailed, aby obsłużyć błędy renderowania podglądu.

private void MediaPlayer_MediaFailed(MediaPlayer sender, MediaPlayerFailedEventArgs args)
{
    tbStatus.Text = "MediaPlayer error: " + args.ErrorMessage;
}

Zatrzymaj podgląd kamery

Aby zatrzymać podgląd aparatu, wywołaj funkcję Pause w obiekcie MediaPlayer .

private void bStopPreview_Click(object sender, RoutedEventArgs e)
{
    // Stop preview
    m_mediaPlayer.Pause();
    m_isPreviewing = false;
    bStartPreview.IsEnabled = true;
    bStopPreview.IsEnabled = false;
}

Resetowanie aplikacji

Aby ułatwić testowanie przykładowej aplikacji, dodaj metodę resetowania stanu aplikacji. Aplikacje aparatu fotograficznego powinny zawsze usuwać aparat i skojarzone zasoby, gdy aparat nie jest już potrzebny.

private void bReset_Click(object sender, RoutedEventArgs e)
{
    if (m_mediaCapture != null)
    {
        m_mediaCapture.Dispose();
        m_mediaCapture = null;
    }

    if(m_mediaPlayer != null)
    {
        m_mediaPlayer.Dispose();
        m_mediaPlayer = null;
    }
    
    m_frameSource = null;
    

    bStartMediaCapture.IsEnabled = false;
    bStartPreview.IsEnabled = false;
    bStopPreview.IsEnabled = false;

    PopulateCameraList();

}