【obs-studio开源项目从入门到放弃】windows 窗口采集和桌面采集的解决方案
前言
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占用率很低,性能很高。
- 创建D3DDevice
- 通过一系列接口获取路径,获取到IDXGIOutputDuplication接口
- 调用AcquireNextFrame,获取当前桌面数据,保存在IDXGIResource中
- 把数据从GPU映射到内存中
- 拷贝需要的数据到自己的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开源项目的理解,难免有错误的地方,如果有欢迎指出。
若有帮助幸甚。
技术参考
更多推荐
所有评论(0)