次の方法で共有


WinUI 3 アプリでカメラプレビューを表示する

このクイック スタートでは、カメラ プレビューを表示する基本的な WinUI 3 カメラ アプリを作成する方法について説明します。 WinUI 3 アプリでは、Microsoft.UI.Xaml.Controls 名前空間の MediaPlayerElement コントロールを使用して、カメラ プレビューをレンダリングし、WinRT クラス MediaCapture を使用してデバイスのカメラ プレビュー ストリームにアクセスします。 MediaCapture には、写真やビデオのキャプチャ、カメラのデバイス ドライバーの構成など、さまざまなカメラ関連のタスクを実行するための API が用意されています。 他の MediaCapture 機能の詳細については、このセクションの他の記事を参照してください。

このチュートリアルのコードは、 GitHub の MediaCapture WinUI 3 サンプルから調整されています

ヒント

この記事の UWP バージョンについては、UWP ドキュメント のカメラ プレビューの表示 を参照してください。

[前提条件]

  • デバイスで開発者モードが有効になっている必要があります。 詳細については、「 開発者向けの設定」を参照してください。
  • Visual Studio 2022 以降、WinUI アプリケーション開発 ワークロード。

新しい WinUI 3 アプリを作成する

Visual Studio で、新しいプロジェクトを作成します。 [ 新しいプロジェクトの作成 ] ダイアログで、言語フィルターを "C#" に設定し、プラットフォーム フィルターを "Windows" に設定し、"空のアプリ、パッケージ (デスクトップの WinUI 3)" プロジェクト テンプレートを選択します。

UI を作成する

この例の単純な UI には、カメラ プレビューを表示するための MediaPlayerElement コントロール、デバイスのカメラから選択できる ComboBoxMediaCapture クラスの初期化、カメラ プレビューの開始と停止、サンプルのリセットのためのボタンが含まれます。 ステータス メッセージを表示するための TextBlock も含まれています。

プロジェクトの 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 クラス定義を更新する

この記事の残りのコードは、プロジェクトの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;
    }
}

ウィンドウの読み込み時に ComboBox が設定されるように、このヘルパー メソッドの呼び出しを MainWindow クラス コンストラクターに追加します。

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

MediaCapture オブジェクトを初期化する

要求された初期化パラメーターを含む MediaCaptureInitializationSettings オブジェクトを渡して、InitializeAsync を呼び出して MediaCapture オブジェクトを初期化します。 さまざまなシナリオを可能にする多くの省略可能な初期化パラメーターがあります。 完全な一覧については、API リファレンス ページを参照してください。 この簡単な例では、次のようないくつかの基本設定を指定します。

  • VideoDeviceId プロパティは、MediaCapture がアタッチするカメラの一意識別子を指定します。 ComboBox の選択したインデックスを使用して、DeviceInformationCollection からデバイス ID を取得します。
  • SharingMode プロパティは、アプリがカメラへの共有、読み取り専用のアクセスを要求しているかどうかを指定します。これにより、ビデオ ストリームから表示およびキャプチャしたり、カメラの排他的制御を行ったりして、カメラの構成を変更できます。 複数のアプリが同時にカメラから読み取ることができますが、一度に排他的に制御できるアプリは 1 つだけです。
  • StreamingCaptureMode プロパティは、ビデオ、オーディオ、またはオーディオとビデオのどちらをキャプチャするかを指定します。
  • MediaCaptureMemoryPreference を使用すると、ビデオ フレームに CPU メモリを特に使用するように要求できます。 値 Auto を使用すると、使用可能な場合、システムは GPU メモリを使用できます。

MediaCapture オブジェクトを初期化する前に、AppCapability.CheckAccess メソッドを呼び出して、ユーザーが Windows 設定でカメラへのアクセスを拒否したかどうかを判断します。

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 プロパティによって公開されます。

深度カメラとは対照的に、カラー ビデオ データであるフレーム ソースを検索するには、 SourceKindを持つフレーム ソースを探します。 一部のカメラ ドライバーは、レコード ストリームとは別の専用のプレビュー ストリームを提供します。 プレビュー ビデオ ストリームを取得するには、VideoPreviewMediaStreamType を持つフレーム ソースを選択します。 プレビュー ストリームが見つからない場合は、 VideoRecord の MediaStreamType を選択して、 レコード ビデオ ストリームを取得できます。 これらのフレーム ソースのいずれも使用できない場合、このキャプチャ デバイスをビデオ プレビューに使用することはできません。

フレーム ソースを選択したら、UI で MediaPlayerElement によってレンダリングされる新しい MediaPlayer オブジェクトを作成します。 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();

}