Condividi tramite


Visualizzare l'anteprima della fotocamera in un'app WinUI 3

In questa guida introduttiva si apprenderà come creare un'app per fotocamera WinUI 3 di base che visualizza l'anteprima della fotocamera. In un'app WinUI 3 usi il controllo MediaPlayerElement nello spazio dei nomi Microsoft.UI.Xaml.Controls per eseguire il rendering dell'anteprima della fotocamera e della classe WinRT MediaCapture per accedere al flusso di anteprima della fotocamera del dispositivo. MediaCapture fornisce API per l'esecuzione di un'ampia gamma di attività correlate alla fotocamera, ad esempio l'acquisizione di foto e video e la configurazione del driver di dispositivo della fotocamera. Per informazioni dettagliate sulle altre funzionalità di MediaCapture , vedere gli altri articoli in questa sezione.

Il codice in questa procedura dettagliata è adattato dall'esempio MediaCapture WinUI 3 su GitHub.

Suggerimento

Per la versione UWP di questo articolo, vedi Visualizzare l'anteprima della fotocamera nella documentazione della piattaforma UWP.

Prerequisiti

  • Il dispositivo deve avere la modalità sviluppatore abilitata. Per ulteriori informazioni, vedere Abilitare il dispositivo allo sviluppo.
  • Visual Studio 2022 o versione successiva con il carico di lavoro Sviluppo di applicazioni WinUI .

Creare una nuova app WinUI 3

In Visual Studio creare un nuovo progetto. Nella finestra di dialogo Crea un nuovo progetto impostare il filtro del linguaggio su "C#" e il filtro della piattaforma su "Windows", quindi selezionare il modello di progetto "App vuota, In pacchetto (WinUI 3 in desktop)".

Creare l'interfaccia utente

L'interfaccia utente semplice per questo esempio include un controllo MediaPlayerElement per la visualizzazione dell'anteprima della fotocamera, un controllo ComboBox che consente di selezionare le fotocamere del dispositivo e i pulsanti per inizializzare la classe MediaCapture , avviare e arrestare l'anteprima della fotocamera e reimpostare l'esempio. Includiamo anche un controllo TextBlock per la visualizzazione dei messaggi di stato.

Nel file MainWindow.xml del progetto sostituire il controllo StackPanel predefinito con il codice XAML seguente.

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

Aggiornare la definizione della classe MainWindow

Il resto del codice in questo articolo verrà aggiunto alla definizione della classe MainWindow nel file di MainWindow.xaml.cs del progetto. Aggiungere prima di tutto alcune variabili di classe che verranno mantenute per tutta la durata della finestra. Queste variabili includono:

  • DeviceInformationCollection che archivierà un oggetto DeviceInformation per ogni fotocamera disponibile. L'oggetto DeviceInformation fornisce informazioni quali l'identificatore univoco e il nome descrittivo per la fotocamera.
  • Oggetto MediaCapture che gestisce le interazioni con il driver della fotocamera selezionata e consente di recuperare il flusso video della fotocamera.
  • Oggetto MediaFrameSource che rappresenta un'origine di fotogrammi multimediali, ad esempio un flusso video.
  • Un booleano per tracciare quando l'anteprima della fotocamera è in esecuzione. Alcune impostazioni della fotocamera non possono essere modificate durante l'esecuzione dell'anteprima, quindi è consigliabile tenere traccia dello stato dell'anteprima della fotocamera.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Popolare l'elenco delle fotocamere disponibili

Successivamente creeremo un metodo helper per rilevare le fotocamere presenti nel dispositivo corrente e popolare comboBox nell'interfaccia utente con i nomi della fotocamera, consentendo all'utente di selezionare una fotocamera da visualizzare in anteprima. DeviceInformation.FindAllAsync consente di eseguire query per molti tipi diversi di dispositivi. Usiamo MediaDevice.GetVideoCaptureSelector per recuperare l'identificatore che specifica che vogliamo recuperare solo i dispositivi di acquisizione video.

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

Aggiungere una chiamata a questo metodo helper al costruttore della classe MainWindow in modo che ComboBox venga popolato al caricamento della finestra.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Inizializzare l'oggetto MediaCapture

Inizializzare l'oggetto MediaCapture chiamando InitializeAsync, passando un oggetto MediaCaptureInitializationSettings contenente i parametri di inizializzazione richiesti. Esistono molti parametri di inizializzazione facoltativi che consentono scenari diversi. Per l'elenco completo, vedere la pagina di riferimento dell'API. In questo semplice esempio vengono specificate alcune impostazioni di base, tra cui:

  • La proprietà VideoDeviceId specifica l'identificatore univoco della fotocamera a cui verrà collegato MediaCapture . L'ID dispositivo viene ottenuto da DeviceInformationCollection usando l'indice selezionato di ComboBox.
  • La proprietà SharingMode specifica se l'app richiede l'accesso condiviso e di sola lettura alla fotocamera, che consente di visualizzare e acquisire dal flusso video o il controllo esclusivo della fotocamera, che consente di modificare la configurazione della fotocamera. Più app possono leggere contemporaneamente da una fotocamera, ma solo un'app alla volta può avere un controllo esclusivo.
  • La proprietà StreamingCaptureMode specifica se si vuole acquisire video, audio o audio e video.
  • MediaCaptureMemoryPreference consente di richiedere di usare in modo specifico la memoria della CPU per i fotogrammi video. Il valore Auto consente al sistema di usare la memoria GPU, se disponibile.

Prima di inizializzare l'oggetto MediaCapture , chiamiamo il metodo AppCapability.CheckAccess per determinare se l'utente ha negato all'app l'accesso alla fotocamera in Impostazioni di Windows.

Annotazioni

Windows consente agli utenti di concedere o negare l'accesso alla fotocamera del dispositivo nell'app Impostazioni di Windows, in Privacy e sicurezza -> Fotocamera. Quando si inizializza il dispositivo di acquisizione, le app devono verificare se hanno accesso alla fotocamera e gestiscono il caso in cui l'accesso viene negato dall'utente. Per altre informazioni, vedere Gestire l'impostazione della privacy della fotocamera di Windows.

La chiamata InitializeAsync viene eseguita dall'interno di un blocco try in modo da poter eseguire il ripristino in caso di errore di inizializzazione. Le app devono gestire correttamente l'errore di inizializzazione. In questo semplice esempio verrà visualizzato solo un messaggio di errore in caso di errore.

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

Inizializzare l'anteprima della fotocamera

Quando l'utente fa clic sul pulsante di anteprima iniziale, tenteremo di creare un Oggetto MediaFrameSource per un flusso video dal dispositivo fotocamera con cui è stato inizializzato l'oggetto MediaCapture . Le origini dei frame disponibili vengono esposte dalla proprietà MediaCapture.FrameSources .

Per trovare un'origine fotogramma che rappresenta dati video a colori, anziché una fotocamera di profondità, ad esempio, cerchiamo un'origine fotogramma con SourceKind of Color. Alcuni driver della fotocamera forniscono un flusso di anteprima dedicato separato dal flusso di registrazione. Per ottenere il flusso video di anteprima, proviamo a selezionare un'origine fotogramma con mediaStreamType di VideoPreview. Se non vengono trovati flussi di anteprima, è possibile ottenere il flusso video del record selezionando mediaStreamType di VideoRecord. Se nessuna di queste fonti dei fotogrammi è disponibile, questo dispositivo di acquisizione non si può usare per l'anteprima video.

Dopo aver selezionato un'origine fotogramma, creeremo un nuovo oggetto MediaPlayer che verrà renderizzato da MediaPlayerElement nella nostra interfaccia utente. Impostiamo la proprietà Source di MediaPlayer su un nuovo oggetto MediaSource creato dall'oggetto MediaFrameSource selezionato.

Chiama Play sull'oggetto MediaPlayer per iniziare a eseguire il rendering del flusso video.

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

Implementare un gestore per l'evento MediaFailed in modo da poter gestire gli errori durante il rendering dell'anteprima.

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

Ferma l'anteprima della fotocamera

Per arrestare l'anteprima della fotocamera, chiama Pause sull'oggetto MediaPlayer .

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

Reimpostare l'app

Per semplificare il test dell'app di esempio, aggiungere un metodo per reimpostare lo stato dell'app. Le app fotocamera devono sempre eliminare la fotocamera e le risorse associate quando la fotocamera non è più necessaria.

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

}