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

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

作者 David更新于 第 109 篇(共 112 篇)

桌面采集 · 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 交回。 这和摄像头的推送式不同(下一章对比)——屏幕内容变化慢、且由你想要的节奏驱动(按目标帧率主动抓),拉取式更合适。工作流:

  1. CreateScreenCapturer / CreateWindowCapturer 建一个采集器(抓整屏还是抓单个窗口)。
  2. GetSourceList 列出可选目标(多显示器 / 各打开的窗口),SelectSource 选一个。
  3. Start(callback) 后,按帧率反复 CaptureFrame(),每次得到一个 DesktopFrame(一块像素)。

抓到的 DesktopFrame 再转成核心认识的 VideoFrame,喂给编码器——于是“屏幕共享”就接进了和摄像头视频一样的管线。对核心引擎而言,屏幕和摄像头都只是“帧的来源”,一视同仁。

屏幕 vs 窗口:两种源

GetSourceList 返回的源分两类,CreateScreenCapturer 和 CreateWindowCapturer 分别对应:

  • 屏幕(screen) —— 抓整个显示器。多显示器时列出每一个,选其一或抓全部。
  • 窗口(window) —— 抓某个应用窗口(如“只共享 PPT 窗口,不露出其他”)。这更讲究:窗口可能被遮挡、最小化,平台后端要处理这些。源码里 cropping_window_capturer 就是处理窗口裁剪的。

这个“先列源、再选源、后采集”的三段式,正是你在会议软件里点“共享屏幕”时弹出的那个“选择要共享的屏幕 / 窗口”面板背后的机制。

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

DesktopCapturer 是抽象,真正抓像素依赖 OS(呼应平台层的本质):

平台后端机制
WindowsGDI / DXGI(Desktop Duplication)
macOSCGDisplayStream / ScreenCaptureKit
LinuxX11 / PipeWire(Wayland)

屏幕采集有个关键优化:只传变化的区域(源码 desktop_capturer_differ_wrapper、blank_detector)。 屏幕大部分时间大片不动(看文档时只有光标闪),整屏每帧都重抓、重编码极其浪费。所以:

这和视频编码的帧间预测理念一致(只编码变化),但在采集层就先做了一道,大幅降低屏幕共享的 CPU 和码率。这也是为什么屏幕共享(静态内容多)通常比摄像头视频省带宽——变化少,差分后要编码传输的数据就少。

小结与下一章

DesktopCapturer(desktop_capture/)负责屏幕共享的像素采集,是拉取式的:Start(callback) 后按帧率反复 CaptureFrame(),帧经 OnCaptureResult 交回,转成 VideoFrame 接入标准视频管线。用 GetSourceList/SelectSource 先列源再选源(屏幕 or 窗口),CreateScreenCapturer/CreateWindowCapturer 建采集器。后端按平台(DXGI/ScreenCaptureKit/PipeWire),并有只传变化区域的优化(differ/blank detector),让静态屏幕省 CPU 和码率。对核心而言,屏幕和摄像头都只是帧源。

另一个更常见的帧源——摄像头。下一章看它的采集,顺带对比推送式与拉取式。👉 摄像头采集。

参考资料

  • [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] 关联视频帧、视频采集编码。

本页目录