共用方式為


在 WinUI 3 應用程式中顯示相機預覽

在這個快速入門中,你將學習如何建立一個基本的 WinUI 相機應用程式,用來顯示相機預覽。 在 WinUI 應用程式中,你可以使用 Microsoft.UI.Xaml.Controls 命名空間中的 MediaPlayerElement 控制項來渲染相機預覽,並用 WinRT 類別 MediaCapture 來access裝置的相機預覽串流。 MediaCapture 提供 API 來執行各種不同的相機相關工作,例如擷取相片和視訊,以及設定相機的裝置驅動程式。 如需其他 MediaCapture 功能的詳細資訊,請參閱本節中的其他文章。

本攻略中的程式碼改編自 github 上的 MediaCapture WinUI 範例。

先決條件

  • 您的裝置必須啟用開發人員模式。 如需詳細資訊,請參閱 開發人員的設定
  • Visual Studio 2026 或更新版本,搭配 WinUI 應用程式開發 工作負載。

建立一個新的 WinUI 應用程式

在 Visual Studio 裡,建立一個新 project。 在 「建立新專案 」對話框中,將語言篩選器設為「C#」,平台篩選器設為「Windows」,然後選擇「WinUI Blank App (Packaged)」專案範本。

建立 UI

此範例的簡單 UI 包含用來顯示相機預覽的 MediaPlayerElement 控件、可讓您從裝置相機選取的 ComboBox ,以及初始化 MediaCapture 類別、啟動和停止相機預覽,以及重設範例的按鈕。 我們也包含用來顯示狀態消息的 TextBlock

在你project的 MainWindow.xml 檔案中,將預設的 StackPanel 控制項替換成以下 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>

更新MainWindow類別定義

本文其餘程式碼會加入你project MainWindow.xaml.cs檔案中的 MainWindow 類別定義中。 首先,新增一些類別變數,以在視窗的存留期內保存。 這些變數包括:

  • 將為每個可用相機儲存 DeviceInformation 物件的 DeviceInformationCollectionDeviceInformation 物件會傳達資訊,例如相機的唯一標識碼和易記名稱。
  • MediaCapture 物件,可處理與所選相機驅動程序的互動,並可讓您擷取相機的視訊串流。
  • MediaFrameSource 物件,代表媒體畫面的來源,例如視訊數據流。
  • 布爾值,用來追蹤相機預覽執行時機。 預覽執行時,某些相機設定無法變更,因此最好追蹤相機預覽的狀態。
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

填入可用的相機清單

接下來,我們將建立協助程式方法來偵測目前裝置上存在的相機,並使用相機名稱填入 UI 中的 ComboBox ,讓使用者選取要預覽的相機。 DeviceInformation.FindAllAsync 可讓您查詢許多不同類型的裝置。 我們使用 MediaDevice.GetVideoCaptureSelector 來擷取標識符,指定我們只想要擷取視訊擷取裝置。

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

將這個協助程式方法的呼叫新增至 MainWindow 類別建構函式,以便在視窗載入時填入 ComboBox

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

初始化 MediaCapture 物件

呼叫 InitializeAsync 來初始化 MediaCapture 物件,並傳入包含所要求初始化參數的 MediaCaptureInitializationSettings 物件。 有許多可啟用不同案例的選擇性初始化參數。 如需完整清單,請參閱 API 參考頁面。 在此簡單範例中,我們會指定一些基本設定,包括:

  • VideoDeviceId 屬性會指定 MediaCapture 要附加之相機的唯一標識符。 我們會使用 ComboBox 的選取索引,從 DeviceInformationCollection 取得裝置標識符。
  • SharingMode 屬性指定應用程式是向攝影機請求共享的唯讀存取,讓你可以觀看並擷取影片串流,或者是要求對攝影機的獨家控制,以便更改攝影機設定。 多個應用程式可以同時從相機讀取,但一次只有一個應用程式可以擁有獨佔控制權。
  • StreamingCaptureMode 屬性會指定我們想要擷取視訊、音訊或音訊和視訊。
  • MediaCaptureMemoryPreference 可讓我們要求特別針對視訊畫面使用 CPU 記憶體。 [ 自動 ] 值可讓系統在可用時使用 GPU 記憶體。

在初始化 MediaCapture物件之前,我們會呼叫 AppCapability.CheckAccess 方法來判斷使用者是否在 Windows 設定中拒絕了我們的應用程式access給相機。

備註

Windows 允許使用者在 Windows 設定中授予或拒絕攝影機的使用權限,位於隱私與安全 -> 攝影機。 在初始化擷取裝置時,應用程式應檢查是否擁有存取攝影機的權限,並處理使用者拒絕存取的情況。 如需詳細資訊,請參閱 處理 Windows 相機隱私權設定

InitializeAsync 呼叫是從 try 區塊內部進行,以便在初始化失敗時復原。 應用程式應該正常處理初始化失敗。 在此簡單範例中,我們只會在失敗時顯示錯誤訊息。

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

初始化相機預覽

當使用者按 [開始預覽] 按鈕時,我們會嘗試從用初始化 MediaCapture 物件的相機裝置建立MediaFrameSource來取得視訊串流。 可用的畫面來源會由 MediaCapture.FrameSources 屬性公開。

若要尋找一個色彩視訊數據的畫面來源,例如相較於深度相機的畫面來源,我們會尋找具有 SourceKindColor 的畫面來源。 某些相機驅動程式會提供與記錄串流分開的專用預覽串流。 若要取得預覽視訊串流,我們會嘗試選取具有 MediaStreamTypeVideoPreview 的畫面來源。 如果找不到預覽串流,我們可以選取 VideoRecordMediaStreamType 來取得錄製視訊串流。 如果這兩個畫面來源都無法使用,則此擷取裝置無法用於影片預覽。

選取畫面來源之後,我們會建立新的 MediaPlayer 物件,由 UI 中的 MediaPlayerElement 轉譯。 我們將 MediaPlayerSource 屬性設定為我們從選取的 MediaFrameSource 建立的新 MediaSource 物件。

MediaPlayer 物件上呼叫 Play,開始轉譯視訊串流。

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

實作 MediaFailed 事件的處理程式,以便處理轉譯預覽的錯誤。

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

停止相機預覽

若要停止相機預覽,請在 MediaPlayer 物件上呼叫 Pause

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

重設應用程式

若要更輕鬆地測試範例應用程式,請新增方法來重設應用程式的狀態。 當不再需要相機時,相機應用程式應該一律處置相機和相關資源。

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

}