Udostępnij za pośrednictwem


Wyświetlanie podglądu aparatu w aplikacji WinUI 3

W tym szybkim przewodniku dowiesz się, jak utworzyć podstawową aplikację WinUI dla aparatu, która wyświetla podgląd z kamery. W aplikacji WinUI używasz kontrolki MediaPlayerElement w przestrzeni nazw Microsoft.UI.Xaml.Controls do renderowania podglądu aparatu oraz klasy WinRT MediaCapture, aby uzyskać dostęp 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 jest dostosowywany z przykładu MediaCapture WinUI na GitHubie.

Wskazówka

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

Wymagania wstępne

  • Urządzenie musi mieć włączony tryb dewelopera. Aby uzyskać więcej informacji, zobacz Ustawienia dla deweloperów.
  • Visual Studio 2022 lub nowszą wersją z pakietem roboczym WinUI do tworzenia aplikacji.

Tworzenie nowej aplikacji WinUI

W Visual Studio utwórz nowy project. W oknie dialogowym Utwórz nowy projekt ustaw filtr języka na "C#", a filtr platformy na "Windows", a następnie wybierz szablon projektu "Pusta aplikacja, spakowana (WinUI na 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 project 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

Reszta kodu w tym artykule zostanie dodana do definicji klasy MainWindow w pliku MainWindow.xaml.cs Twojego 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 kamery, co umożliwia wyświetlanie i przechwytywanie ze strumienia wideo, czy wyłącznego dostępu do kamery, co pozwala na zmianę jej konfiguracji. 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 ustalić, czy użytkownik odmówił naszej aplikacji dostępu do aparatu w ustawieniach systemu Windows.

Uwaga / Notatka

System Windows umożliwia użytkownikom przyznawanie lub odrzucanie access do aparatu urządzenia w ustawieniach systemu Windows w obszarze Prywatność i Zabezpieczenia -> Camera. Podczas inicjowania urządzenia przechwytywania aplikacje powinny sprawdzić, czy mają dostęp do kamery i obsługiwać przypadek, w którym dostęp jest odrzucany 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();

}