Windows Graphics Capture (WGC) 屏幕捕获简介

Windows Graphics Capture (WGC) 屏幕捕获简介

简介

Windows.Graphics.Capture 是微软在 Windows 10 1903 新增的一套官方"屏幕捕获(屏幕录制)API"。它是 WinRT API(Windows Runtime),可以在 C++、C#、Rust 等语言中使用。

相比传统的 GDI 截屏、DXGI Desktop Duplication,WGC 具有以下优势:

  • 官方支持,兼容性好
  • 性能优秀,GPU 加速
  • 原生支持HDR
  • 支持窗口和显示器捕获

WGC 完整使用流程

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    WGC 捕获流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 创建 D3D11 Device                                       │
│     D3D11CreateDevice() + BGRA_SUPPORT                      │
│              ↓                                              │
│  2. 获取捕获目标                                             │
│     GraphicsCaptureItem (窗口/显示器)                        │
│              ↓                                              │
│  3. 创建 FramePool                                          │
│     Direct3D11CaptureFramePool::Create()                    │
│              ↓                                              │
│  4. 创建 CaptureSession                                     │
│     framePool.CreateCaptureSession(item)                    │
│              ↓                                              │
│  5. 注册帧回调                                               │
│     framePool.FrameArrived += OnFrameArrived                │
│              ↓                                              │
│  6. 开始捕获                                                 │
│     session.StartCapture()                                  │
│              ↓                                              │
│  7. 处理帧数据                                               │
│     frame = framePool.TryGetNextFrame()                     │
│              ↓                                              │
│  8. 停止捕获                                                 │
│     session.Close() / framePool.Close()                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基础概念 Q&A

Win32 与 WinRT 的区别

Win32 WinRT
Windows 最传统、最基础的系统 API 现代 Windows Runtime API
使用 C/C++ 接口,C# 可通过 P/Invoke 调用 可使用 C++/WinRT、C# 调用
控制台程序、DLL 项目默认是 Win32 Win32 程序中可使用 C++/WinRT 调用 WinRT
具备系统级能力,权限最大 有沙箱机制,不能做强力系统操作

WPF 与 UWP 的区别

  • WPF(Windows Presentation Foundation):基于 .NET、XAML,用于构建桌面 UI 的 C# 框架
  • UWP(Universal Windows Platform):使用 WinRT 作为底层的一种 App 类型,主要使用 C# 开发

核心组件详解

CaptureSession - 捕获生命周期控制器

使用 WGC 的重要步骤之一就是创建 CaptureSession,其功能是控制捕获的生命周期,是捕获的遥控器。

主要功能:

  • 开始捕获 StartCapture()
  • 停止捕获 Close()
  • 设置是否显示红框 IsCursorCaptureEnabled
  • 指定捕获目标(窗口/桌面)

Direct3D11CaptureFramePool - 帧数据入口

Direct3D11CaptureFramePool 是 WGC 捕获帧的唯一入口,用户只能通过它来获取 WGC 捕获的帧。

注意:虽然名字中有 Direct3D11,但它是 WGC 的组件,不是 D3D11 的。之所以叫这个名字是因为输出帧是 D3D11 Texture。

核心特性:

  • 这是一个 FIFO 队列
  • 队列满时,新帧会被丢弃
  • 保证系统捕获线程永远不会被用户代码阻塞

WGC 捕获数据流:

复制代码
系统捕获到新帧
       ↓
FramePool 内部存储该帧(最多 N 帧)
       ↓
触发 FrameArrived 回调
       ↓
用户调用 TryGetNextFrame() 获取帧

FramePool 的两种创建方式

方法 特点 适用场景
Create() 需要 DispatcherQueue,FrameArrived 在当前线程触发 有 UI 消息循环的应用
CreateFreeThreaded() 不需要 DispatcherQueue,回调在 FramePool 内部线程触发 后台服务、无 UI 应用

CreateFreeThreaded 注意事项:

  • 如果回调函数耗时,不会影响系统底层 GPU 生成帧
  • 但会导致丢帧,影响捕获结果

GraphicsCaptureItem - 捕获目标句柄

GraphicsCaptureItem 表示你想让 Windows 进行屏幕捕获的目标对象,可以是:

  • 一个窗口(HWND)
  • 一个显示器(Monitor)
  • 一个虚拟桌面区域(Windows 11)
  • 一个 AppWindow(WinUI)

它不是纹理,不是帧,不是画面本身。它是一个 WinRT 对象(WinRT COM),用于告诉 WGC 你想捕获什么。

IDXGISwapChain3 - 交换链接口

交换链(SwapChain)是 Direct3D 应用用来:

  • 存放渲染的帧(BackBuffer)
  • 将 BackBuffer 显示到屏幕(Present)
  • 管理帧之间的切换(swap / flip)

如果不需要将捕获的帧渲染出来,就不需要 IDXGISwapChain3。

窗口录制 vs 显示器录制

特性 窗口录制 显示器录制
捕获范围 单个窗口客户区 整个显示器
性能开销 较低 较高
DPI 感知 自动处理 需要处理
隐私保护 仅捕获窗口内容 捕获所有可见内容
适用场景 单应用录制、远程桌面 全屏录制、直播推流

窗口大小变化处理:

当捕获的窗口大小改变时,需要重建 FramePool:

cpp 复制代码
void OnItemClosed(GraphicsCaptureItem const&, IInspectable const&)
{
    // 窗口关闭时的处理
}

// 监听窗口大小变化
item.Closed({ this, &OnItemClosed });

// 窗口大小变化时需要:
// 1. 关闭旧的 FramePool
// 2. 使用新的尺寸创建 FramePool
// 3. 重新创建 CaptureSession

帧率控制与丢帧处理

FramePool 缓冲区大小

cpp 复制代码
// 创建 FramePool 时指定缓冲区大小
auto framePool = Direct3D11CaptureFramePool::Create(
    device,
    DirectXPixelFormat::B8G8R8A8UIntNormalized,
    2,  // 缓冲区大小:建议 2-3 帧
    item.Size()
);

缓冲区大小选择建议:

  • 1 帧:最低延迟,但容易丢帧
  • 2-3 帧:平衡延迟和稳定性,推荐
  • 4+ 帧:更稳定,但延迟增加

丢帧原因分析

原因 解决方案
回调处理时间过长 优化帧处理逻辑,减少回调内工作量
缓冲区过小 适当增加 FramePool 缓冲区大小
GPU 负载过高 降低分辨率或帧率
内存不足 及时释放帧资源,避免内存累积

帧率限制方案

如果需要限制捕获帧率(如 30fps),可以在回调中控制:

cpp 复制代码
std::chrono::steady_clock::time_point lastFrameTime;
const auto frameInterval = std::chrono::milliseconds(33); // ~30fps

void OnFrameArrived(Direct3D11CaptureFramePool const& sender, IInspectable const&)
{
    auto now = std::chrono::steady_clock::now();
    if (now - lastFrameTime < frameInterval)
    {
        // 跳过此帧
        auto frame = sender.TryGetNextFrame();
        return; // 帧会自动释放
    }
    lastFrameTime = now;
    
    // 正常处理帧
    auto frame = sender.TryGetNextFrame();
    ProcessFrame(frame);
}

高级特性

Dirty Region 机制

Dirty Region(脏矩形):每一帧窗口可能只有一部分变化,这些变化的区域就叫 Dirty Region。

通过 TryGetNextFrame() 拿到的永远是一张完整的 2D 纹理。开启 Dirty Region 后,系统会通过其他字段告诉你哪些地方发生了变化,你可以选择是否使用这个信息进行优化。

HDR 兼容性

在创建 FramePool 时指定格式:

cpp 复制代码
// SDR 格式(推荐,兼容性好)
DirectXPixelFormat::B8G8R8A8UIntNormalized

// HDR 格式
DirectXPixelFormat::R16G16B16A16Float

如果捕获 HDR 内容但指定 SDR 格式,WGC 会在内部进行格式转换,你拿到的帧永远是创建时指定的格式。

D3D11 Device 创建详解

D3D11CreateDevice 函数原型

cpp 复制代码
HRESULT D3D11CreateDevice(
    IDXGIAdapter* pAdapter,         // 显卡适配器,nullptr = 默认
    D3D_DRIVER_TYPE DriverType,     // 驱动类型
    HMODULE Software,               // 软件光栅化 DLL,通常 nullptr
    UINT Flags,                     // 创建标志
    const D3D_FEATURE_LEVEL* pFeatureLevels, // Feature Level 列表
    UINT FeatureLevels,             // Feature Level 数量
    UINT SDKVersion,                // SDK 版本,固定 D3D11_SDK_VERSION
    ID3D11Device** ppDevice,        // 输出设备
    D3D_FEATURE_LEVEL* pFeatureLevel, // 输出实际 Feature Level
    ID3D11DeviceContext** ppImmediateContext // 输出上下文
);

驱动类型

枚举值 说明
D3D_DRIVER_TYPE_HARDWARE 使用 GPU 硬件加速(正常使用)
D3D_DRIVER_TYPE_WARP CPU 模拟 GPU,无 GPU 时保证可用
D3D_DRIVER_TYPE_REFERENCE 官方参考驱动,调试用,极慢

创建标志

Flag 作用
D3D11_CREATE_DEVICE_BGRA_SUPPORT 支持 BGRA 格式,WGC 必须开启
D3D11_CREATE_DEVICE_VIDEO_SUPPORT 启用视频处理能力(颜色转换、缩放等)
D3D11_CREATE_DEVICE_DEBUG 启用 D3D 调试层

推荐组合:

cpp 复制代码
auto flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT 
           | D3D11_CREATE_DEVICE_VIDEO_SUPPORT;

示例代码

cpp 复制代码
winrt::com_ptr<ID3D11Device> CreateD3D11Device()
{
    winrt::com_ptr<ID3D11Device> device;
    UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
    // 优先使用硬件加速
    HRESULT hr = D3D11CreateDevice(
        nullptr, 
        D3D_DRIVER_TYPE_HARDWARE, 
        nullptr, 
        flags, 
        nullptr, 0, 
        D3D11_SDK_VERSION, 
        device.put(), 
        nullptr, nullptr
    );
    
    // 回退到 WARP
    if (hr == DXGI_ERROR_UNSUPPORTED)
    {
        hr = D3D11CreateDevice(
            nullptr, 
            D3D_DRIVER_TYPE_WARP, 
            nullptr, flags, 
            nullptr, 0, 
            D3D11_SDK_VERSION, 
            device.put(), 
            nullptr, nullptr
        );
    }
    
    winrt::check_hresult(hr);
    return device;
}

GraphicsCapturePicker

GraphicsCapturePicker 是 Windows 提供的系统 UI 选择器,让用户选择要捕获的窗口或显示器。

类似于文件选择器,但它是窗口/屏幕选择器。

注意事项:

  • 需要 UI 线程和窗口句柄
  • 不适合纯后台库使用
  • 可考虑自行实现窗口枚举和选择 UI

项目依赖配置

NuGet 包(packages.config)

xml 复制代码
<packages>
  <!-- C++ WinRT 支持 -->
  <package id="Microsoft.Windows.CppWinRT" version="2.0.240405.15" />
  
  <!-- 安全 C++ 工具库 (WIL) -->
  <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240803.1" />
  
  <!-- Windows SDK -->
  <package id="Microsoft.Windows.SDK.CPP" version="10.0.26100.1" />
  <package id="Microsoft.Windows.SDK.CPP.x64" version="10.0.26100.1" />
</packages>

必需的链接库

复制代码
windowsapp.lib  // WinRT API 支持
d3d11.lib       // Direct3D 11
dxgi.lib        // DXGI

实际踩坑经验

1. 链接错误:无法解析的外部符号 SetRestrictedErrorInfo

原因: 缺少 WinRT 链接库

解决方案: 在附加依赖库中添加 windowsapp.lib

2. 预编译头注意事项

如果项目使用了预编译头(pch.h),确保:

  • 所有 .cpp 文件在最前面 include pch.h
  • 或者项目设置中正确配置了预编译头选项

3. 窗口大小变化处理

捕获过程中窗口大小改变时:

  1. 旧的 FramePool 需要关闭
  2. 使用新尺寸重新创建 FramePool
  3. 重新创建 CaptureSession

4. 内存管理

  • 每个帧用完后及时释放
  • 使用 RAII 智能指针管理资源
  • 避免在回调中分配大量内存

5. 线程安全

  • Create() 方式的回调在 UI 线程
  • CreateFreeThreaded() 回调在工作线程
  • 注意多线程访问共享资源时的同步

参考资料


相关推荐
爱学习的程序媛3 小时前
Windows系统下安装与配置FreeSWITCH完整指南
windows·实时互动·webrtc·实时音视频·信息与通信·媒体
song8546011344 小时前
为啥windows中使用docker部署需要启动 Docker Desktop
windows·docker·容器
云边散步4 小时前
godot2D游戏教程系列二(23)
笔记·学习·游戏·音视频·游戏开发
qq_283720054 小时前
VSCode 编译 Qt 5.12 QML 完整教程(Windows + MinGW)
windows·vscode·qt
linkingvision5 小时前
国产操作系统和国产GPU 能代替Windows + Intel的安防视频客户端解码功能么
音视频·视频监控·国产显卡·vaapi·国产cpu·国产gpu
独隅5 小时前
在 Windows 上部署 PyTorch 模型的三种主流方式
人工智能·pytorch·windows
AI服务老曹5 小时前
打破协议孤岛:基于 GB28181/RTSP 的 AI 视频统一接入网关架构解析(源码级)
人工智能·架构·音视频
烧饼Fighting6 小时前
java+vue推rtsp流实现视频播放(由javacv+ffmpg转为vlcj)
java·开发语言·音视频
XiYang-DING6 小时前
【Java SE】泛型(Generics)
java·windows·python