Visualizzare l'anteprima della fotocamera

Questo articolo descrive come visualizzare rapidamente il flusso di anteprima della fotocamera all'interno di una pagina XAML in un'app UWP. Per creare un'app che consente di acquisire foto e video con la fotocamera, devi eseguire attività come la gestione dell'orientamento del dispositivo e della fotocamera o l'impostazione delle opzioni di codifica per il file acquisito. Per alcuni scenari, potresti voler visualizzare semplicemente il flusso di anteprima dalla fotocamera senza preoccuparti di queste altre considerazioni. Questo articolo illustra come eseguire questa operazione con una quantità minima di codice. Tieni presente che è sempre necessario arrestare il flusso di anteprima correttamente dopo averlo usato, seguendo i passaggi seguenti.

Per informazioni sulla scrittura di un'app per la fotocamera che acquisisce foto o video, vedi Acquisizione di foto, video e audio di base con MediaCapture.

Aggiungere dichiarazioni di funzionalità al manifesto dell'app

Affinché l’app possa accedere alla fotocamera di un dispositivo, devi dichiarare l’uso da parte dell’app delle funzionalità webcam e microfono del dispositivo.

Aggiungere funzionalità al manifesto dell'app

  1. In Microsoft Visual Studio fai doppio clic sull'elemento package.appxmanifest in Esplora soluzioni per aprire la finestra di progettazione del manifesto dell'applicazione.
  2. Fare clic sulla scheda Funzionalità.
  3. Seleziona la casella di controllo per Webcam e per Microfono.

Aggiungere un oggetto CaptureElement alla pagina

Usa captureElement per visualizzare il flusso di anteprima all'interno della pagina XAML.

<CaptureElement Name="PreviewControl" Stretch="Uniform"/>

Usare MediaCapture per avviare il flusso di anteprima

L’oggetto MediaCapture è l’interfaccia tra la tua app e la fotocamera del dispositivo. Questa classe è un membro dello spazio dei nomi Windows.Media.Capture. L’esempio in questo articolo usa anche le API degli spazi dei nomi Windows.ApplicationModel e System.Threading.Tasks, oltre a quelli inclusi dal modello di progetto predefinito.

Aggiungi direttive using per includere gli spazi dei nomi seguenti nel file con estensione cs della pagina.

using Windows.UI.Core;
using Windows.UI.Xaml.Navigation;
using Windows.Media.Capture;
using Windows.ApplicationModel;
using System.Threading.Tasks;
using Windows.System.Display;
using Windows.Graphics.Display;

Dichiara una variabile membro della classe per l'oggetto MediaCapture e un valore booleano per rilevare se la fotocamera è in modalità di anteprima.

MediaCapture mediaCapture;
bool isPreviewing;

Dichiarare una variabile di tipo DisplayRequest che verrà usata per assicurarsi che la visualizzazione non si spegni durante l'esecuzione dell'anteprima.

DisplayRequest displayRequest = new DisplayRequest();

Creare un metodo helper per avviare l'anteprima della fotocamera, denominato StartPreviewAsync in questo esempio. A seconda dello scenario dell'app, è possibile chiamare questo metodo dal gestore dell'evento OnNavigatedTo che viene chiamato quando la pagina viene caricata o attende e avvia l'anteprima in risposta agli eventi dell'interfaccia utente.

Crea una nuova istanza della classe MediaCapture e chiama InitializeAsync per inizializzare il dispositivo di acquisizione. Questo metodo potrebbe non riuscire, ad esempio nei dispositivi che non hanno una fotocamera, quindi dovresti chiamarlo dall’interno di un blocco try. Verrà generata una UnauthorizedAccessException quando tenti di inizializzare la fotocamera se l’utente ha disabilitato l’accesso alla fotocamera nelle impostazioni di privacy del dispositivo. Vedrai anche questa eccezione durante lo sviluppo se non hai aggiunto le funzionalità appropriate al manifesto dell'app.

Importante In alcune famiglie di dispositivi prima di concedere all’app l’accesso alla fotocamera del dispositivo viene visualizzata una richiesta di consenso per l’utente. Per questo motivo, è necessario chiamare solo MediaCapture.InitializeAsync dal thread principale dell'interfaccia utente. Il tentativo di inizializzare la fotocamera da un altro thread potrebbe causare un errore di inizializzazione.

Connetti MediaCapture a CaptureElement impostando la proprietà Source. Avviare l'anteprima chiamando StartPreviewAsync. Questo metodo genererà un'eccezione FileLoadException se un'altra app ha il controllo esclusivo del dispositivo di acquisizione. Vedere la sezione successiva per informazioni sull'ascolto delle modifiche nel controllo esclusivo.

Chiama RequestActive per assicurarti che non venga attivata la modalità sospensione per il dispositivo mentre è in esecuzione l'anteprima. Infine, imposta la proprietà DisplayInformation.AutoRotationPreferences su Landscape per impedire la rotazione dell'interfaccia utente e di CaptureElement quando l'utente cambia l'orientamento del dispositivo. Per altre informazioni sulla gestione delle modifiche dell'orientamento del dispositivo, vedere Gestire l'orientamento del dispositivo con MediaCapture.

       private async Task StartPreviewAsync()
       {
           try
           {

               mediaCapture = new MediaCapture();
               await mediaCapture.InitializeAsync();

               displayRequest.RequestActive();
               DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;
           }
           catch (UnauthorizedAccessException)
           {
               // This will be thrown if the user denied access to the camera in privacy settings
               ShowMessageToUser("The app was denied access to the camera");
               return;
           }

           try
           {
               PreviewControl.Source = mediaCapture;
               await mediaCapture.StartPreviewAsync();
               isPreviewing = true;
           }
           catch (System.IO.FileLoadException)
           {
               mediaCapture.CaptureDeviceExclusiveControlStatusChanged += _mediaCapture_CaptureDeviceExclusiveControlStatusChanged;
           }

       }

Gestire le modifiche nel controllo esclusivo

Come indicato nella sezione precedente, StartPreviewAsync genererà un'eccezione FileLoadException se un'altra app ha il controllo esclusivo del dispositivo di acquisizione. A partire da Windows 10 versione 1703, è possibile registrare un gestore per l'evento MediaCapture.CaptureDeviceExclusiveControlStatusChanged, che viene generato ogni volta che cambia lo stato esclusivo del controllo del dispositivo. Nel gestore per questo evento controllare la proprietà MediaCaptureDeviceExclusiveControlStatusChangedEventArgs.Status per visualizzare lo stato corrente. Se il nuovo stato è SharedReadOnlyAvailable, si sa che non è attualmente possibile avviare l'anteprima ed è possibile aggiornare l'interfaccia utente per avvisare l'utente. Se il nuovo stato è ExclusiveControlAvailable, è possibile provare di nuovo ad avviare l'anteprima della fotocamera.

private async void _mediaCapture_CaptureDeviceExclusiveControlStatusChanged(MediaCapture sender, MediaCaptureDeviceExclusiveControlStatusChangedEventArgs args)
{
    if (args.Status == MediaCaptureDeviceExclusiveControlStatus.SharedReadOnlyAvailable)
    {
        ShowMessageToUser("The camera preview can't be displayed because another app has exclusive access");
    }
    else if (args.Status == MediaCaptureDeviceExclusiveControlStatus.ExclusiveControlAvailable && !isPreviewing)
    {
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        {
            await StartPreviewAsync();
        });
    }
}

Arrestare il flusso di anteprima

Quando hai finito di usare il flusso di anteprima, dovresti sempre arrestarlo ed eliminare correttamente le risorse associate per assicurarti che la fotocamera sia disponibile per altre app nel dispositivo. I passaggi necessari per arrestare il flusso di anteprima sono:

  • Se la fotocamera è in modalità anteprima, chiamare StopPreviewAsync per arrestare il flusso di anteprima. Verrà generata un'eccezione se chiami StopPreviewAsync mentre l'anteprima non è in esecuzione.
  • Impostare la proprietà Source di CaptureElement su Null. Usare CoreDispatcher.RunAsync per assicurarsi che questa chiamata sia eseguita nel thread dell'interfaccia utente.
  • Chiamare il metodo Dispose dell'oggetto MediaCapture per rilasciare l'oggetto. Anche in questo caso, usare CoreDispatcher.RunAsync per assicurarsi che questa chiamata sia eseguita nel thread dell'interfaccia utente.
  • Impostare la variabile membro MediaCapture su Null.
  • Chiamare RequestRelease per consentire la disattivazione della schermata quando è inattiva.
private async Task CleanupCameraAsync()
{
    if (mediaCapture != null)
    {
        if (isPreviewing)
        {
            await mediaCapture.StopPreviewAsync();
        }

        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            PreviewControl.Source = null;
            if (displayRequest != null)
            {
                displayRequest.RequestRelease();
            }

            mediaCapture.Dispose();
            mediaCapture = null;
        });
    }
    
}

È consigliabile arrestare il flusso di anteprima quando l'utente si sposta dalla pagina eseguendo l'override del metodo OnNavigatedFrom.

protected async override void OnNavigatedFrom(NavigationEventArgs e)
{
    await CleanupCameraAsync();
}

Dovresti arrestare adeguatamente il flusso di anteprima anche quando la tua app viene sospesa. A tale scopo, registrare un gestore per l'evento Application.Suspending nel costruttore della pagina.

public MainPage()
{
    this.InitializeComponent();

    Application.Current.Suspending += Application_Suspending;
}

Nel gestore dell’evento Suspending controlla prima di tutto che la pagina sia visualizzata nel Frame dell’applicazione confrontando il tipo di pagina con la proprietà CurrentSourcePageType. Se la pagina non è attualmente visualizzata, l’evento OnNavigatedFrom dovrebbe essere già stato generato e il flusso di anteprima arrestato. Se la pagina è attualmente visualizzata, ottenere un oggetto SuspendingDeferral dagli argomenti dell'evento passati nel gestore per assicurarsi che il sistema non sospende l'app fino a quando il flusso di anteprima non è stato arrestato. Dopo aver arrestato il flusso, chiamare il metodo Complete del rinvio per consentire al sistema di continuare a sospendere l'app.

private async void Application_Suspending(object sender, SuspendingEventArgs e)
{
    // Handle global application events only if this page is active
    if (Frame.CurrentSourcePageType == typeof(MainPage))
    {
        var deferral = e.SuspendingOperation.GetDeferral();
        await CleanupCameraAsync();
        deferral.Complete();
    }
}