Windows。Graphics.Capture命名空間提供 API,用於從顯示視窗或應用程式視窗擷取影格,建立影片串流或快照,以建立協作且互動的體驗。
透過螢幕擷取,開發者會啟動安全的系統介面,讓終端使用者選擇要擷取的顯示或應用程式視窗,系統會在被擷取項目周圍畫上黃色通知邊框。 若有多次同時捕獲,則會在每個被捕獲物品周圍畫出黃色邊框。
Note
螢幕擷取 API 僅支援 Windows 桌面裝置及 Windows Mixed Reality 沉浸式頭戴裝置。
本文說明如何擷取顯示視窗或應用程式視窗的單一影像。
檢查螢幕擷取支援
在嘗試擷取前,請確認目前裝置是否支援螢幕擷取。 在 GraphicsCaptureSession 使用 IsSupported 方法來判斷是否有螢幕擷取功能:
// 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;
}
螢幕擷取可能不被支援的原因有多種,包括裝置不符合硬體需求。
啟動系統介面開始螢幕擷取
使用 GraphicsCapturePicker 類別來呼叫系統選擇器的使用者介面。 最終使用者使用此介面選擇要擷取的顯示視窗或應用程式視窗。 選擇器回傳一個 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 建立 一個 Direct3D11CaptureFramePool ,包含你的 D3D 裝置、支援的像素格式(DXGI_FORMAT_B8G8R8A8_UNORM)、所需影格數(可為任意整數)及影格大小。 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 內容時,建議對擷取管線中的每個元件使用 DXGI_FORMAT_R16G16B16A16_FLOAT ,包括 Direct3D11CaptureFramePool,目標目標如 CanvasBitmap。 視需求而定,可能需要額外處理,例如儲存為 HDR 內容格式或 HDR 轉 SDR 色調映射。 本文聚焦於SDR內容擷取。 欲了解更多資訊,請參閱 使用 DirectX 搭配高動態範圍顯示器及進階色彩。
一旦使用者明確同意在系統介面中擷取應用程式視窗或顯示, GraphicsCaptureItem 即可與多個擷取會話關聯。 這樣你的應用程式就能選擇捕捉同一個項目,用於不同的體驗。
擷取擷取幀
當你的幀池和擷取會話建立好後,請在你的 GraphicsCaptureSession 實例上呼叫 StartCapture,通知系統開始將擷取幀傳送到你的應用程式。
要取得這些擷取框架,也就是 Direct3D11CaptureFrame 物件,請使用 Direct3D11CaptureFramePool.FrameArrived 事件:
// Handle frame arrival
_framePool.FrameArrived += OnFrameArrived;
建議避免在 FrameArrived 的 UI 執行緒上做大量工作,因為這個事件會在每次有新幀可用時觸發。 如果你選擇在 UI 執行緒上監聽 FrameArrived 事件,請留意每次事件觸發時所執行的工作量。
或者,你也可以用 Direct3D11CaptureFramePool.TryGetNextFrame 的方法手動拉取影格,直到你取得所有需要的影格。
Direct3D11CaptureFrame物件包含ContentSize、Surface 以及 SystemRelativeTime。 SystemRelativeTime 是 QPC(QueryPerformanceCounter)時間,可用於同步其他媒體元素。
程序擷取幀
呼叫 TryGetNextFrame 時會檢查 Direct3D11CaptureFramePool 中的每一幀,並依 Direct3D11CaptureFrame 物件的壽命重新檢查。 對於受管理的應用程式,建議使用 Direct3D11CaptureFrame.Dispose 方法。 Direct3D11CaptureFrame 實作了 IDisposable 介面,因此處置該框架會將緩衝區歸還至集區。
應用程式不應儲存對 Direct3D11CaptureFrame 物件的參考,也不應在畫面重新簽入後儲存底層 Direct3D 表面的參考資料。
在這個範例中,每一幀都被轉換成 CanvasBitmap,這是 Win2D 函式庫的一部分:
// Convert frame to Win2D bitmap and display
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
_canvasDevice,
frame.Surface);
底層 Direct3D 表面的大小一律為建立(或重新建立)Direct3D11CaptureFramePool時所指定的大小。 如果內容大於框,內容會被裁切至框的大小。 如果內容小於幀數,則幀的其餘部分包含未定義的資料。 建議應用程式用 ContentSize 屬性複製出該 Direct3D11CaptureFrame 的子矩形,以避免顯示未定義內容。
儲存截圖
有了 CanvasBitmap 後,你可以將其儲存為圖片檔案。 以下範例使用 FileSavePicker,將目前畫面儲存為 PNG 檔案。 在 WinUI 3 應用程式中,你必須用視窗 handle 初始化選擇器:
// 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 方法。
當呼叫 Recreate 時,所有現有的框架都會被丟棄。 這是為了避免將其底層 Direct3D 表面所屬的裝置交付給應用程式,而該應用程式可能已無法再存取這些裝置。 因此,在呼叫 Recreate 之前,先處理所有待處理的影格可能是明智的做法。
WinUI 3 注意事項
在將螢幕截圖程式碼從 UWP 遷移到 WinUI 3(Windows 應用程式 SDK)時,請注意以下差異:
視窗控制代碼初始化 — 例如 和GraphicsCapturePicker 這類選擇器,必須使用FileSavePicker 以視窗控制代碼進行初始化。 更多資訊請參見 「擷取視窗指柄(HWND)」。InitializeWithWindow - UI 執行緒上的合成 API — 在 WinUI 3 中, CanvasComposition 表面操作(例如繪圖到 CompositionDrawingSurface)必須使用 DispatcherQueue.TryEnqueue 派遣到 UI 執行緒。 框架擷取與點陣圖建立可以在框架池的背景執行緒進行,但更新合成視覺化必須在 UI 執行緒上進行。
-
命名空間變更 — 使用
Microsoft.UI.Composition、Microsoft.UI.Xaml.Hosting和Microsoft.UI.Dispatching,而非其對應的Windows.UI。Windows.Graphics.Capture命名空間保持不變。