前言

obs系列文章入口:https://blog.csdn.net/qq_33844311/article/details/121479224

微软的窗口采集分主要是有以下 4种方式,obs的窗口采集和显示器采集使用了其中的3种。详细说明请参考文章最后的参考链接。

  • GDI
  • DXGI
  • Window Graphics Capturer
  • Magnification 基于放大镜技术实现录屏采集

这里简单说明一下,GDI 窗口采集方式是兼容性最好的,不受 windows 版本的影响,基本兼容各版本系统。缺点就是比较吃CPU性能,消耗大量cpu运算时间,若不单采鼠标,则使用gdi采集时,鼠标会出现闪烁,无法实现过滤指定窗口。如果采集高分辨率窗口或者屏幕,在比较差的设备上可能会满足不了采集的帧率。

DXGI 是针对win8 以及 win10 系统的桌面采集方式,优点是使用GPU直接处理纹理,效率最高。缺点是win7 系统不支持。

WGC 采集方式是 win 10 1903 版本之后才有的第四代桌面采集技术。优点是采集效率最高,拓展屏采集支持高,1080p采集消耗gpu达到个位数。缺点是兼容性差只有win10 版本 1903 + 才可以使用。低版本无法使用。

窗口采集源和桌面采集源的注册

win-capture.dll 包含了下面四种 source的注册

struct obs_source_info duplicator_capture_info;  // 使用 DXGI or WGC 采集桌面
struct obs_source_info monitor_capture_info;  	 // 使用 GDI 方式采集桌面
struct obs_source_info window_capture_info;		 // 使用 GDI or WGC 采集程序窗口
struct obs_source_info game_capture_info;		 // 通过 hook DirectX 绘制 api 捕获游戏画面

在注册前会判断当前 windows 系统版本是否支持 DXGI or WGC 高性能窗口捕获。

bool graphics_uses_d3d11 = false;
bool wgc_supported = false;

bool obs_module_load(void)
{
	struct win_version_info ver;
	bool win8_or_above = false;
	char *config_dir;

	struct win_version_info win1903 = {
		.major = 10, .minor = 0, .build = 18362, .revis = 0};

	config_dir = obs_module_config_path(NULL);
	if (config_dir) {
		os_mkdirs(config_dir);
		bfree(config_dir);
	}
	// 获取当前windows 系统版本号
	get_win_ver(&ver);

	win8_or_above = ver.major > 6 || (ver.major == 6 && ver.minor >= 2);

	obs_enter_graphics();
	graphics_uses_d3d11 = gs_get_device_type() == GS_DEVICE_DIRECT3D_11;
	obs_leave_graphics();

	// win10 version >=  10.0.18363.0  支持 WGC 窗口采集
	if (graphics_uses_d3d11)
		wgc_supported = win_version_compare(&ver, &win1903) >= 0;
	
	// win7 使用 GDI  win8 及以上使用 DXGI or WGC
	if (win8_or_above && graphics_uses_d3d11)
		obs_register_source(&duplicator_capture_info);// 使用 DXGI or WGC 采集桌面 高性能桌面采集
	else
		obs_register_source(&monitor_capture_info);	  // 使用 GDI 方式采集桌面 兼容性好 性能较差
	// 注册窗口捕获
	obs_register_source(&window_capture_info);

	char *config_path = obs_module_config_path(NULL);

	init_hook_files();
	init_hooks_thread =
		CreateThread(NULL, 0, init_hooks, config_path, 0, NULL);
	
	// 注册游戏捕获
	obs_register_source(&game_capture_info);

	return true;
}

窗口采集

如果用户设置的自动选择采集桌面源方式,具体使用 DXGI or WGC,有一个算法做出选择。

参考 obs的提交日志

提交: 7e263ab77591e3582e49ab40418cda0b0ff7e357 [7e263ab]
父级: c03320cfc2
作者: jpark37 <jpark37@users.noreply.github.com>
日期: 2021年3月11日 21:07:09
提交者: Jim
提交时间: 2021年3月12日 20:59:31
win-capture: Better laptop test for auto-selection

Only auto-select WGC for desktop capture if a battery exists and
multiple GPU adapters are present. May false-positive on desktops with
a smart UPS attached, but we don't know of a better test.

选择桌面采集方式 DXGI or WGC

如果是台式机选择 WGC 最高性能方式采集桌面,如果是笔记本则需要笔记本有独立显卡才会选择WGC

static enum display_capture_method
choose_method(enum display_capture_method method, bool wgc_supported,
	      HMONITOR monitor, int *dxgi_index)
{
	if (!wgc_supported)
		method = METHOD_DXGI;

	if (method != METHOD_WGC) {
		obs_enter_graphics();
		*dxgi_index = gs_duplicator_get_monitor_index(monitor);
		obs_leave_graphics();
	}

	if (method == METHOD_AUTO) {
		method = METHOD_DXGI;
		if (*dxgi_index == -1) {
			method = METHOD_WGC;
		} else {
			SYSTEM_POWER_STATUS status;
			// 获取电池状态 (BatteryFlag  == 128 没有电池)
			// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getsystempowerstatus
			// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-system_power_status
			// 笔记本且有两个显示适配器 (核显+独显)才使用 WGC 桌面采集方式
			if (GetSystemPowerStatus(&status) && status.BatteryFlag < 128) {
				obs_enter_graphics();
				const uint32_t count = gs_get_adapter_count();
				obs_leave_graphics();
				if (count >= 2)
					method = METHOD_WGC;
			}
		}
	}
	return method;
}

GDI 窗口采集原理

GDI :Graph device interface 图形设备接口,应用程序调用图形编程的接口。
核心是创建内存兼容DC,关联一个bitmap资源,然后BitBlt调用可以从显存dc拷贝到内存兼容dc。

DXGI 窗口采集原理

Windows8以后微软引入了一套新的接口,叫“Desktop Duplication API”,而由于Desktop Duplication API是通过Microsoft DirectX Graphics Infrastructure (DXGI)来提供桌面图像的,速度非常快。由于是通过GPU,所以cpu占用率很低,性能很高。

  1. 创建D3DDevice
  2. 通过一系列接口获取路径,获取到IDXGIOutputDuplication接口
  3. 调用AcquireNextFrame,获取当前桌面数据,保存在IDXGIResource中
  4. 把数据从GPU映射到内存中
  5. 拷贝需要的数据到自己的buffer里

WGC 窗口采集原理

Windows Graphics Capture 是WinRT 提供的接口,obs 中提供了 c++ 调用的实现(libwinrt) 大家可以自行查看。
从 Windows 10 版本 1803 开始,Windows.Graphics.Capture 命名空间提供 API 以从显示或应用程序窗口获取帧,以创建视频流或快照以构建协作和交互体验。

微软博客:New Ways to do Screen Capture https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/

基于放大镜技术实现录屏采集

从 Windows Vista 开始,微软新引入了一个新的 Magnification API(放大镜效果),当我们将放大倍率设置成1(默认倍率就是1)亦可以用它来截取屏幕图像。MSDN上提供了该库的完整文档。

最关键的部分,利用 MagSetWindowFilterList 这个神奇的 API,它能够指定一些窗口,在我们截取指定源目标时,从采集到的图像中将 FilterList 中的窗口过滤掉,好像这些窗口根本没有显示一样。这就是我们使用这放大镜方案的主要原因。

这个方法最大的优点是采集时能够过滤掉指定的一些窗口。缺点是只能在Windows 8及以后系统使用,虽然Magnification API是微软从Windows Vista开始引入,但是关键的获取图像数据相关的API只有在Windows 8才能够使用,也就是说在Windows 8以前的版本,我们只能使用放大镜来放大屏幕,却无法使用放大镜来截取图像数据。

并且同样也有效率问题,采集的效率甚至比不上用GDI截屏。

具体实现可以参考 webrtc 源码
src\modules\desktop_capture\win\screen_capturer_win_magnifier.h
src\modules\desktop_capture\win\screen_capturer_win_magnifier.cc

总结

obs 对 windows 平台下的窗口采集和桌面采集做了非常好的封装,想要理解这块儿的代码首先得对 GDI DXGI WGC 技术有一定的了解。

首先还是得看微软相关技术的官方文档介绍。没有什么捷径和技巧。

以上都是个人工作当中对obs-studio开源项目的理解,难免有错误的地方,如果有欢迎指出。

若有帮助幸甚。


技术参考

  1. 本文部分技术点出处:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
  2. Windows桌面采集技术
  3. Webrtc 屏幕共享
  4. OBS原理分析–windows窗口图像帧捕获
  5. windows - 屏幕抓取技术总结
  6. Windows桌面端录屏采集实现教程
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐