桌面端屏幕分享实践

本篇主要介绍 Windows 端和 macOS 端上屏幕分享的实现方式与注意事项。这两套系统都是闭源的,主要信息来源于官方文档,以及加上各位技术前辈和个人的一些摸索,如有不当或者错误的地方,还请诸位不吝指正。

作者:刘国元 网易资深开发工程师

一、前言

实时音视频通信的整个流程,可以大致分为数据采集、编码、传输、解码、渲染几个环节。各类平板、手机、电脑等终端是数据采集和渲染的重要环节。鉴于目前市面上介绍桌面端的屏幕数据采集资料总结的比较少,本文抛砖引玉讨论一下,在开发桌面端屏幕数据采集过程中的一些实践经验。

在 Windows 和 macOS 两大桌面操作系统的应用层上实现屏幕采集,主要工作是针对这类基于窗口的系统实现窗口图像的抓取。不同桌面端的图形系统提供了不同的系统级实现,以及不同的系统 API,我们无法通过一套标准的接口来实现该功能,只能针对性地给出不同的封装,下面分条细述。

二、Windows

Windows 上实现屏幕分享算是几个平台最复杂的,单单是 Windows 提供的抓取屏幕图像的技术就有好多种,简单列举概述如下:

  • Windows GDI:这是 Windows 系统上兼容性较好的一套方案,从  Windows 2000 开始就有,现在还在继续提供服务,效率也还不错,部分接口底层支持了硬件加速功能,至于缺点我们后面叙述。
  • Microsoft DirectX Graphics Infrastructure(DXGI):这套 API 是为了封装不同版本 DirectX 部分接口而出现的,首次出现是在 Direct 3D 10,现在还在更新。本文关注的是 DirectX 提供的显示器数据抓取技术。
  • DWM:这项技术是微软在 Vista 上,为了提高桌面窗口显示效果而提供的一套 API,其基本思路是:改变过去直接绘制窗口到屏幕的做法,在视频内存中开辟一块离屏渲染区域,用来做各种像素处理,之后再交给屏幕显示,以此实现 Vista 有的毛玻璃效果,以及按下窗口键 +Tab 键来做桌面窗口切换时的 3D 转换效果等。通过这套 API 我们可以实现窗口缩略图的渲染,在 Windows 7 上,DWM 可以通过切换系统主题来控制其打开和关闭,即 Aero 主题。从 Windows 8 开始,系统默认都是打开 DWM 。这套 API 一般可以用来获取目标窗口的缩略图(thumbnail,支持动态更新),用来分享画面的话,分辨率偏低,不太能满足需求。
  • Magnification API:我们姑且称之为“放大镜 API”,这个是 Windows 上预装的放大镜程序底层使用的技术,我们正好可以利用这个技术,同时还可以实现窗口过滤功能。这套 API 也是目前兼容范围较广,各类 App 使用非常多的窗口显示数据抓取技术,我们后面会详细分析。
  • Windows Grapics Capture(WGC API):微软最新提供的一套正式用来抓取屏幕数据的接口,遗憾的是最初是给 UWP 提供的,而非 Win32 App,且要求必须是 Windows 10, Update(1809)及之后的系统才支持,但是官方提供的,我们也要予以足够的重视。
  • Windows Media API,以及各种 hook 技术 …… 可用范围较窄,因此这里不讨论。

虽然可以列举的方案有很多种,但是没有一种方案可以覆盖从 Windows 7 到 Windows 10 上运行的所有 GUI 程序窗口(且不论 Windows XP 系统),从这一点可以看到微软最近这二十年在窗口系统上“激进”的“创新”。

屏幕分享功能常见场景可以大致分为两种:

  • 分享某个应用的单个窗口;
  • 分享指定显示器的内容。如果有多块外接显示器,可能还要考虑是否要分享整个虚拟桌面(把几块显示器图像拼接起来),不过目前几乎没看到这类应用场景。

我们要做的就是组合前面提到的可用的技术,来覆盖这两种场景。接下来我们分别讨论。

(一) 应用窗口采集 

在详细介绍应用窗口采集实现方案之前,我们先看一下要这个场景下还有哪些细节需要考虑进来。

我们开始屏幕分享之后,为了让分享者能够做一些常用的操作,比如开始/停止/暂停/恢复/分享、邀请联系人、打开一个 IM 窗口进行交流等。一般情况下,我们在应用层会做一个操作的浮窗,在上面暴露多个操作入口,但观看端是不想看到这些画面的,且这些窗口还可能挡住分享者桌面上一些重要的信息。这就引出我们需要考虑的最重要,也是最难实现的一个点:有一些桌面上的窗口需要在分享过程中被过滤掉。

针对屏幕分享场景,上述支持窗口过滤的方案基本上只有两种:Magnification API 和 WGC API。

1. Magnification API

Magnification API 方案,因为其覆盖范围较广,所支持的功能又正好满足我们需要,所以我们将其作为优选方案,其他方案作为补充。

在 Magnification API 官方资料Magnification API Overview中有这么一段话:

The Magnification API is not supported under WOW64; that is, a 32-bit magnifier application will not run correctly on 64-bit Windows.

意思是说目前的 32-bit 的应用程序运行在 64-bit 系统上可能出问题。但是实践下来,32-bit 与 64-bit 的应用程序没有什么差异,个人认为微软应该可以重新整理一下这部分文档了。

主要的 API 中,MagSetWindowFilterList 支持设置一组待过滤的窗口句柄,在上面的场景中,表现为前文提到的浮窗,MagSetWindowSource 用来启动一次窗口数据抓取,具体数据通过 MagSetImageScalingCallback 设置的回调函数 Magimagescalingcallback 同步返回,我们可以在自定义的 Magimagescalingcallback 函数中拿到抓取的数据做进一步处理,理想情况下,这样就足够了。

当然,实际情况没有这么理想。还有好多“杂事儿”需要处理:

  1. 比如被分享的窗口被不小心关闭了,最小化了,大小改变了,隐藏了,甚至整个被分享的应用程序直接 crash 了。
  2. 每次返回的数据都需要申请一大块内存来保存,然后转发给其他处理环节,那么就需要维护内存申请的问题。对于 32-bit 的应用程序,用户内存空间仅有 2G(非常多 3G)大小,而一般涉及到音视频的应用程序中难免会有大量内存操作,这会进一步导致内存碎片的产生,如果碰到分享者在分享一个 4K 屏上全屏窗口的场景,那么很可能刚开始分享的一段时间内运行正常,在未来某个时间点,突然发生大块内存申请不到的情况。这就需要根据实际情况做具体的处理。
  3. 在某些 Nvidia 显卡(尤其是一些笔记本)上该接口会认真完成全套流程调用,但问题是返回的数据是纯黑屏数据,有时候是一个4×4大小的数据,于是我们需要检测返回的数据。
  4. 支持过滤的窗口有一个最大数值,测试下来大概是 24 个窗口左右,所以应该提示应用层的开发同学,使用过滤窗口的时候保持克制。
  5. Magnification API 接口对非主屏支持的比较差,还有主屏显示分辨率低于非主屏的场景,这时候不出意外是会出现 crash。

对于情况 1、2 我们可以写一些逻辑代码处理;对于情况 3、4、5 我们就不得不另外找保底方案来处理;尤其是情况5往往会直接 crash,这时候需要提前判断,采用保底方案。市面上大部分会议 App 都是创建一个独立的屏幕分享进程,这样即使这个进程崩溃也不会影响主进程的功能,大不了让用户重启一下分享功能。

2. Windwos GDI

GDI 方案的核心函数有两个: PrintWindow 和 BitBlt。

PrintWindow 内部实现是向目标窗口发送了一个消息(WM_PRINT 或者 WM_PRINTCLIENT)。PrintWindow 有一个优点是即使目标窗口被遮挡,也会把目标窗口的内容绘制出来,但缺点是这个函数在Windows8.1以下操作系统上无法获取到硬件加速区域的渲染数据,而且可能会造成目标窗口闪烁。Windows 8.1 及以上版本的系统,支持把 nFlags 设置为 PW_RENDERFULLCONTENT。这样即便是使用硬件加速区域的渲染数据也能获取到: PrintWindow(window_, mem_dc, PW_RENDERFULLCONTENT)。

相比 PrintWindow 而言, BitBlt 函数的优点是速度较快,从 Windows 7 开始支持了硬件加速,但其缺点是无法绘制被其他窗口遮挡的区域。

测试下来发现,Win8.1以上的系统,使用PrintWindow要比Magnification API性能高,可以获得较高得采集帧率。

针对无法使用 Magnification API 的情况,回退到 GDI 方案之后,又无法采集使用硬件加速渲染的应用,我们还有最后一个办法:通过采集目标窗口所在显示器的画面,计算目标窗口区域,再从截取到的显示器画面中把目标区域裁剪出来,从而得到最后的画面,但这个方案无法处理目标窗口被遮挡的场景。

3. Windows Grapics Capture(WGC API)

这种方案放在最后说,是因为微软推出的时间很晚,参考 New Ways to do Screen Capture。这个是千呼万唤始出来的技术,仅支持 Windows 10, Update(1809)(时间是2018-11-13)以及更高版本的操作系统。如果要使用 WGC,开发环境需要安装 Windows 10 SDK, version 1809(10.0.17763.0)及以上版本,这套 API 原本是给 UWP 程序使用的,如果想在 Win32 程序上使用,可以通过 C++/WinRT 来做。关于 C++/WinRT 的内容读者需要单独了解,这里就不做介绍了。

这套方案诞生之初,会给目标应用的窗口带一个的高亮的黄框,麻烦的是没有提供 API 控制这个黄框的开关,也不能换颜色,总之是无法控制,在这个问题被人们诟病了近3年之后,终于在 Windows 10,Update(21H1)(时间是2021-05-25)中加了一个控制开关  GraphicsCaptureSession.IsBorderRequired Property,开发环境需要更新 Windows SDK 到 10.0.20348.0 及以上版本。

同样地,WGC 也需要像放大镜 API 一样,手动处理目标窗口移动、大小变化、最小化、恢复、隐藏、关闭的场景。

WGC 可以根据产品的策略,决定 Windows 10, Update(1809)以上的系统是否都使用这种方案,或者是 Windows 10, Update(21H1)以上的系统才使用。

最后提一下,WGC 方案除了是官方针对屏幕分享场景而出的一套解决方案外,采集速度也是其他方案不能比拟的,这对于采集一些画面变化较快的场景(比如游戏,视频)来说,是一个很好的选择,可以比较完美地解决屏幕分享画面卡顿的问题。当然,天下没有免费的午餐,伴随高采集帧率而来的是较高的性能消耗。

(二)应用窗口采集策略 

针对上面介绍的技术,网易云信 SDK 在实现的时候做了如下策略:

  1. 如果系统高于 Windows 10, Update(1809),我们使用 WGC 方案;
  2. 其次是GDI 方案,如果采集对象是UWP窗口,或者当前运行在Win7系统上、没有开启Aero、并且采集窗口所在进程是黑名单项,那么执行回退策略;否则,继续使用 GDI 方案;
  3. Magnication API方案,在检测到采集的是黑屏数据,或者发现返回的数据格式不正确的时候,执行回退策略;
  4. 最后是通过 GDI 先采集指定显示器,然后裁剪目标区域的方式模拟应用采集。

(三) 显示器的采集 

这里,我们只讨论分享某一块显示器的场景,虚拟桌面场景方案类似。

1. Magnification API

同应用分享一样,我们优先考虑 Magnification API,因为这种方案支持过滤窗口,并且使用方法也是完全一样的,只不过是前面设置的是目标应用窗口的位置坐标,在这里换成要抓取的显示器坐标值。

2. DXGI

DXGI 用来管理独立于 Direct3D 图形运行时的低级任务。DXGI 为多个版本的 Direct3D 提供了一个通用框架。DXGI 的 Desk较好 Duplication 方案只能抓取显示器画面,正好可以使用在这种场景。DXGI 的架构图如下:

https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/images/dxgi-dll.png

Figure 1:DXGI架构

相比应用分享,有一些特殊的情况,比如正在分享的显示器(有意或者无意地)突然被移除了,这时候是直接退出分享?还是先暂停,然后等待显示器重新接上并继续分享呢?

3. GDI

GDI 也可以用在在采集屏幕上,而且兼容性很好,只是同样也不支持过滤窗口。

4. WGC

WGC 方案在使用上,相比与前面的应用窗口画面抓取,只需要注意把为窗口创建的 IGraphicsCaptureItem 换为显示器目标的即可,其余流程一致。这种方案只能通过SetWindowDisplayAffinity过滤本进程内的窗口。

(四) 显示器采集策略 

显示器采集的策略按照下面的原则执行:

如果系统高于 Windows 10, Update(20H1),我们使用 WGC 方案;

然后尝试使用 Magnification API,如果失败,执行回退策略;

尝试使用 DXGI 的方式采集显示器,如果不支持,那么执行回退策略;

最后一步尝试使用 GDI 方案。

三、macOS

Mac 上的取屏由于系统原本就提供了支持,比 Windows 上的简单很多,也不需要回退策略,故在此简单介绍,取屏的几个核心的方法是 Mac 上 CG 库提供的。

 (一)应用窗口采集 

核心方法是 CGWindowListCreateImage,为指定的一组窗口生成图片,同 Windows 系统上的应用窗口采集一样,需要处理窗口的变化。

 (二)显示器的采集 

显示器采集的核心方法是:

  1. CGWindowListCreateImageFromArray 支持从一组 Windows id 中生成一张图片;
  2. CGDisplayCreateImage 支持获取指定显示器的图片。

显示器分享时,对于过滤窗口的实现,大致思路是:先枚举屏幕上所有窗口,并把过滤窗口排除在外,得到一个窗口 id 数组;接着通过

CGWindowListCreateImageFromArray 得到这组窗口的一个截图 img_orig,然后通过计算过滤窗口的位置 rect,在 img_orig 中扣出 rect 处的图像 img_exclude。接着通过 CGDisplayCreateImage 得到目标显示器的截图 img_monitor,然后按照相对位置,把 img_exclude 盖在 img_monitor 上,这样就得到了一个看起来过滤掉某些窗口的显示器截图。

四、还有一些场景

在屏幕分享场景中,还有一些地方没有提及,比如 Windows 和 macOS 系统都支持一些视觉辅助功能,比如整屏图像放大,以及高清DPI。

macOS 和 Windows 10 上还有一个多桌面的概念,允许用户把一些程序拖动到这些桌面上运行。

Windows 上的 Office 和 WPS 的幻灯片应用,以及 macOS 中的 Keynote,开始播放幻灯片时,一般是新创建一个窗口,这时候如果不做特殊的处理,抓取到的就还是老的窗口显示数据,或者老的窗口可能已经被销毁了。

随着云信 G2 SDK 跟客户产品不断迭代更新,我们会为客户持续提供更加稳定易用的技术和产品,帮助各行各业的组织,连接和服务10亿人。

作者介绍

刘国元,网易云信资深开发工程师,长期从事桌面端开发,现专注于 RTC 方面的开发工作。

关于网易云信

网易云信:网易智企旗下融合通信云服务专家、通信与视频 PaaS 平台。集网易 24 年 IM 以及音视频技术打造的融合通信云服务专家,稳定易用的通信与视频 PaaS 平台。提供融合通信与视频的核心能力与组件,包含 IM 即时通讯、5G 消息平台、一键登录、信令、短信与号码隐私保护等通信服务,音视频通话、直播、点播、互动直播与互动白板等音视频服务,视频会议等组件服务,并联合网易易盾推出一站式安全通信方案「安全通」。目前,网易云信已经成功发送 1.6 万亿条消息,覆盖智能终端 SDK 数累计超过 186 亿,我们期待每个智能终端都有云信的融合通信能力。

文章标题:桌面端屏幕分享实践,发布者:网易智企,转载请注明出处:https://worktile.com/kb/p/6009

(3)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
网易智企的头像网易智企认证作者
上一篇 2022年3月17日 上午12:44
下一篇 2022年3月17日 上午12:49

相关推荐

  • 为什么数据库的触发器用的很少

    原因有:一、性能问题;二、维护问题;三、安全问题;四、不适用于所有情况;五、其他替代方法。数据库触发器可能会对数据库的性能产生一定的影响。当数据库中的触发器被激活时,它会自动执行一些操作,这可能会导致数据库的响应时间变慢。 一、性能问题 数据库触发器可能会对数据库的性能产生一定的影响。当数据库中的触…

    2023年5月29日
    67500
  • 专题知识库怎么构建

    专题知识库构建的步骤:一、明确知识库的目的和范围;二、选择适合的知识库工具;三、制定知识库管理规范和流程;四、收集和整理知识库内容;六、推广和使用知识库。在建立知识库之前,需要确定知识库的建立目的和覆盖范围。 一、明确知识库的目的和范围 在建立知识库之前,需要确定知识库的建立目的和覆盖范围。比如需要…

    2023年4月30日
    30900
  • 项目管理根本原因分析

    标题:项目管理根本原因分析 摘要:项目管理中的根本原因分析(RCA)致力于查找和解决问题的根本起因,以防止问题的重复出现。项目管理中,顺利的项目交付不仅依赖于执行效率,还取决于在遇到问题时识别并解决根本问题的能力。本文的核心观点包括:项目管理中使用RCA的重要性、RCA方法举例、问题解决过程中的挑战…

    2024年1月8日
    23500
  • 营销决策有哪些

    营销决策有:1、情感营销策略;2、体验营销策略;3、植入营销策略;4、口碑营销策略;5、事件营销策略;6、比附营销策略;7、饥饿营销策略;8、恐吓营销策略;9、会员营销策略。情感营销就是把消费者个人情感差异和需求作为企业品牌营销战略的核心。 1、情感营销策略 情感营销就是把消费者个人情感差异和需求作…

    2023年1月9日
    76300
  • 为什么没有vscode目录

    在探讨缺失的Visual Studio Code(VSCode)目录问题时,关键原因可概括为:缺乏必要的初始化、权限限制、路径错误、以及潜在的安装问题。其中,缺乏必要的初始化尤为关键,这意味着若VSCode未能正确启动或未按预期执行其初始化过程,则可能不会创建或更新其目录结构。这种情况通常发生在首次…

    2024年4月3日
    13700
  • 泛微oa办公系统是什么

    泛微OA办公系统是基于协同办公管理应用的办公自动化系统。可以帮助企业建立统一的协同信息交互和协同办公平台,支持整个企业的不同分支机构实现跨地域的、支持不同办公人员和业务人员根据业务项目的需要实现跨部门的协信息交流、共享和协同协作。 泛微OA办公系统是基于协同办公管理应用的办公自动化系统。可以帮助企业…

    2023年5月28日
    90100
  • 为什么vscode隐藏代码

    Visual Studio Code(VSCode)隐藏代码是一个实用特性,主要用于增加代码可读性、提高开发效率、保护敏感代码、优化代码管理。增加代码可读性是因为当我们处理大型文件时,能够通过折叠不相关的部分,让开发者集中注意力在当前工作的区域,从而减少视觉上的干扰和提高代码分析的效率。 一、增加代…

    2024年4月3日
    7000
  • 发文oa系统

    发文OA系统 是一种用于处理和管理办公室内部及对外文档的电子化办公软件。1、提高办公效率;2、确保信息安全;3、强化流程管理;4、实现文档共享;5、便捷的远程访问功能。 对于发文OA系统中的提高办公效率方面进行详细阐述,该系统通过自动化流程降低了文档处理的时间成本。例如,它允许用户快速地创建文档模板…

    2024年1月12日
    19100
  • 商家为什么不愿意用聚合支付

    商家对于使用聚合支付的犹豫主要涉及以下几个方面:1、费用问题;2、数据安全隐患;3、操作复杂性;4、法规合规风险;5、客户体验问题。其中,费用问题是让很多商家产生疑虑的核心因素,由于聚合支付平台通常会收取一定的手续费,可能增加商家的运营成本。 1、费用问题 聚合支付虽然提供了多种支付方式的整合,便捷…

    2023年8月10日
    71500
  • RTC技术在全球范围内的音视频通信表现

    实时通讯(RTC)技术在音视频通讯领域的表现十分杰出,表现在:1、实时互动性强、2、兼容性和互操作性好、3、延迟低、4、质量优、5、可伸缩性强。其中,实时互动性强表现在用户能即时传输和接收音视频数据,迅速响应对话另一方的信息,从而为在线教育、远程工作、虚拟会议等提供了有效支撑。 一、实时互动的性能表…

    2023年12月25日
    27800

发表回复

登录后才能评论

评论列表(1条)

  • 7985
    7985 2023年7月7日 下午6:15

    这个方案如下向dxgi一样满足如下需求
    1)如何拿单独鼠标(而不是将鼠标嵌入图里面)
    2)如何拿到赃区域(即变化区域,而不是全图)
    谢谢

注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部