在进入具体的方式讨论前,我们先看看 Windows 桌面图形界面的简化架构,如下图:

在 Windows Vista 之前,Windows 界面的复合画面经由 Graphics Device Interface(以下简称 GDI)技术直接渲染到桌面上。

在 Windows Vista 以及之后的版本,Desktop Composition 的工作就交由一个新的模块Desktop Window Manager(以下简称 DWM) 来完成了。

如上图所示,应用程序画完自己的界面后,提交给 DWM 把它合成到桌面上,而 DWM 经过一系列演进后,为了提效,基于微软自己的 Direct3D(以下简称 D3D) 实现了整套技术,在 D3D 这层的下面是 Windows Display Driver Model(以下简称 WDDM,Windows 图形驱动程序模型)。

所以在 Windows 下实现录屏采集,基本上可以从最基本的 GDI 技术和 D3D 技术两方面考虑。

基于 GDI 技术实现录屏采集

  • 基本采样流程

在 Windows 平台上有过图形开发经验的开发者,应该都知道 BitBlt 这个 API, 它为我们实现了 Windows DC 间的内容拷贝,假如将 Source DC 指定为 Window DC 或是 Destop DC,这就实现了对屏幕指定源的画面截取。下图说明了基于 GDI 技术录屏采集的大致调用过程:

以整个桌面作为采集源举例,通用做法是调用 GetDC(GetDesktokWindow()) 获取桌面 DC,通过 CreateDIBSection 创建一个设备无关的位图对象以及内存 DC,最后调用 BitBlt 把桌面 DC 的原始数据翻转到内存 DC 上,这样从内存 DC 上就能直接获取到桌面的原始 RGB 数据。

需要注意的是,创建一个设备无关的位图时,CreateDIBSection 的第4个参数 ppvBits 是提前分配好的位图数据缓冲区。而在以实时视频流的方式共享屏幕的场景下,需要以每秒十几次甚至几十次的频率进行采样,从效率的要求考虑,这里自然不可能每次都重新分配缓冲区,所以可以根据源的分辨率,在采集前就分配一个足够大的空间。

CreateDIBSection的函数原型如下图所示:

HBITMAP CreateDIBSection(HDC              hdc,const BITMAPINFO *pbmi,UINT             usage,VOID             **ppvBits,HANDLE           hSection,DWORD            offset
);
  • 绘制鼠标

经过以上基本采样流程,得到的画面内容是不含鼠标的,而在一次研讨会或在线课的材料共享环节,主讲人往往要通过鼠标,指明当前所讲,因此必须将鼠标还原到画面中。

具体实现过程如下:

CURSORINFO ci = { 0 };
ci.cbSize = sizeof(ci);
ZeroMemory(&ci, sizeof(CURSORINFO));
ci.cbSize = sizeof(CURSORINFO);if (::GetCursorInfo(&ci))
{POINT ptCursorPos = { 0, 0 };ptCursorPos = ci.ptScreenPos;::DrawIconEx(m_hCompDC,ptCursorPos.x , ptCursorPos.y,ci.hCursor,0, 0, 0, 0,DI_NORMAL | DI_DEFAULTSIZE | DI_COMPAT);
}
  • 优劣势分析

以上 GDI 录屏技术,其优势是通用性,即可以在所有 Windows 系统版本实现。

但同时,它也存在一些劣势。由于整体的运算、拷贝过程都在 CPU 中完成,导致采样效率偏低,尤其在高频采样(> 20fps)时,对 CPU资源的消耗过高,而且后续还要处理为实时视频流,经过同样高频的编码、网络包发送,多方面因素叠加,自然对机器性能有更高的要求。而现实场景中,主持人、老师的机器往往吃不消,在可行的情况下,我们尽量争取应用更进阶、高效的技术实现, GDI 则可以作为一种保底方案。

在具体实现中,还有两点需要特别提醒:

1)在 Windows XP 下,可以通过 BitBlt 函数最后的参数,来控制是否拷贝 Layered Window。只有 SRCCPY 标识,表示拷贝内容不包含 Layered Window;如果是SRCCPY | CAPTUREBLT,则表示拷贝包括 Layered Window 在内的所有窗口。而这个标识,在 Windows Vista 之后的系统版本开启 DWM 的情况下,已经无效,因为这种情况下所有的窗口都是 Layered Window;

2)在 Windows Vista 之后的系统版本开启 DWM 的情况下,单次抓取速度变得非常慢(作者机器实测 30ms +);

基于 DXGI 技术实现录屏采集

  • 基本采样流程

除了用 GDI 技术实现录屏,实际上在 Windows 平台上,微软提供了多种录屏方案,相对 GDI 技术来说,其大多数接口的处理性能并不理想,或存在诸多限制,通用性不足。

从 Windows 8 开始,微软引入了一套新技术叫 Desktop Duplication API,应用程序可以通过这套 API 请求桌面的图形数据。由于 Desktop Duplication API 是通过 DirectX Graphics Infrastructure(以下简称 DXGI)来提供桌面图像的,竞争的是 GPU 流水线资源,所以 CPU 占用率很低,采集性能非常高。

由于这套能力整合在 DirextX 中提供,所以与大部分 DirectX 接口的使用方式基本一致,其流程概括如下图。

如图所示,使用 DXGI 需要一些简单的 DirectX 基础知识,通过各种 DirectX COM 接口的查询,最终获取 IDXGIOutputDuplication 接口指针,截屏时使用其中核心的AcquireNextFrame API 获取当前桌面图像,此外,它还提供 GetFrameDirtyRects 等 API,可以获取经过 GPU 计算后发生了变化的脏矩形区域。

  • 绘制鼠标

和 GDI 面临相同的问题,直接通过 AcquireNextFrame API 获取到的画面中,也是不含鼠标图像的。想要将鼠标绘制到画面中,我们需要和GDI相关的API配合使用。

在采集之前创建数据缓冲区时,将缓冲区关联到设备无关的位图上,并将位图选入临时的内存DC(一般由桌面DC生成的临时内存DC),再将通过 AcquireNextFrame获取的画面拷贝到缓冲区后,这时可以使用GDI绘制鼠标的方法,将鼠标绘制到位图上,这样数据缓冲区中的图像数据就包含了鼠标了。

  • 优劣势分析

在 Windows 平台上,从现有的录屏采集方式(包括GDI采集和放大镜采集)来看,DXGI 是性能最好的。

其劣势是只在 Windows 8 系统版本及以上才支持,所以在整体方案中,一般要与 GDI 共同组合提供。此外,它无法指定某个程序窗口进行采集。

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

  • 基本采样流程

前面所述的两种方式都可以实现录屏采集,也是最常用的两种方式。

但有时,我们要指定源来采集,并且希望采集到的画面不被其他内容干扰。比如,在线课堂上,老师指定采集了一个 PPT 窗口,共享授课的过程中时不时需要操作一下其他 App,此时,其他 App的窗口有可能遮挡了 PPT 窗口,而老师并不想将这些无关或隐私信息对外暴露。因此我们需要实现一个采集源过滤器(filter),排除掉无关信息。

在 Windows XP 时代,用 GDI 的 BitBlt API 进行采集时,指定一个 Window DC,并且对最后一个参数去掉 CAPTUREBLT 标识,即可排除掉其他 Layered Window 的干扰。但如今 Windows XP 已成过去式,这项措施无法解决现有系统的过滤问题,必须找到另一替代方案。

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

在初始化相关模块后,首先创建放大镜控件的主窗口,并且将其设置为全屏不可见,因为我们要使用它来捕获图像,它只是一个工具,所以不能也不需要在用户侧显示它。因此,设置窗口扩展属性 WS_EX_LAYERED,调用 SetLayeredWindowAttributes 设置全透明。

::SetLayeredWindowAttributes(hwnd, 0 ,255, LWA_ALPHA);

接着创建放大镜窗口作为主窗口的子窗口,窗口类名必须为“Magnifier”。如果要捕获鼠标光标,还要设置窗口属性为 MS_SHOWMAGNIFIEDCURSOR。

hwndMag = ::CreateWindow(WC_MAGNIFIER, TEXT("Magnifier"), WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR */| WS_VISIBLE,0, 0, m_ScreenX, m_ScreenY, hostDlg->GetSafeHwnd(), NULL, hInstance, NULL );

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

那么如何获得录屏图像呢?每当我们调用 MagSetWindowSource 时,都会触发回调MagSetImageScalingCallback数据回调。原型如下:

:data, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)

其中第二个参数 srcdata 就是指向录屏结果图像的原始数据,srcheader 则包含数据的长度,以及图像长宽等元信息,至此,我们可以使用这两个参数来构建位图了。

  • 优劣势分析

这个方法最大的优点是采集时能够过滤掉指定的一些窗口。

缺点是只能在Windows 8及以后系统使用,虽然Magnification API是微软从Windows Vista开始引入,但是关键的获取图像数据相关的API只有在Windows 8才能够使用,也就是说在Windows 8以前的版本,我们只能使用放大镜来放大屏幕,却无法使用放大镜来截取图像数据。

并且同样也有效率问题,采集的效率甚至比不上用GDI截屏,整屏采集一次需要50ms左右。

总结

上面我们描述了三种录屏采集方式的实现流程,并分析了各自的优劣势:

GDI是在Windows下截屏的通用方法,没有什么限制,一般在没有特殊需求的场景下使用;

DXGI截取效率最高,但是不能在Windows 7下使用,也不能过滤窗口;

放大镜可以过滤窗口,但是也不能在Windows 7下使用,并且效率也不高。

Windows在不断发展和进步,基本没有一种通用的抓屏技术可以高效的抓取所有的系统。所以我们在实现录屏采集时,要灵活组合三种方式,以适配复杂的应用场景。

技术干货 | 录屏采集实现教程 —— Windows桌面端相关推荐

  1. 技术干货 | 录屏采集实现教程 —— macOS桌面端

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  2. 技术干货 | 录屏采集实现教程 —— iOS端

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  3. 技术干货 | 基于 Qt Quick Plugin 快速构建桌面端跨平台组件

    导读:桌面端的 UI 开发框架对比移动端.Web 端的成熟方案,一直处于不温不火的状态.随着疫情掀起的风波,桌面端在线教育.视频会议等需求不断涌现.本文将围绕 Qt Quick 的优势来介绍如何快速创 ...

  4. Windows桌面端录屏采集实现

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  5. 计算机xp考试经验,全国专业技术人员计算机应用能力考试标准教程——Windows XP操作系统...

    全国专业技术人员计算机应用能力考试标准教程--Windows XP操作系统 语音 编辑 锁定 讨论 上传视频 全国专业技术人员计算机应用能力考试标准教程--Windows XP操作系统,是一本职称计算 ...

  6. 苹果手机和电脑怎么录屏?详细教程来了!

    ​相信小伙伴身边有不少人使用的是苹果手机和电脑.安卓手机和windows电脑怎么录屏不少人都已经知道了,那么苹果手机和电脑怎么录屏 呢?现在,小编就来详细的教教大家如何录屏,快拿出小本本记下来哦! 一 ...

  7. Win7电脑系统录屏功能使用教程分享

    Win7电脑系统录屏功能使用教程分享.电脑上其实不用下载也可以使用录制屏幕这个功能,很多用户不懂如何去开启这个功能的方法,如果你还没有学会自带录屏工具的使用方法,那么可以通过本文来进行学习. 操作步骤 ...

  8. 为什么截屏不能分享微信_为什么腾讯可以在移动端QQ做到闪照,而在Windows桌面端做不到?...

    为什么腾讯可以在移动端QQ做到阅后即焚(闪照),而在Windows桌面端做不到?如题,即使QQ可以在Android端做到图片阅后即焚(闪照),我也可以在Windows上开一个安卓模拟器从而打开QQ,截 ...

  9. VM虚拟机安装使用OBS直播录屏软件图文教程及注意事项

    因为有时候会需要在虚拟机中用到 OBS 软件,所以奇技视频写了一个教程方便大家,也备为不时之需.OBS 用作录屏时,虽然在码率高的时候文件较大,但优点也很明显的,因为录下来的视频不会受到桌面广告弹窗. ...

最新文章

  1. protoc-3.2.0-win32转java文件
  2. Unity Game窗口中还原Scene窗口摄像机操作 强化版
  3. HTTP(超文本传输协议)
  4. OpenGL基础36:天空盒
  5. 提取rosbag中的图像话题存为本地图像
  6. 编译原理完整学习笔记(四):语法分析
  7. 如何用c#来制作一个activeX控件或类似的东西
  8. 怎样编辑pdf文件?手把手教你如何使用PDF编辑器
  9. Hcse 交换知识点-2
  10. 用AI让逝去的亲人照片动起来后,数百万网友泪目:原来思念这么重
  11. MySQL学习笔记(2)
  12. LR 杂记--nmon 分析 AIX 和 Linux 性能
  13. WebGIS前端框架(openlayers,mapbox,leaflet)图形图像底层渲染原理分析
  14. 在线任务管理服务大汇总
  15. Flutter Missing parentheses in call to ‘print‘. Did you mean print(‘Insert‘, text,‘to line‘, line_nu
  16. 对于IT这两个字眼,是不是只能由学习过IT同学.下面为大家讲解一下IT的由来!
  17. 面渣逆袭:线程池夺命连环十八问
  18. c 盘空间又满了?微信清理神器帮你释放空间
  19. linux中find的用法
  20. 基于FreeSurfer的海马亚区分割

热门文章

  1. 指针定义、指针与数组、指针运算、比较
  2. 小程序 实现星星评分(共10分),含有半星
  3. XMind2020介绍、下载
  4. 扬帆跨境电商:Shopify放量5000W扩大规模
  5. html5圆圈闪烁,html5 css3圆形波浪百分比加载动画特效
  6. express 框架之session
  7. linux如何连接手机传文件,Ubuntu和手机通过蓝牙互传文件
  8. 一种崭新的长尾分布下分类问题的通用算法|NeurIPS 2020
  9. H5 -- 自定义微信分享第三方页面链接的标题和小缩略图
  10. mysql笔试题15道