obs的视屏录制主要分3种:

  • 窗口采集:采集应用程序窗口
  • 显示器采集:也叫全屏采集,可以采集整个屏幕,当有多个显示器时,可以设置采集其中一个显示器
  • 游戏采集:可以采集游戏窗口

  在plugin-main.c可以看到各个采集的定义,代码如下:

extern struct obs_source_info duplicator_capture_info;
extern struct obs_source_info monitor_capture_info;
extern struct obs_source_info window_capture_info;
extern struct obs_source_info game_capture_info;

  这几个采集信息的结构体应分别在其它几个不同的文件定义,所以用extern声明。
  在第74行,会加载这些采集方式,代码如下:

if (graphics_uses_d3d11)wgc_supported = win_version_compare(&ver, &win1903) >= 0;if (win8_or_above && graphics_uses_d3d11)obs_register_source(&duplicator_capture_info);  //win8以上显示采集
elseobs_register_source(&monitor_capture_info);     //win7及以下显示器采集//窗口采集
obs_register_source(&window_capture_info);

  由于windows系统的窗口在win8版本换了实现方式,为了采集效率,所以分两种不同的形式进行采集。窗口采集则是BitBlt或者wcg.
  关于windows的显示技术,可以去看微软文档:【显示基础结构的发展】
  从该文档可知Windows 8 引入了新的 Microsoft DirectX 图形基础结构 (基于 DXGI) 的 API,使独立软件供应商能够更轻松地 (isv) 支持桌面协作和远程桌面访问方案。
  本篇重点说一下win8以上的系统显示器采集方式,Windows Desktop duplication
  Windows 8引入了新的基于 Microsoft DirectX 图形基础设施 (DXGI) 的 API,使独立软件供应商 (ISV) 可以更轻松地支持桌面协作和远程桌面访问方案。
  此类应用程序广泛用于企业和教育方案。 这些应用程序有一个共同的要求:访问桌面内容,以及将内容传输至远程位置的能力。 桌面Windows 8 API 提供对桌面内容的访问权限。
  目前,Windows API 都允许应用程序无缝实现此方案。 因此,应用程序使用镜像驱动程序、屏幕抓取和其他专有方法来访问桌面的内容。 但是,这些方法具有以下一组限制:
  优化性能可能很有挑战性。
  这些解决方案可能不支持较新的图形呈现 API,因为 API 在产品发布后发布。
  Windows并不总是提供丰富的元数据来帮助优化。
  并非所有解决方案都与 Vista 和更高版本的 Windows 中的桌面组合Windows。
  Windows 8引入了一个基于 DXGI 的 API,称为 桌面复制 API。 此 API 通过使用位图和关联的元数据进行优化,提供对桌面内容的访问。 此 API 适用于启用的都卡主题,不依赖于应用程序使用的图形 API。 如果用户可以在本地控制台上查看应用程序,则还可以远程查看内容。 这意味着,即使全屏 DirectX 应用程序也可以复制。 请注意,API 提供保护,防止访问受保护的视频内容。
  API 使应用程序能够请求Windows,以提供对沿监视器边界的桌面内容的访问。 应用程序可以复制一个或多个活动显示。 当应用程序请求重复时,会发生以下情况:
  Windows呈现桌面,并向应用程序提供副本。每个呈现的帧都放置在 GPU 内存中。每个呈现的帧都附带以下元数据:

  • 脏区域
  • 屏幕到屏幕移动
  • 鼠标光标信息
  • 向应用程序提供对帧和元数据的访问权限。
  • 应用程序负责处理每个帧:
  • 应用程序可以选择基于脏区域进行优化。
  • 应用程序可以选择使用硬件加速处理移动和鼠标数据。
  • 应用程序可以选择在流式处理之前使用硬件加速进行压缩。

  obs的桌面复制主要是在duplicator-monitor-capture.c、monitor-capture.c以及core的libobs-d3d11,主要是用DXGI技术获取屏幕数据,相比于GDI的截图技术有巨大的效率提升。
  在添加采集源时,可以选择使用DXGI技术:

  桌面采集,要达到60fps是很有难度的技术,采用DXGI是很好的选择。
在monitor-capture.c文件中实现了很多于显示器操作的代码,如果有需求,可以摘取这部分代码应用到项目中,例如:

  • 枚举显示器
static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect,LPARAM param)
{struct monitor_info *monitor = (struct monitor_info *)param;if (monitor->cur_id == 0 || monitor->desired_id == monitor->cur_id) {monitor->rect = *rect;monitor->id = monitor->cur_id;}UNUSED_PARAMETER(hdc);UNUSED_PARAMETER(handle);return (monitor->desired_id > monitor->cur_id++);
}
  • 获取显示器的名字
static const char *monitor_capture_getname(void *unused)
{UNUSED_PARAMETER(unused);return TEXT_MONITOR_CAPTURE;
}

  该文件主要是对应win7以下的显示器桌面,采集方法用的是BitBlt, 对于win8以上的桌面OBS主要提供WCG和DXGI这两种方式进行采集,则在duplicator-monitor-capture.c中实现,duplicator_capture_tick方法决定是采用WCG还是DXGI, 主要代码如下:

if (capture->capture_winrt) {capture->exports.winrt_capture_free(capture->capture_winrt);capture->capture_winrt = NULL;
}if (!capture->duplicator) {capture->reset_timeout += seconds;if (capture->reset_timeout >= RESET_INTERVAL_SEC) {capture->duplicator = gs_duplicator_create(capture->dxgi_index);capture->reset_timeout = 0.0f;}
}if (capture->duplicator) {if (capture->capture_cursor)cursor_capture(&capture->cursor_data);//调用libobs-d3d11的导出函数gs_duplicator_update_frameif (!gs_duplicator_update_frame(capture->duplicator)) {free_capture_data(capture);} else if (capture->width == 0) {reset_capture_data(capture);}
}

  如果是使用DXGI, 在采集时gs_duplicator_update_frame一直会被调用,获取桌面资源,该函数封装在libobs-d3d11这个dll中:

EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *d)
{DXGI_OUTDUPL_FRAME_INFO info;ComPtr<ID3D11Texture2D> tex;ComPtr<IDXGIResource> res;HRESULT hr;if (!d->duplicator) {return false;}if (d->updated) {return true;}hr = d->duplicator->AcquireNextFrame(0, &info, res.Assign());if (hr == DXGI_ERROR_ACCESS_LOST) {return false;} else if (hr == DXGI_ERROR_WAIT_TIMEOUT) {return true;} else if (FAILED(hr)) {blog(LOG_ERROR,"gs_duplicator_update_frame: Failed to update ""frame (%08lX)",hr);return true;}hr = res->QueryInterface(__uuidof(ID3D11Texture2D),(void **)tex.Assign());if (FAILED(hr)) {blog(LOG_ERROR,"gs_duplicator_update_frame: Failed to query ""ID3D11Texture2D (%08lX)",hr);d->duplicator->ReleaseFrame();return true;}copy_texture(d, tex);d->duplicator->ReleaseFrame();d->updated = true;return true;
}

  这里要注意的是AcquireNextFrame的返回值,当桌面画面没有变化或没有新图像到来时会返回DXGI_ERROR_WAIT_TIMEOUT,此时无需做图像更新操作,直接返回循环等待下一帧图像。下面是微软的官方说明:

AcquireNextFrame returns:S_OK if it successfully received the next desktop image.
DXGI_ERROR_ACCESS_LOST if the desktop duplication interface is invalid. The desktop duplication interface typically becomes invalid when a different type of image is displayed on the desktop. Examples of this situation are:
Desktop switch
Mode change
Switch from DWM on, DWM off, or other full-screen application
In this situation, the application must release the IDXGIOutputDuplication interface and create a new IDXGIOutputDuplication for the new content.
DXGI_ERROR_WAIT_TIMEOUT if the time-out interval elapsed before the next desktop frame was available.
DXGI_ERROR_INVALID_CALL if the application called AcquireNextFrame without releasing the previous frame.
E_INVALIDARG if one of the parameters to AcquireNextFrame is incorrect; for example, if pFrameInfo is NULL.
Possibly other error codes that are described in the DXGI_ERROR topic.

  获取到纹理数据后,需要做拷贝,调用copy_texture。
  dx的接口是基于com开发的,如果不熟悉com技术,这些代码看起来很吃力,毕竟dx是微软最难的sdk。
  了解com技术的会,应该知道,任何基于com的对象,都得派生于IUnknown,例如DXGIObject

MIDL_INTERFACE("aec22fb8-76f3-4639-9be0-28eb43a67a2e")IDXGIObject : public IUnknown

  IDXGIOutputDuplication派生于DXGIObject,作为结构体gs_duplicator的成员

struct gs_duplicator : gs_obj {ComPtr<IDXGIOutputDuplication> duplicator;gs_texture_2d *texture;int idx;long refs;bool updated;void Start();inline void Release() { duplicator.Release(); }gs_duplicator(gs_device_t *device, int monitor_idx);~gs_duplicator();
};

  在构造时,调用Start().
  另外说明一点,采用DXGI时,obs依赖于libobs-winrt项目. 在使用obs sdk二次开发时,要注意带上libobs-winrt生成的dll.

obs源码分析【八】:显示器采集相关推荐

  1. obs源码分析【一】:main函数

    目录 main函数在哪里 obs项目架构 main函数浅析 crash的处理 obs配置 obs log 网络请求libcurl 主界面 Qt适配高dpi屏幕 总结   最近对obs的代码感兴趣了,在 ...

  2. Spring Security源码分析八:Spring Security 退出

    为什么80%的码农都做不了架构师?>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spr ...

  3. 【转】ABP源码分析八:Logger集成

    ABP使用Castle日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方 ...

  4. Spring源码分析八:Mybatis ORM映射框架原理

    文章目录 (一)Mybatis单独操作数据库程序 1.1.数据库表 1.2.建立PO 1.3.建立mapper接口映射 1.4.建立Mybatis配置文件 1.5.建立mapper映射文件 1.6.测 ...

  5. mybatis的使用及源码分析(八) mybatis的rowbounds分析

    Mybatis提供了一个简单的逻辑分页类RowBounds,其原理类似于在内存中做了一个分页,不是数据库层面的分页,性能不算好,谨慎使用 一. RowBounds源码分析 1 RowBounds源码: ...

  6. obs源码分析【五】:音频采集线程

      在第三篇介绍了视频的线程,音频的线程代码也是在那一块儿: if (!ResetAudio())throw "Failed to initialize audio";   音频线 ...

  7. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  8. OBS源码分析--视频采集显示

    OBS如何进行初始化视频采集的: 首先,在OBS的初始化程序中,会调用ResetVideo==>AttemptToResetVideo==>obs_reset_video==>obs ...

  9. tcp/ip 协议栈Linux内核源码分析八 路由子系统分析三 路由表

    内核版本:3.4.39 Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来.路 ...

最新文章

  1. 命令行的基本使用方法(权限)
  2. 如何将Unix时间戳转换为DateTime,反之亦然?
  3. 定位 android8.1.0,8.1.2 实现Android定位(2)
  4. python 示例_带有示例的Python文件关闭属性
  5. python 回归去掉共线性_线性回归中的多重共线性与岭回归
  6. 曝张一鸣在游戏群批员工上班时聊游戏,遭回怼:那你退群啊
  7. 杰出人士的34个好习惯
  8. java 数组 源码_Java数组转List的三种方式及对比
  9. Mac 如何安装 Mysql@5.7
  10. 单片机怎么通过按键控制计时器的开始和停止_单片机新手入门系列视频集锦
  11. Contexts使用以及详细配置
  12. 【跨境电商】WhatsApp营销保姆级教程!
  13. 三菱PLC基础知识 辅助继电器M
  14. 使用axis生成WebService调用客户端
  15. Windows Live SkyDrive 介绍之图片存储:摄影师在线图片展示的新选择
  16. [渝粤教育] 徐州工业职业技术学院 药物分离技术 参考 资料
  17. Bmob后端云(云数据库表的具体操作)
  18. 2021蓝桥杯预选赛题解
  19. Linux 安装数据库
  20. IBM ServerGuide 9.21

热门文章

  1. 长沙距离中国的“凤凰城”还有多远?
  2. Servlet[jsp]的Servlet.service()引发了具有根本原因的异常 (这个是什么情况?求解答)
  3. 计算机主机放到什么位置最好,电脑桌放在哪个位置好 这些建议你一定要看
  4. Cesium地下管线信息系统(视频)
  5. 【智能开发】血压计方案设计与硬件开发
  6. 蚂蚁金服蒋国飞:区块链商用时代正在加速到来
  7. dism 分割镜像_DISM修改WDS启动镜像实现自动捕获镜像
  8. python怎么处理中英文符号网名_英文带符号的网名_英文网名带符号加中文
  9. C语言while循环语句 do while语句 for循环语句
  10. Pentaho Kettle Solutions (读书笔记)【2. Kettle基本概念】