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 (Universal Windows Platform). La creazione di un'app che acquisisce foto e video usando la fotocamera richiede di 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 di app, basta semplicemente mostrare il flusso di anteprima dalla fotocamera senza preoccuparsi di queste altre considerazioni. Questo articolo illustra come eseguire questa operazione con un minimo di codice. Nota: è consigliabile arrestare sempre correttamente il flusso di anteprima al termine dell'operazione seguendo la procedura seguente.

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

Aggiungere dichiarazioni di funzionalità al manifesto dell'app

Per consentire all'app di accedere alla fotocamera di un dispositivo, bisogna dichiarare che l'app usa le funzionalità del dispositivo webcam e microfono.

Aggiungere funzionalità al manifesto dell'app

  1. In Esplora soluzioni in Microsoft Visual Studio aprire la finestra di progettazione per il manifesto dell'applicazione facendo doppio clic sull'elemento package.appxmanifest.
  2. Fare clic sulla scheda Funzionalità.
  3. Selezionare la casella Webcam e la casella microfono.

Aggiungere CaptureElement alla pagina

Usare 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 dell'app per la fotocamera del dispositivo. Questa classe è un membro di 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.

Aggiungere direttive using per includere gli spazi dei nomi seguenti nel file.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;

Dichiarare una variabile membro della classe per l'oggetto MediaCapture e un valore booleano per rilevare se la fotocamera è attualmente in anteprima.

MediaCapture mediaCapture;
bool isPreviewing;

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

DisplayRequest displayRequest = new DisplayRequest();

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

Creare una nuova istanza della classe MediaCapture e chiamare InitializeAsync per inizializzare il dispositivo di acquisizione. Questo metodo potrebbe non riuscire, ad esempio, nei dispositivi che non dispongono di una fotocamera, quindi è necessario chiamarlo dall'interno di un blocco try. Si genera UnauthorizedAccessException quando si tenta di inizializzare la fotocamera se l'utente ha disabilitato l'accesso alla fotocamera nelle impostazioni di privacy del dispositivo. Questa eccezione viene visualizzata anche durante lo sviluppo se non si è trascurato di aggiungere le funzionalità appropriate al manifesto dell'app.

Importante In alcune famiglie di dispositivi, viene visualizzata una richiesta di consenso dell'utente prima che all'app venga concesso l'accesso alla fotocamera del dispositivo. Per questo motivo, è necessario chiamare solo MediaCapture.InitializeAsync dal thread principale dell'interfaccia utente. Il tentativo di inizializzare la fotocamera da un altro thread può 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 in ascolto delle modifiche nel controllo esclusivo.

Chiamare RequestActive per assicurarsi che il dispositivo non vada in sospensione mentre è in esecuzione l'anteprima. Infine impostare la proprietà DisplayInformation.AutoRotationPreferences su Orizzontale per impedire la rotazione dell'interfaccia utente e CaptureElement quando l'utente modifica l'orientamento del dispositivo. Per altre informazioni sulla gestione delle modifiche dell'orientamento dei dispositivi, 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 di controllo esclusivo del dispositivo. Nel gestore per questo evento controllare la proprietà MediaCaptureDeviceExclusiveControlStatusChangedEventArgs.Status per vedere qual è lo stato corrente. Se il nuovo stato è SharedReadOnlyAvailable, non è possibile avviare l'anteprima e potrebbe essere necessario aggiornare l'interfaccia utente per avvisare l'utente. Se il nuovo stato è ExclusiveControlAvailable, è possibile provare ad avviare nuovamente 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

Al termine dell'uso del flusso di anteprima, è consigliabile arrestare sempre il flusso ed eliminare correttamente le risorse associate per assicurarsi che la fotocamera sia disponibile per altre app nel dispositivo. I passaggi necessari per arrestare il flusso di anteprima sono:

  • Se la fotocamera è attualmente in anteprima, chiamare StopPreviewAsync per arrestare il flusso di anteprima. Verrà generata un'eccezione se si chiama StopPreviewAsync mentre l'anteprima non è in esecuzione.
  • Impostare la proprietà Source di CaptureElement su Null. Usare CoreDispatcher.RunAsync per assicurarsi che questa chiamata venga eseguita nel thread dell'interfaccia utente.
  • Chiamare il metodo Dispose dell'oggetto MediaCapture per rilasciare l'oggetto. Di nuovo, usare CoreDispatcher.RunAsync per assicurarsi che questa chiamata venga eseguita nel thread dell'interfaccia utente.
  • Impostare la variabile membro MediaCapture su Null.
  • Chiamare RequestRelease per consentire la disattivazione della schermata quando non è attiva.
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 allontana dalla pagina eseguendo l'override del metodo OnNavigatedFrom.

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

Inoltre è consigliabile arrestare correttamente il flusso di anteprima quando l'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 eventi Suspending verificare prima di tutto che la pagina venga visualizzata in 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 deve essere stato 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 Completo del differimento 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();
    }
}