# 桌面采集 · DesktopCapturer 抓屏幕与窗口

> 屏幕共享的第一步是把屏幕/窗口的像素抓出来，这是 desktop_capture 模块的活。这一章读 desktop_capturer.h，讲清 DesktopCapturer 的拉取式采集模型（Start/CaptureFrame/回调）、屏幕与窗口的选择，以及跨平台后端和「只传变化区域」的优化。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-15
- 原文：https://daiw.org/manual/webrtc/desktop-capture
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 桌面采集 · DesktopCapturer 抓屏幕与窗口

从这一章起，转向[平台采集层](https://daiw.org/manual/webrtc/platform-overview)——[核心引擎处理的“帧”从哪来](https://daiw.org/manual/webrtc/video-frame)。屏幕共享（远程协助、在线会议演示）的第一步，是把**屏幕或窗口的像素抓出来**，这是 `desktop_capture/` 模块的职责。它是[平台相关](https://daiw.org/manual/webrtc/platform-overview)的，但抽象接口统一。

## DesktopCapturer：拉取式采集

看核心接口（真实源码 `desktop_capture/desktop_capturer.h`）：

```cpp
// desktop_capture/desktop_capturer.h(节选,真实源码)
class DesktopCapturer {
 public:
  class Callback {
    virtual void OnCaptureResult(Result result,
                                 std::unique_ptr<DesktopFrame> frame) = 0;
  };
  virtual void Start(Callback* callback) = 0;
  virtual void CaptureFrame() = 0;                 // 抓一帧(异步,结果给 callback)
  virtual bool GetSourceList(SourceList* sources); // 列出可抓的屏幕 / 窗口
  virtual bool SelectSource(SourceId id);          // 选一个来抓

  static std::unique_ptr<DesktopCapturer> CreateScreenCapturer(...);
  static std::unique_ptr<DesktopCapturer> CreateWindowCapturer(...);
};
```

<Callout type="info">
  **`DesktopCapturer` 是**拉取式（pull）**的：你 `Start` 注册回调，然后每需要一帧就调一次 `CaptureFrame()`，抓好的帧异步经 `Callback::OnCaptureResult` 交回。** 这和[摄像头的推送式](https://daiw.org/manual/webrtc/video-capture)不同（下一章对比）——屏幕内容变化慢、且由**你想要的节奏**驱动（[按目标帧率主动抓](https://daiw.org/manual/webrtc/video-timing)），拉取式更合适。工作流：

  1. `CreateScreenCapturer` / `CreateWindowCapturer` 建一个采集器（抓整屏还是抓单个窗口）。
  2. `GetSourceList` 列出可选目标（[多显示器 / 各打开的窗口](https://daiw.org/manual/webrtc/desktop-capture)），`SelectSource` 选一个。
  3. `Start(callback)` 后，[按帧率](https://daiw.org/manual/webrtc/video-timing)反复 `CaptureFrame()`，每次得到一个 `DesktopFrame`（一块像素）。

  抓到的 [`DesktopFrame`](https://daiw.org/manual/webrtc/video-frame) 再转成核心认识的 [`VideoFrame`](https://daiw.org/manual/webrtc/video-frame)，喂给[编码器](https://daiw.org/manual/webrtc/video-source-encoder)——于是“屏幕共享”就接进了[和摄像头视频一样的管线](https://daiw.org/manual/webrtc/video-pipeline-overview)。**对核心引擎而言，屏幕和摄像头都只是“帧的来源”，一视同仁。**
</Callout>

## 屏幕 vs 窗口：两种源

`GetSourceList` 返回的源分两类，`CreateScreenCapturer` 和 `CreateWindowCapturer` 分别对应：

- **屏幕（screen）** —— 抓整个显示器。[多显示器](https://daiw.org/manual/webrtc/desktop-capture)时列出每一个，选其一或抓全部。
- **窗口（window）** —— 抓某个应用窗口（如“只共享 PPT 窗口，不露出其他”）。这更讲究：窗口可能被遮挡、最小化，平台后端要处理这些。源码里 `cropping_window_capturer` 就是处理窗口裁剪的。

这个“[先列源、再选源、后采集](https://daiw.org/manual/webrtc/desktop-capture)”的三段式，正是你在会议软件里点“共享屏幕”时弹出的那个“选择要共享的屏幕 / 窗口”面板背后的机制。

## 跨平台后端与“只传变化”优化

`DesktopCapturer` 是抽象，真正抓像素依赖 OS（呼应[平台层的本质](https://daiw.org/manual/webrtc/platform-overview)）：

| 平台 | 后端机制 |
| --- | --- |
| Windows | GDI / DXGI(Desktop Duplication) |
| macOS | CGDisplayStream / ScreenCaptureKit |
| Linux | X11 / PipeWire(Wayland) |

<Callout type="info">
  **屏幕采集有个关键优化：**只传变化的区域**（源码 `desktop_capturer_differ_wrapper`、`blank_detector`）。** 屏幕大部分时间**大片不动**（看文档时只有光标闪），整屏每帧都重抓、重编码极其浪费。所以：

  - **差分（differ）** —— [算出这帧相比上帧哪些区域变了](https://daiw.org/manual/webrtc/video-jitter-buffer)（dirty region），只处理变化部分。
  - **空白检测（blank detector）** —— 识别整块没变的画面，避免无谓编码。

  这和[视频编码的帧间预测](https://daiw.org/manual/webrtc/video-source-encoder)理念一致（[只编码变化](https://daiw.org/manual/webrtc/simulcast-svc)），但在**采集层**就先做了一道，大幅降低[屏幕共享的 CPU 和码率](https://daiw.org/manual/webrtc/rate-control)。这也是为什么屏幕共享（静态内容多）通常比摄像头视频[省带宽](https://daiw.org/manual/webrtc/video-bitrate-allocation)——变化少，差分后要编码传输的数据就少。
</Callout>

## 小结与下一章

**`DesktopCapturer`**（`desktop_capture/`）负责[屏幕共享的像素采集](https://daiw.org/manual/webrtc/platform-overview)，是**拉取式**的：`Start(callback)` 后按[帧率](https://daiw.org/manual/webrtc/video-timing)反复 `CaptureFrame()`，帧经 `OnCaptureResult` 交回，转成 [`VideoFrame`](https://daiw.org/manual/webrtc/video-frame) 接入[标准视频管线](https://daiw.org/manual/webrtc/video-pipeline-overview)。用 **`GetSourceList`/`SelectSource`** [先列源再选源](https://daiw.org/manual/webrtc/desktop-capture)（屏幕 or 窗口），`CreateScreenCapturer`/`CreateWindowCapturer` 建采集器。后端[按平台](https://daiw.org/manual/webrtc/platform-overview)（DXGI/ScreenCaptureKit/PipeWire），并有**只传变化区域**的优化（[differ/blank detector](https://daiw.org/manual/webrtc/desktop-capture)），让静态屏幕[省 CPU 和码率](https://daiw.org/manual/webrtc/rate-control)。对核心而言，[屏幕和摄像头都只是帧源](https://daiw.org/manual/webrtc/video-frame)。

另一个更常见的帧源——**摄像头**。下一章看它的采集，顺带对比推送式与拉取式。👉 [摄像头采集](https://daiw.org/manual/webrtc/video-capture)。

## 参考资料

- [1] libwebrtc 源码.`modules/desktop_capture/desktop_capturer.h`（`Start`/`CaptureFrame`/`GetSourceList`/`SelectSource`）。
- [2] libwebrtc 源码.`desktop_capturer_differ_wrapper.h`、`blank_detector_desktop_capturer_wrapper.h`、`cropping_window_capturer.h`。
- [3] 关联[视频帧](https://daiw.org/manual/webrtc/video-frame)、[视频采集编码](https://daiw.org/manual/webrtc/video-source-encoder)。
