Sdílet prostřednictvím


Zobrazení náhledu fotoaparátu v aplikaci WinUI 3

V tomto rychlém startu se dozvíte, jak vytvořit základní aplikaci fotoaparátu WinUI, která zobrazuje náhled fotoaparátu. V aplikaci WinUI použijete ovládací prvek MediaPlayerElement v oboru názvů Microsoft.UI.Xaml.Controls pro vykreslení náhledu kamery a třídu WinRT MediaCapture pro přístup ke streamu náhledu kamery zařízení. MediaCapture poskytuje rozhraní API pro provádění široké škály úloh souvisejících s fotoaparátem, jako je například zachytávání fotek a videí a konfigurace ovladače zařízení fotoaparátu. Podrobnosti o dalších funkcích MediaCapture najdete v dalších článcích v této části.

Kód v tomto návodu je upraven z ukázky MediaCapture WinUI na github.

Návod

Informace o verzi tohoto článku pro UWP najdete v Zobrazení náhledu fotoaparátu v dokumentaci k UWP.

Požadavky

  • Vaše zařízení musí mít povolený vývojářský režim. Další informace najdete v tématu Nastavení pro vývojáře.
  • Visual Studio 2022 nebo novější s úlohou Vývoj aplikací WinUI.

Vytvoření nové aplikace WinUI

V Visual Studio vytvořte nový project. V dialogovém okně Vytvoření nového projektu nastavte filtr jazyka na "C#" a filtr platformy na "Windows", potom vyberte šablonu "Prázdná aplikace, Zabalené (WinUI pro plochu)".

Vytvoření uživatelského rozhraní

Jednoduché uživatelské rozhraní pro tento příklad zahrnuje ovládací prvek MediaPlayerElement pro zobrazení náhledu kamery, pole ComboBox , které umožňuje vybrat z fotoaparátů zařízení a tlačítka pro inicializaci třídy MediaCapture , spuštění a zastavení náhledu kamery a resetování ukázky. Zahrneme také TextBlock pro zobrazení stavových zpráv.

V souboru MainWindow.xml vašeho project nahraďte výchozí ovládací prvek StackPanel následujícím kódem 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>

Aktualizace definice třídy MainWindow

Zbytek kódu v tomto článku se přidá do definice třídy MainWindow v souboru MainWindow.xaml.cs vašeho project. Nejprve přidejte několik proměnných tříd, které se budou uchovávat po celou dobu životnosti okna. Mezi tyto proměnné patří:

  • A DeviceInformationCollection , který bude ukládat DeviceInformation objekt pro každou dostupnou kameru. DeviceInformation objekt sděluje informace, jako je jedinečný identifikátor a popisný název kamery.
  • Objekt MediaCapture , který zpracovává interakce s ovladačem vybrané kamery a umožňuje načíst datový proud videa kamery.
  • Objekt MediaFrameSource, který představuje zdroj multimediálních snímků, jako je stream videa.
  • Logická hodnota, která se má sledovat, když je spuštěn náhled kamery. Některá nastavení fotoaparátu se nedají změnit, když je náhled spuštěný, takže je vhodné sledovat stav náhledu fotoaparátu.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Naplnění seznamu dostupných fotoaparátů

Dále vytvoříme pomocnou metodu pro detekci fotoaparátů, které jsou přítomné na aktuálním zařízení, a naplníme pole ComboBox v uživatelském rozhraní názvy fotoaparátů, což uživateli umožní vybrat fotoaparát k náhledu. DeviceInformation.FindAllAsync umožňuje dotazovat se na mnoho různých druhů zařízení. Používáme MediaDevice.GetVideoCaptureSelector k načtení identifikátoru, který určuje, že chceme načíst pouze zařízení pro záznam videa.

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

Do konstruktoru třídy MainWindow přidejte volání této pomocné metody, aby se comboBox naplní při načtení okna.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Inicializujte objekt MediaCapture

Inicializujte objekt MediaCapture voláním InitializeAsync a předejte objekt MediaCaptureInitializationSettings, který obsahuje požadované inicializační parametry. Existuje mnoho volitelných inicializačních parametrů, které umožňují různé scénáře. Úplný seznam najdete na stránce s referenčními informacemi k rozhraní API. V tomto jednoduchém příkladu určíme několik základních nastavení, včetně:

  • Vlastnost VideoDeviceId určuje jedinečný identifikátor kamery, ke které se mediaCapture připojí. Získáme ID zařízení z DeviceInformationCollection pomocí vybraného indexu ComboBox.
  • Vlastnost SharingMode určuje, jestli aplikace požaduje sdílenou access jen pro čtení do fotoaparátu, což umožňuje zobrazit a zachytit stream videa nebo výhradní kontrolu kamery, což umožňuje změnit konfiguraci kamery. Z fotoaparátu může současně číst více aplikací, ale výhradní kontrolu může mít jenom jedna aplikace najednou.
  • Vlastnost StreamingCaptureMode určuje, zda chceme zachytit video, zvuk nebo zvuk a video.
  • MediaCaptureMemoryPreference nám umožňuje vyžádat si konkrétně využití paměti procesoru pro snímky videa. Hodnota Auto umožňuje systému používat paměť GPU, pokud je k dispozici.

Před inicializací objektu MediaCapture voláme metodu AppCapability.CheckAccess a určíme, jestli uživatel zamítl aplikaci access fotoaparátu v nastavení Windows.

Poznámka:

Systém Windows umožňuje uživatelům udělit nebo odepřít přístup k fotoaparátu zařízení v nastavení Windows v části Ochrana osobních údajů a zabezpečení -> Kamera. Při inicializaci zařízení pro zachytávání by aplikace měly zkontrolovat, jestli mají access do fotoaparátu, a zpracují případ, kdy uživatel access odepře. Další informace naleznete v tématu Zpracování nastavení ochrany osobních údajů fotoaparátu systému Windows.

Volání InitializeAsync je provedeno uvnitř bloku try , abychom mohli obnovit, pokud inicializace selže. Aplikace by měly řádně zpracovávat selhání inicializace. V tomto jednoduchém příkladu zobrazíme chybovou zprávu o selhání.

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

Inicializace náhledu fotoaparátu

Když uživatel klikne na tlačítko Náhled startu, pokusíme se vytvořit MediaFrameSource pro datový proud videa ze zařízení fotoaparátu, pomocí kterého byl objekt MediaCapture inicializován. Přístupné zdroje rámce jsou dostupné prostřednictvím vlastnosti MediaCapture.FrameSources.

Chcete-li najít zdroj snímků, který obsahuje barevná videodata, například na rozdíl od hloubkové kamery, hledáme zdroj snímků, který má SourceKindColor. Některé ovladače fotoaparátu poskytují vyhrazený náhledový stream, který je oddělený od streamu pro záznam. Abychom získali stream videa ve verzi Preview, pokusíme se vybrat zdroj snímku, který má MediaStreamTypeVideoPreview. Pokud nejsou nalezeny žádné streamy náhledu, můžeme získat záznamový video stream výběrem MediaStreamTypeVideoRecord. Pokud není k dispozici žádný z těchto zdrojů snímků, toto zařízení pro zachycení se nedá použít pro náhled videa.

Jakmile vybereme zdroj rámce, vytvoříme nový objekt MediaPlayer , který bude vykreslen MediaPlayerElement v našem uživatelském rozhraní. Vlastnost SourcemediaPlayer nastavíme na nový objekt MediaSource , který vytvoříme z vybraného MediaFrameSource.

Zavolejte Přehrát na objektu MediaPlayer, aby bylo možné začít vykreslovat video stream.

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

Implementujte obslužnou rutinu události MediaFailed , abyste mohli zpracovat chyby vykreslování náhledu.

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

Zastavení náhledu kamery

Pokud chcete zastavit náhled kamery, zavolejte metodu Pause u objektu MediaPlayer.

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

Resetování aplikace

Pokud chcete usnadnit testování ukázkové aplikace, přidejte metodu pro resetování stavu aplikace. Aplikace fotoaparátu by měly vždy likvidovat kameru a přidružené prostředky, pokud už kameru nepotřebujete.

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

}