共用方式為


顯示相機預覽

本文說明如何在通用 Windows 平台 (UWP) 應用程式中的 XAML 頁面內,快速顯示相機預覽串流。 建立使用相機攝錄相片和影片的應用程式,需要您為攝錄檔案執行裝置操作與相機轉向,或是設定編碼選項等工作。 在某些應用程式案例中,您可能只想從相機顯示預覽串流,而不打算煩惱這些其他考量。 本文說明如何使用最少的程式碼來執行此動作。 請注意,當您完成預覽串流時,應該一律遵循下列步驟來正確關閉預覽串流。

如需撰寫攝錄相片或影片相機應用程式的資訊,請參閱使用 MediaCapture 攝錄相片、影片與音訊的基本知識

將功能宣告加到應用程式資訊清單

為了讓您的應用程式存取裝置的相機,您必須宣告應用程式使用網路攝影機麥克風裝置功能。

將功能加到應用程式資訊清單

  1. 在 Microsoft Visual Studio 的 [方案總管] 中,按兩下 package.appxmanifest 項目,開啟應用程式資訊清單的設計工具。
  2. 選取 [功能] 索引標籤。
  3. 核取 [網路攝影機] 方塊和 [麥克風] 方塊。

將 CaptureElement 新增至您的頁面

使用 CaptureElement 在 XAML 頁面中顯示預覽串流。

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

使用 MediaCapture 啟動預覽串流

MediaCapture 物件是裝置相機的應用程式介面。 這個類別是 Windows.Media.Capture 命名空間的成員。 本文中的範例也會使用來自 Windows.ApplicationModelSystem.Threading.Tasks 命名空間的 API,以及預設專案範本所包含的 API。

新增 using 指示詞,以在頁面的 .cs 檔案中包含下列命名空間。

//MainPage.xaml.cs
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;

宣告 MediaCapture 物件的類別成員變數,以及用來追蹤相機目前是否正在預覽的布林值。

MediaCapture mediaCapture;
bool isPreviewing;

宣告 DisplayRequest 類型的變數,該變數將用來確保預覽執行時不會關閉顯示器。

DisplayRequest displayRequest = new DisplayRequest();

建立協助程式方法來啟動相機預覽,在此範例中稱為 StartPreviewAsync。 視您應用程式的案例而定,您可能會想要從載入頁面或等候頁面時呼叫的 OnNavigatedTo 事件處理常式來呼叫此方法,並啟動預覽以回應 UI 事件。

建立 MediaCapture 類別的新執行個體,並呼叫 InitializeAsync 來初始化攝錄裝置。 例如,在沒有相機的裝置上,這個方法可能會失敗,因此您應該從 try 區塊內呼叫它。 如果使用者在裝置隱私權設定中停用相機存取,而您嘗試初始化相機,則系統會擲回 UnauthorizedAccessException。 如果您已忽略將適當的功能加入應用程式資訊清單,則也會在開發期間看到此例外狀況。

重要事項 在一些裝置系列上,會先向使用者顯示使用者同意提示,然後才會將裝置相機的存取權授予您的應用程式。 基於這個理由,您只能從主要 UI 執行緒呼叫 MediaCapture.InitializeAsync。 嘗試從另一個執行緒初始化相機,可能會導致初始化失敗。

注意

Windows 可讓使用者在 [隱私權與安全性 -> 相機] 底下的 [Windows 設定] 應用程式中授與或拒絕裝置相機的存取權。 初始化擷取裝置時,應用程式應該檢查他們是否可以存取相機,並處理使用者拒絕存取的情況。 如需詳細資訊,請參閱 處理 Windows 相機隱私權設定

MediaCapture 連線到 CaptureElement,方法是設定 Source 屬性。 呼叫 StartPreviewAsync 來啟動預覽。 如果另一 個應用程式具有攝錄裝置的獨佔控制權,這個方法將會擲回 FileLoadException。 如需接聽獨佔控制權變更的資訊,請參閱下一節。

呼叫 RequestActive 以確定裝置在執行預覽時不會進入睡眠狀態。 最後,將 DisplayInformation.AutoRotationPreferences 屬性設定為 Landscape,以防止使用者變更裝置方向時,UI 和 CaptureElement 跟著旋轉。 如需處理裝置方向變更的詳細資訊,請參閱使用 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;
           }

       }

處理獨佔控制權中的變更

如上一節所述,如果另一個應用程式擁有攝錄裝置的獨佔控制權,StartPreviewAsync 將會擲回 FileLoadException。 從 Windows 10 版本 1703 開始,您可以註冊 MediaCapture.CaptureDeviceExclusiveControlStatusChanged 事件的處理常式,每當裝置的獨佔控制權狀態變更時,就會引發此事件。 在此事件的處理常式中,檢查 MediaCaptureDeviceExclusiveControlStatusChangedEventArgs.Status 屬性以查看目前的狀態。 如果新的狀態為 SharedReadOnlyAvailable,則您知道您目前無法啟動預覽,而您可能想要更新 UI 以對使用者發出警示。 如果新狀態為 ExclusiveControlAvailable,您可以再次嘗試啟動相機預覽。

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

關閉預覽串流

當您使用預覽串流完畢時,應該一律關閉串流並適當處置相關聯的資源,以確保相機可供裝置上的其他應用程式使用。 關閉預覽串流所需的步驟如下:

  • 如果相機目前正在預覽,請呼叫 StopPreviewAsync 以停止預覽串流。 如果您在預覽未執行時呼叫 StopPreviewAsync,將會擲回例外狀況。
  • CaptureElementSource 屬性設定為 null。 使用 CoreDispatcher.RunAsync 確定此呼叫是在 UI 執行緒上執行。
  • 呼叫 MediaCapture 物件的 Dispose 方法以釋放物件。 再一次,使用 CoreDispatcher.RunAsync 確定此呼叫是在 UI 執行緒上執行。
  • MediaCapture 成員變數設定為 null。
  • 呼叫 RequestRelease 以允許在非使用中時關閉畫面。
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;
        });
    }
    
}

當使用者覆寫 OnNavigatedFrom 方法離開頁面時,您應該關閉預覽串流。

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

當您的應用程式暫停時,您也應該妥善地關閉預覽串流。 若要這樣做,請在頁面建構函式中註冊 Application.Suspending 事件的處理常式。

public MainPage()
{
    this.InitializeComponent();

    Application.Current.Suspending += Application_Suspending;
}

Suspending 事件處理常式中,先檢查以確定頁面正藉由比較頁面類型與 CurrentSourcePageType 屬性,來顯示應用程式的 Frame。 如果頁面目前未顯示,則應該已引發 OnNavigatedFrom 事件,且已關閉預覽串流。 如果目前正在顯示頁面,請從傳遞至處理常式的事件引數取得 SuspendingDeferral 物件,以確保系統在預覽串流關閉之前不會暫停您的應用程式。 串流關閉之後,請呼叫延遲 deferral 的 Complete 方法,讓系統繼續暫停您的應用程式。

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