桌面采集 · DesktopCapturer 抓屏幕与窗口
屏幕共享的第一步是把屏幕/窗口的像素抓出来,这是 desktop_capture 模块的活。这一章读 desktop_capturer.h,讲清 DesktopCapturer 的拉取式采集模型(Start/CaptureFrame/回调)、屏幕与窗口的选择,以及跨平台后端和「只传变化区域」的优化。
桌面采集 · DesktopCapturer 抓屏幕与窗口
从这一章起,转向平台采集层——核心引擎处理的“帧”从哪来。屏幕共享(远程协助、在线会议演示)的第一步,是把屏幕或窗口的像素抓出来,这是 desktop_capture/ 模块的职责。它是平台相关的,但抽象接口统一。
DesktopCapturer:拉取式采集
看核心接口(真实源码 desktop_capture/desktop_capturer.h):
// 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(...);
};DesktopCapturer 是拉取式(pull)的:你 Start 注册回调,然后每需要一帧就调一次 CaptureFrame(),抓好的帧异步经 Callback::OnCaptureResult 交回。 这和摄像头的推送式不同(下一章对比)——屏幕内容变化慢、且由你想要的节奏驱动(按目标帧率主动抓),拉取式更合适。工作流:
CreateScreenCapturer/CreateWindowCapturer建一个采集器(抓整屏还是抓单个窗口)。GetSourceList列出可选目标(多显示器 / 各打开的窗口),SelectSource选一个。Start(callback)后,按帧率反复CaptureFrame(),每次得到一个DesktopFrame(一块像素)。
抓到的 DesktopFrame 再转成核心认识的 VideoFrame,喂给编码器——于是“屏幕共享”就接进了和摄像头视频一样的管线。对核心引擎而言,屏幕和摄像头都只是“帧的来源”,一视同仁。
屏幕 vs 窗口:两种源
GetSourceList 返回的源分两类,CreateScreenCapturer 和 CreateWindowCapturer 分别对应:
- 屏幕(screen) —— 抓整个显示器。多显示器时列出每一个,选其一或抓全部。
- 窗口(window) —— 抓某个应用窗口(如“只共享 PPT 窗口,不露出其他”)。这更讲究:窗口可能被遮挡、最小化,平台后端要处理这些。源码里
cropping_window_capturer就是处理窗口裁剪的。
这个“先列源、再选源、后采集”的三段式,正是你在会议软件里点“共享屏幕”时弹出的那个“选择要共享的屏幕 / 窗口”面板背后的机制。
跨平台后端与“只传变化”优化
DesktopCapturer 是抽象,真正抓像素依赖 OS(呼应平台层的本质):
| 平台 | 后端机制 |
|---|---|
| Windows | GDI / DXGI(Desktop Duplication) |
| macOS | CGDisplayStream / ScreenCaptureKit |
| Linux | X11 / PipeWire(Wayland) |
屏幕采集有个关键优化:只传变化的区域(源码 desktop_capturer_differ_wrapper、blank_detector)。 屏幕大部分时间大片不动(看文档时只有光标闪),整屏每帧都重抓、重编码极其浪费。所以:
- 差分(differ) —— 算出这帧相比上帧哪些区域变了(dirty region),只处理变化部分。
- 空白检测(blank detector) —— 识别整块没变的画面,避免无谓编码。
这和视频编码的帧间预测理念一致(只编码变化),但在采集层就先做了一道,大幅降低屏幕共享的 CPU 和码率。这也是为什么屏幕共享(静态内容多)通常比摄像头视频省带宽——变化少,差分后要编码传输的数据就少。
小结与下一章
DesktopCapturer(desktop_capture/)负责屏幕共享的像素采集,是拉取式的:Start(callback) 后按帧率反复 CaptureFrame(),帧经 OnCaptureResult 交回,转成 VideoFrame 接入标准视频管线。用 GetSourceList/SelectSource 先列源再选源(屏幕 or 窗口),CreateScreenCapturer/CreateWindowCapturer 建采集器。后端按平台(DXGI/ScreenCaptureKit/PipeWire),并有只传变化区域的优化(differ/blank detector),让静态屏幕省 CPU 和码率。对核心而言,屏幕和摄像头都只是帧源。
另一个更常见的帧源——摄像头。下一章看它的采集,顺带对比推送式与拉取式。👉 摄像头采集。