写点什么

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

用户头像
ZEGO即构
关注
发布于: 3 小时前

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

 

作为实时屏幕共享的第一步,录屏采集在不同终端和系统上的实现方式有所不同。之前我们已经分享了 Android 端、iOS 端实现录屏采集的方式,本次将分享本系列的第二篇,如何实现 macOS 桌面端屏幕共享的录屏采集


在 macOS 系统下,说到媒体数据相关,就不得不提到 macOS 上 AVFoundation 多媒体框架。它是基于 Apple Darwin 操作系统上的视听媒体框架。从 macOSX lion,也就是 10.7 版本开始,它就是 macOS 平台的默认媒体框架。

 

AVFoundation 的核心类 AVCaptureSession 可用于捕捉视频和音频,协调视频和音频的输入和输出流。它为我们提供了管理输入设备、采集、输出、预览等一系列接口,这张图就是围绕 AVCaptureSession 的实现流程图,后面会有对该流程的详细介绍。



除了 AVCaptureSession,macOS 系统还有一套基于 Quartz 高级绘图引擎 Core Graphics 框架,它是位于 Apple Darwin 操作系统之上的绘图层框架,其中共有两种组件:

 

一种是 Quartz compositor 合成视窗系统,管理和合成幕后视窗影像来创建 Mac OS X 用户界面,另一种是 Quartz 2D,这是以 PDF 的规范为基础的图形库,用来绘制二维文字和图形。

 

在 macOS 中,Core Graphics 还包括用于处理显示硬件,低级用户输入事件和窗口系统的服务。

 

所以在 macOS 下实现录屏采集,基本上可以从 AVCaptureSession 或者 Core Graphics 两方面考虑。


基于 AVCaptureSession 技术实现录屏采集

01 基本采样流程

在 Apple 平台上有过音视频开发经验的开发者,应该都知道 AVCaptureSession 这个类, 除了会涉及到 AVCaptureSession 之外,我们还会涉及到例如 AVCaptureInput、AVCaptureOutput、AVCaptureVideoPreviewLayer 等一系列的类。AVCaptureSession 这个在其中就是充当会话者的角色, 我们可以理解为平时生活中的插线板,有输入,有输出。其中 AVCaptureDeviceInput 为输入, AVCapturePhotoOutput、AVCaptureMovieFileOutput 等是输出,它们之间的关系我们可以看一下这幅图:


具体录屏实现:


第一步,使用 Capture Session 管理数据流;


AVCaptureSession *screenCaptureSession = [[AVCaptureSession alloc] init];
复制代码

第二步,配置分辨率;

if ([self.screenCaptureSession canSetSessionPreset:AVCaptureSessionPresetHigh])      [self.screenCaptureSession setSessionPreset:AVCaptureSessionPresetHigh];
复制代码


第三步,配置 Capture Inputs 添加到 Session 中;


一个 AVCaptureInput 代表一种或多种媒体数据,比如输入设备可以同时提供视频和音频数据。每种媒体流代表一个 AVCaptureInputPort 对象。使用 AVCaptureConnection 可以将 AVCaptureInputPort 与 AVCaptureOutput 连接起来。这里我们采集桌面使用 AVCaptureScreenInput 就可以了


AVCaptureScreenInput *captureScreenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()];    if ([self.screenCaptureSession canAddInput:captureScreenInput])           [self.screenCaptureSession addInput:captureScreenInput];
复制代码


第四步,使用 Capture Outputs 从 Session 中获取输出流;


第五步,使用 CaptureConnections 获取处理完成的流数据;


在数据采集中输入源有自己的硬件参数可以设置流控,输出源作为一个被动接受对象,它并没有太多流控设置,苹果巧妙地引入 AVCaptureConnections 解决这个问题。

 

打个比方,我们要控制蓄水池里面的水位。正确做法不是等水满了后把水放掉,而是换一个小点的入水管。AVCaptureConnections 就是 Session 和 Output 中间的控制节点。每个 Output 与 Session 建立连接后,都会分配一个默认的 AVCpatureConnection。我们屏幕采集的实时数据,也就是从 AVCpatureConnection 得到的。

- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{}
复制代码


通过以上 5 步,我们就完成桌面采集的数据了。

 

需要注意的是,在 MacOS 10.15 以后,用户采集桌面数据,需要在安全性与隐私,隐私权限里打开屏幕录制功能,否则不能采集到屏幕的画面。

02 优劣势分析

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


第一,在 startRunning 时,这个方法是一个阻塞调用,可能要花一些时间,因此你应该在串行队列上执行会话设置,以使主队列不被阻塞(使 UI 保持响应)。

 

第二,在正在运行的会话上的多个配置操作时,你需要结合 beginConfiguration 和 commitConfiguration 批处理为原子更新。否则会产生意料之外的结果。

 

以上就是 AVCaptureSession 录屏技术,它的优势是满足了录屏的基本需求,并且经过系统高度封装,实现起来毫不费力。由系统实现的流控可以完美平衡硬件和软件的性能。尤其是在低版本的设备上,这个优点尤为明显。

 

但同时,它也存在一些劣势。由于采样设置的指定,只能通过硬件 deviceInput,所以它只能指定屏幕采集,而不能满足我们只采集指定的窗口,这在我们实际的使用中会有诸多限制。并且它不能很好的过滤掉我们不需要的窗口,而这个在实际应用中是非常普遍的需求。

 

有什么办法能够采集指定窗口的图像,或者过滤桌面的指定窗口呢?接下来,我们看第二种,基于 Core Graphics 技术的录屏采集。

 基于 Core Graphics 技术实现录屏采集

01 基本采样流程

Core Graphics 框架提供了 Quartz Window Services。里面提供了很多关于 macOS 窗口管理的相关信息。它包含了所有用户在屏幕上,或者不在屏幕上(即隐藏的窗口)的所有正在运行的应用窗口。还可以使用 Quartz Window Services 生成窗口内容的图像。

 

下面我们来看具体的采样实现流程:

 

首先我们需要获取所有的窗口列表;

CFArrayRef array =(CGWindowListCopyWindowInfo(kCGWindowListOptionAll | kCGWindowListExcludeDesktopElements | kCGWindowListOptionOnScreenOnly, kCGNullWindowID));
复制代码


获取窗口后我们可以根据获取到的窗口列表获取每个窗口的图像;

 CGImageRef windowImage = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming);
复制代码


由于我们需要实时获取桌面的图像,图像的数据采集(生产者)和分发(消费者)不一定能够匹配。我们需要在生产者和消费者中间创建一个缓冲区。这样生产者只需要定时获取当前桌面或者窗口的图像,然后储存在缓冲区。另一端消费者也根据当前设置的帧率从缓存区获取相应的图像。

 

由于避免缓存区过大占用过多的系统内存,这里缓存区也会设置一个阈值。以保证不会过度消耗系统内存。这样我们就能够获取到桌面或者窗口的媒体数据流了。

02 绘制鼠标

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

 

想要将鼠标绘制到里面,我们可以通过 CGContext 将系统全局鼠标图像合成入窗口,或者桌面的指定位置。这样我们就能获取到包含鼠标的图像数据了。

 

具体的实现可以参考以下示例代码:


 // 创建绘制图像的上下文 CGContextRef context = CGBitmapContextCreate(imgData,                                 width,                                 height,                                 8, // 8 bits per component                                 bytesPerRow,                           CGImageGetColorSpace(pSourceImage),// 将桌面窗口图像绘入上下文中CGContextDrawImage(context, bgBoundingBox, pSourceImage);// 将鼠标图像绘入上下文中CGContextDrawImage(context, CGRectMake(mouse_x , mouse_y ,mouse_w, mouse_h), [mouse CGImageForProposedRect:NULL context:NULL hints:NULL]);// 最后生成合入鼠标图像的桌面或者窗口图像CGImageRef pFinalImage = CGBitmapContextCreateImage(context);free(imgData);CGContextRelease(context);
复制代码


03 过滤窗口

由于在使用过程中,用户可能需要隐藏自己的指定窗口,不希望分享的人看到相关的信息。但同时又需要采集桌面的其他数据。这时我们就需要过滤掉不需要的窗口。


示例代码可参考下面的:

//列举出桌面所有窗口CFArrayRef onScreenWindows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);CFMutableArrayRef finalList = CFArrayCreateMutableCopy(NULL, 0, onScreenWindows);//过滤移除指定的窗口for (long i = CFArrayGetCount(finalList) - 1; i >= 0; i--) {  CGWindowID window = (CGWindowID)(uintptr_t)CFArrayGetValueAtIndex(finalList, i);  if ([self.excludeWindows containsObject:@(window)]) {     CFArrayRemoveValueAtIndex(finalList, i);  }}//最后根据剩下的窗口列表生成组合的图像CGImageRef windowImage = CGWindowListCreateImageFromArray(item.deskBounds, finalList, kCGWindowImageDefault);CFRelease(finalList);CFRelease(onScreenWindows);
复制代码

04 优劣势分析

在 macOS 平台上,Core Graphics 是基于 Quartz 高级绘图引擎框架。它不仅可以截取桌面,也可以截取窗口图像,它所拥有的众多 API 还可以灵活的组合,帮助我们截取窗口或者桌面部分。并且,他还可以通过窗口组合来合成我们想要的图像,达到过滤我们不需要共享的窗口的目的。

 

它的劣势在于,因为 API 较为简陋,整体需要自己实现一套录屏的方案;对于采集的流控需要自己控制,比较繁琐;由于不断的采集重绘,CPU 占用比较大。经过我亲测对比,同等分辨率下,相较于系统的 AVCaptureSession 的 CPU 占用率会多占用 5~10%左右。

总结


最后我们来看一下 macOS 下实现录屏采集的两种方式以及各自的优劣势:

 

AVCaptureSession 是在 macOS 下比较成熟的方案,性能优化的最好,实现也很方便,缺点是只能够截取屏幕,无法实现截取指定窗口,过滤窗口;

 

Core Graphics 的 API 比较繁杂,比较耗费 CPU 占用。有一定的学习门槛,无法采集鼠标与动画,需要自己实现一套采集逻辑,但是支持采集窗口,过滤窗口,更加灵活多样,能够满足所有的功能。

发布于: 3 小时前阅读数: 4
用户头像

ZEGO即构

关注

专注音视频领域19年 2020.04.15 加入

全球领先的音视频云服务商,已为映客、酷狗、喜马拉雅、荔枝、好未来、作业帮、掌门一对一、Live.Me、UpLive、Mico、平安科技等众多行业头部企业提供音视频云服务。

评论

发布
暂无评论
技术干货 | macOS桌面端录屏采集实现教程