画面の取り込み

Windows。Graphics.Capture 名前空間は、ディスプレイまたはアプリケーション ウィンドウからフレームを取得する API を提供し、ビデオ ストリームやスナップショットを作成して、コラボレーションと対話型のエクスペリエンスを構築します。

画面キャプチャを使用すると、開発者はセキュリティで保護されたシステム UI を呼び出して、エンド ユーザーがキャプチャする表示ウィンドウまたはアプリケーション ウィンドウを選択し、アクティブにキャプチャされた項目の周囲に黄色の通知境界線がシステムによって描画されます。 複数の同時キャプチャ セッションの場合、キャプチャされる各項目の周囲に黄色の境界線が描画されます。

Note

画面キャプチャ API は、Windowsデスクトップ デバイスとWindows Mixed Realityイマーシブ ヘッドセットでのみサポートされます。

この記事では、表示ウィンドウまたはアプリケーション ウィンドウの 1 つの画像をキャプチャする方法について説明します。

画面キャプチャのサポートを確認する

キャプチャを試みる前に、現在のデバイスが画面キャプチャをサポートしているかどうかを確認します。 GraphicsCaptureSessionIsSupported メソッドを使用して、画面キャプチャが使用可能かどうかを判断します。

// Check if screen capture is supported
if (!GraphicsCaptureSession.IsSupported())
{
    // Hide capture UI
    CaptureControlsPanel.Visibility = Visibility.Collapsed;
    PreviewPlaceholderText.Text = "Screen capture isn't supported on this device.";
    UpdateStatus("Screen capture isn't supported on this device.");
    return;
}

デバイスがハードウェア要件を満たしていない場合など、画面キャプチャがサポートされない理由はいくつかあります。

システム UI を起動して画面キャプチャを開始する

GraphicsCapturePicker クラスを使用して、システム ピッカー UI を呼び出します。 エンド ユーザーはこの UI を使用して、キャプチャする表示ウィンドウまたはアプリケーション ウィンドウを選択します。 ピッカーは、キャプチャ セッションの作成に使用される GraphicsCaptureItem を 返します。

WinUI 3 アプリでは、 PickSingleItemAsync を呼び出す前に、ウィンドウ ハンドルを使用してピッカーを初期化する必要があります。

// Launch picker and start capture
var picker = new GraphicsCapturePicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
GraphicsCaptureItem item = await picker.PickSingleItemAsync();
if (item != null)
{
    StartCaptureInternal(item);
}

キャプチャ フレーム プールとキャプチャ セッションを作成する

GraphicsCaptureItem を使用して、D3D デバイス、サポートされているピクセル形式 (DXGI_FORMAT_B8G8R8A8_UNORM)、必要なフレームの数 (任意の整数)、およびフレーム サイズを使用して Direct3D11CaptureFramePool を作成します。 GraphicsCaptureItem クラスの Size プロパティは、フレームのサイズとして使用できます。

// Create frame pool and capture session
_framePool = Direct3D11CaptureFramePool.Create(
    _canvasDevice,
    CaptureDirectXPixelFormat.B8G8R8A8UIntNormalized,
    BufferCount,
    item.Size);
_session = _framePool.CreateCaptureSession(item);

Note

Windows HD カラーが有効になっているシステムでは、コンテンツピクセル形式が必ずしもDXGI_FORMAT_B8G8R8A8_UNORMであるとは限りません。 HDR コンテンツをキャプチャするときにピクセル オーバークリップ (つまり、キャプチャされたコンテンツが洗い流されたように見える) を回避するには、キャプチャ パイプライン内のすべてのコンポーネント (CanvasBitmap などのターゲットの宛先である Direct3D11CaptureFramePool を含む) に対してDXGI_FORMAT_R16G16B16A16_FLOATを使用することを検討してください。 必要に応じて、HDR コンテンツ形式への保存や HDR から SDR へのトーン マッピングなどの追加処理が必要になる場合があります。 この記事では、SDR コンテンツのキャプチャに重点を置いています。 詳細については、「 ダイナミック レンジの高いディスプレイと高度な色で DirectX を使用する」を参照してください。

ユーザーがアプリケーション ウィンドウのキャプチャまたはシステム UI での表示に明示的に同意すると、 GraphicsCaptureItem を複数のキャプチャ セッションに関連付けることができます。 これにより、アプリケーションは、さまざまなエクスペリエンスに対して同じ項目をキャプチャすることを選択できます。

キャプチャ フレームを取得する

フレーム プールとキャプチャ セッションが作成されたら、GraphicsCaptureSession インスタンスで StartCapture を呼び出して、キャプチャ フレームのアプリへの送信を開始するようシステムに通知します。

Direct3D11CaptureFrame オブジェクトであるこれらのキャプチャ フレームを取得するには、Direct3D11CaptureFramePool.FrameArrived イベントを使用します。

// Handle frame arrival
_framePool.FrameArrived += OnFrameArrived;

このイベントは新しいフレームが使用可能なたびに発生するため、 FrameArrived の UI スレッドで大量の作業を行わないようにすることをお勧めします。 UI スレッドで FrameArrived をリッスンする場合は、イベントが発生するたびに実行している作業の量に注意してください。

または、必要なすべてのフレームを取得するまで 、Direct3D11CaptureFramePool.TryGetNextFrame メソッドを使用して手動でフレームをプルすることもできます。

Direct3D11CaptureFrame オブジェクトには、プロパティ ContentSizeSurface、および SystemRelativeTime が含まれています。 SystemRelativeTime は、他のメディア要素の同期に使用できる QPC (QueryPerformanceCounter) 時刻です。

プロセス キャプチャ フレーム

Direct3D11CaptureFramePool の各フレームは、TryGetNextFrame を呼び出すときにチェックアウトされ、Direct3D11CaptureFrame オブジェクトの有効期間に従ってチェックインされます。 マネージド アプリケーションの場合は、 Direct3D11CaptureFrame.Dispose メソッドを使用することをお勧めします。 Direct3D11CaptureFrameIDisposable を実装するため、フレームを破棄するとバッファーがプールに返されます。

アプリケーションは 、Direct3D11CaptureFrame オブジェクトへの参照を保存したり、フレームがチェックインされた後に基になる Direct3D サーフェスへの参照を保存したりしないでください。

この例では、各フレームは、Win2D ライブラリの一部である CanvasBitmap に変換されます。

// Convert frame to Win2D bitmap and display
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
    _canvasDevice,
    frame.Surface);

基になる Direct3D サーフェスは常に、 Direct3D11CaptureFramePool を作成 (または再作成) するときに指定されたサイズです。 コンテンツがフレームより大きい場合、コンテンツはフレームのサイズにクリップされます。 コンテンツがフレームよりも小さい場合、フレームの残りの部分には未定義のデータが含まれます。 未定義のコンテンツが表示されないように、アプリケーションでは、その Direct3D11CaptureFrameContentSize プロパティを使用してサブレクトをコピーアウトすることをお勧めします。

スクリーンショットを保存する

CanvasBitmap を作成したら、イメージ ファイルとして保存できます。 次の例では、FileSavePicker を使用して、現在のフレームを PNG ファイルとして保存します。 WinUI 3 アプリでは、ウィンドウ ハンドルを使用してピッカーを初期化する必要があります。

// Save screenshot
var savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
savePicker.SuggestedFileName = "screen-capture";
savePicker.FileTypeChoices.Add("PNG image", new List<string> { ".png" });
WinRT.Interop.InitializeWithWindow.Initialize(savePicker, _hwnd);
StorageFile? file = await savePicker.PickSaveFileAsync();
if (file is not null)
{
    using var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite);
    await frameToSave.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
}

キャプチャ項目のサイズ変更またはデバイスの喪失に反応する

キャプチャ プロセス中に、アプリケーションは Direct3D11CaptureFramePool の側面を変更したい場合があります。 これには、新しい Direct3D デバイスの提供、フレーム バッファーのサイズの変更、プール内のバッファー数の変更も含まれます。 これらの各シナリオでは、Direct3D11CaptureFramePool オブジェクトの Recreate メソッドが推奨される方法です。

再作成が呼び出されると、既存のすべてのフレームが破棄されます。 これは、基になる Direct3D サーフェスが、アプリケーションがアクセスできなくなったデバイスに属しているフレームを渡さないようにするためです。 このため、再作成を呼び出す前に、保留中のすべてのフレームを処理することをお勧 めします

WinUI 3 に関する考慮事項

UWP から WinUI 3 (Windows アプリ SDK) に画面キャプチャ コードを移行する場合は、次の点に注意してください。

  • ウィンドウ ハンドルの初期化GraphicsCapturePickerFileSavePicker などのピッカーは、InitializeWithWindow を使用してウィンドウ ハンドルを指定して初期化する必要があります。 詳細については、「 ウィンドウ ハンドルの取得 (HWND)」を参照してください。
  • UI スレッド上のコンポジション API — WinUI 3 では、 CanvasComposition サーフェス操作 ( CompositionDrawingSurface への描画など) を DispatcherQueue.TryEnqueue を使用して UI スレッドにディスパッチする必要があります。 フレーム キャプチャとビットマップの作成は、フレーム プールのバックグラウンド スレッドで行うことができますが、コンポジション ビジュアルの更新は UI スレッドで行う必要があります。
  • Namespace changes — 対応する Microsoft.UI.Composition の代わりに、Microsoft.UI.Xaml.HostingMicrosoft.UI.Dispatching、および Windows.UI を使用します。 Windows.Graphics.Capture 名前空間は変更されません。

こちらも参照ください