前言
WebRTC中桌面采集Desktop Capture总体上分为两类:屏幕和窗口,分别对应下面两个类型
- 屏幕捕获 - webrtc::DesktopCapturer::CreateScreenCapturer (静态方法)
- 窗口捕获 - webrtc::DesktopCapturer::CreateWindowCapturer(静态方法)
Windows平台
屏幕采集
Windows上使用CreateRawScreenCapturer创建采集对象,具体实现在screen_capturer_win.cc中。
屏幕捕获根据创建时设置选项的不同,可以有多种层级的对象嵌套,嵌套是为了尽可能地使用一些高级特性,比如硬件加速,窗口过滤,黑屏检测,以及鼠标捕捉。
根据创建时设置的参数,最多可能创建的层级如下:
- ScreenCapturerWinGdi 屏幕/桌面采集一定会创建GDI的这个类,这个是最后兜底的方案,GDI是Windows平台上兼容性最好的,当然并不是可以处理所以场景的。
- ScreenCapturerWinDirectx 如果选项中允许开启DX(set_allow_directx_capturer(true)),就创建。
- BlankDetectorDesktopCapturerWrapper 与DX的方案配套的,用来检测采集到的是否为黑屏数据
- ScreenCapturerWinMagnifier 使用放大镜的方案(set_allow_use_magnification_api(true)),默认不开启。这个可以排除某些窗口来完成采集屏幕/桌面。
- DesktopAndCursorComposer 控制是否要采集鼠标光标
- DesktopCapturerDifferWrapper 控制是否要开启逐帧对比,通过对比相邻两帧,找出二者之间的变化区域。(内部使用了SSE指令做数据块的对比,在文件differ_vector_sse2.h/.cc中)
上面6类对象,通过一个工具类 FallbackDesktopCapturerWrapper 来做包装,完成失败回退的动作。
比如说 使用FallbackDesktopCapturerWrapper(main_capturer, secondary_capturer)包装的两个capturer,如果main_capturer失败的话,就是用secondary_capturer来完成。在设置参数的时候也是二者都会被设置到,获取值的时候,按照优先级依次获取。
而创建采集器的选项 DesktopCaptureOptions 支持的参数有:
- set_allow_directx_capturer
- set_allow_use_magnification_api
- set_allow_capture_mousecursor
- set_detect_updated_region
创建好上述对象之后,接下来就是指定SourceId进行采集了,以及做好采集回调工作了。这个在DesktopCapturer类中对应:
- Start(CallBack* callback); 设置回调对象
- CaptureFrame(); 采集的主函数
- SelectSource(SourceId id); 选择采集屏幕/虚拟桌面/窗口
- Callback::OnCaptureResult 回调接口
// 具体抓一帧数据的实现,注意内存分配的方式
void ScreenCapturerWinGdi::CaptureFrame() {
TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
int64_t capture_start_time_nanos = rtc::TimeNanos();
queue_.MoveToNextFrame();
RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());
// Make sure the GDI capture resources are up-to-date.
PrepareCaptureResources();
if (!CaptureImage()) {
RTC_LOG(WARNING) << "Failed to capture screen by GDI.";
callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
return;
}
// Emit the current frame.
std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
frame->mutable_updated_region()->SetRect(
DesktopRect::MakeSize(frame->size()));
frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
rtc::kNumNanosecsPerMillisec);
frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
}
应用采集
Windows上应用采集使用的是CreateRawWindowCapturer创建采集对象,具体实现在desktop_capturer.cc中。
根据选项,最多有下面几层:
- WindowCapturerWin
- DesktopAndCursorComposer
- DesktopCapturerDifferWrapper
Mac平台
Mac上的取屏比Windows上的简单很多。
屏幕采集
创建屏幕采集对象的接口与Windows相同,只不过是在.mm文件中实现的。
接口DesktopCapturer::CreateRawScreenCapturer在screen_capturer_darwin.mm中实现:
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
const DesktopCaptureOptions& options) {
std::unique_ptr<DesktopCapturer> capturer = CreateRawScreenCapturer(options);
if (capturer) {
if (options.allow_capture_mousecursor()) {
capturer.reset(new DesktopAndCursorComposer(std::move(capturer),options));
}
if (options.detect_updated_region()) {
capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
}
}
return capturer;
}
所谓的raw screen capturer就是一个ScreenCapturerMac对象。抓屏的方法:ScreenCapturerMac::CaptureFrame在文件screen_capturer_mac.mm中实现。
取屏的几个核心的方法是Mac上CG库提供的:
- CGWindowListCreateImageFromArray 从一组Windows id中生成一张图片
- CGDisplayCreateImage 获取指定显示器的图片
应用采集
DesktopCapturer::CreateRawWindowCapturer接口在window_capturer_mac.mm文件中实现。code也很简单:
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
return std::unique_ptr<DesktopCapturer>(
new WindowCapturerMac(options.full_screen_chrome_window_detector(),
options.configuration_monitor()));
}
WindowCapturerMac
核心方法是 CGWindowListCreateImage,为指定的一组窗口生成图片。