技术干货 | 录屏采集实现教程 —— macOS桌面端
实时屏幕共享功能,在视频会议、游戏直播、在线教育等场景中已广泛被应用。近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势。
作为实时屏幕共享的第一步,录屏采集在不同终端和系统上的实现方式有所不同。之前我们已经分享了Android端,下面将分享本系列的第二篇,如何实现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 技术实现录屏采集
基本采样流程
在 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中获取输出流;
AVCaptureVideoDataOutput *captureVideoOutput = [[AVCaptureVideoDataOutput alloc] init];[captureVideoOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
第五步,使用CaptureConnections获取处理完成的流数据;
在数据采集中输入源有自己的硬件参数可以设置流控,输出源作为一个被动接受对象,它并没有太多流控设置,苹果巧妙地引入AVCaptureConnections解决这个问题。
打个比方,我们要控制蓄水池里面的水位。正确做法不是等水满了后把水放掉,而是换一个小点的入水管。AVCaptureConnections就是Session和Output中间的控制节点。每个Output与Session建立连接后,都会分配一个默认的AVCpatureConnection。我们屏幕采集的实时数据,也就是从AVCpatureConnection得到的。
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{}
通过以上5步,我们就完成桌面采集的数据了。
需要注意的是,在MacOS 10.15以后,用户采集桌面数据,需要在安全性与隐私,隐私权限里打开屏幕录制功能,否则不能采集到屏幕的画面。
优劣势分析
在具体实现中,还有两点需要特别提醒:
第一,在 startRunning 时,这个方法是一个阻塞调用,可能要花一些时间,因此你应该在串行队列上执行会话设置,以使主队列不被阻塞(使UI保持响应)。
第二,在正在运行的会话上的多个配置操作时,你需要结合beginConfiguration和commitConfiguration 批处理为原子更新。否则会产生意料之外的结果。
以上就是 AVCaptureSession录屏技术,它的优势是满足了录屏的基本需求,并且经过系统高度封装,实现起来毫不费力。由系统实现的流控可以完美平衡硬件和软件的性能。尤其是在低版本的设备上,这个优点尤为明显。
但同时,它也存在一些劣势。由于采样设置的指定,只能通过硬件deviceInput,所以它只能指定屏幕采集,而不能满足我们只采集指定的窗口,这在我们实际的使用中会有诸多限制。并且它不能很好的过滤掉我们不需要的窗口,而这个在实际应用中是非常普遍的需求。
有什么办法能够采集指定窗口的图像,或者过滤桌面的指定窗口呢?接下来,我们看第二种,基于Core Graphics技术的录屏采集。
基于 Core Graphics 技术实现录屏采集
基本采样流程
Core Graphics框架提供了Quartz Window Services。里面提供了很多关于macOS 窗口管理的相关信息。它包含了所有用户在屏幕上,或者不在屏幕上(即隐藏的窗口)的所有正在运行的应用窗口。还可以使用Quartz Window Services生成窗口内容的图像。
下面我们来看具体的采样实现流程:
首先我们需要获取所有的窗口列表;
CFArrayRef array =(CGWindowListCopyWindowInfo(kCGWindowListOptionAll | kCGWindowListExcludeDesktopElements | kCGWindowListOptionOnScreenOnly, kCGNullWindowID));
获取窗口后我们可以根据获取到的窗口列表获取每个窗口的图像;
CGImageRef windowImage = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming);
由于我们需要实时获取桌面的图像,图像的数据采集(生产者)和分发(消费者)不一定能够匹配。我们需要在生产者和消费者中间创建一个缓冲区。这样生产者只需要定时获取当前桌面或者窗口的图像,然后储存在缓冲区。另一端消费者也根据当前设置的帧率从缓存区获取相应的图像。
由于避免缓存区过大占用过多的系统内存,这里缓存区也会设置一个阈值。以保证不会过度消耗系统内存。这样我们就能够获取到桌面或者窗口的媒体数据流了。
绘制鼠标
经过以上基本采样流程,得到的画面内容是不含鼠标的,而在一次研讨会或在线课的材料共享环节,主讲人往往要通过鼠标,指明当前所讲的内容,因此必须将鼠标还原到画面中。
想要将鼠标绘制到里面,我们可以通过CGContext将系统全局鼠标图像合成入窗口,或者桌面的指定位置。这样我们就能获取到包含鼠标的图像数据了。
具体的实现可以参考以下示例代码:
// 创建绘制图像的上下文CGContextRef context = CGBitmapContextCreate(imgData,width,height,8, // 8 bits per componentbytesPerRow,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);
过滤窗口
由于在使用过程中,用户可能需要隐藏自己的指定窗口,不希望分享的人看到相关的信息。但同时又需要采集桌面的其他数据。这时我们就需要过滤掉不需要的窗口。
示例代码可参考下面的:
//列举出桌面所有窗口
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);
优劣势分析
在 macOS 平台上,Core Graphics是基于Quartz高级绘图引擎框架。它不仅可以截取桌面,也可以截取窗口图像,它所拥有的众多API还可以灵活的组合,帮助我们截取窗口或者桌面部分。并且,他还可以通过窗口组合来合成我们想要的图像,达到过滤我们不需要共享的窗口的目的。
它的劣势在于,因为API较为简陋,整体需要自己实现一套录屏的方案;对于采集的流控需要自己控制,比较繁琐;由于不断的采集重绘,CPU占用比较大。经过我亲测对比,同等分辨率下,相较于系统的AVCaptureSession的CPU占用率会多占用5~10%左右。
总结
最后我们来看一下macOS下实现录屏采集的两种方式以及各自的优劣势:
AVCaptureSession是在macOS下比较成熟的方案,性能优化的最好,实现也很方便,缺点是只能够截取屏幕,无法实现截取指定窗口,过滤窗口;
Core Graphics 的API 比较繁杂,比较耗费CPU占用。有一定的学习门槛,无法采集鼠标与动画,需要自己实现一套采集逻辑,但是支持采集窗口,过滤窗口,更加灵活多样,能够满足所有的功能。
技术干货 | 录屏采集实现教程 —— macOS桌面端相关推荐
- 技术干货 | 录屏采集实现教程 —— Windows桌面端
在进入具体的方式讨论前,我们先看看 Windows 桌面图形界面的简化架构,如下图: 在 Windows Vista 之前,Windows 界面的复合画面经由 Graphics Device Inte ...
- 技术干货 | 录屏采集实现教程 —— iOS端
实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...
- 技术干货 | 基于 Qt Quick Plugin 快速构建桌面端跨平台组件
导读:桌面端的 UI 开发框架对比移动端.Web 端的成熟方案,一直处于不温不火的状态.随着疫情掀起的风波,桌面端在线教育.视频会议等需求不断涌现.本文将围绕 Qt Quick 的优势来介绍如何快速创 ...
- Windows桌面端录屏采集实现
实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...
- Win7电脑系统录屏功能使用教程分享
Win7电脑系统录屏功能使用教程分享.电脑上其实不用下载也可以使用录制屏幕这个功能,很多用户不懂如何去开启这个功能的方法,如果你还没有学会自带录屏工具的使用方法,那么可以通过本文来进行学习. 操作步骤 ...
- 苹果手机和电脑怎么录屏?详细教程来了!
相信小伙伴身边有不少人使用的是苹果手机和电脑.安卓手机和windows电脑怎么录屏不少人都已经知道了,那么苹果手机和电脑怎么录屏 呢?现在,小编就来详细的教教大家如何录屏,快拿出小本本记下来哦! 一 ...
- VM虚拟机安装使用OBS直播录屏软件图文教程及注意事项
因为有时候会需要在虚拟机中用到 OBS 软件,所以奇技视频写了一个教程方便大家,也备为不时之需.OBS 用作录屏时,虽然在码率高的时候文件较大,但优点也很明显的,因为录下来的视频不会受到桌面广告弹窗. ...
- 录屏专家怎么用?录屏软件使用教程(附下载)
想要更好地录制电脑屏幕可以使用电脑录屏专家,但有很多人在安装录屏专家之后,不知道如何使用.录屏专家怎么用?怎样使用录屏专家录制电脑屏幕? 下面小编给您分享录屏软件使用的教程(附安装教程),需要的小伙伴 ...
- android 小米录制视频,小米手机如何录屏 小米手机录屏方法【教程】
相信很多人喜欢用小米,一个很大的原因便是功能丰富多样的MIUI.而本周,MIUI又更新了一个很多人非常期待的功能:录屏.目前支持的机型有小米3/4/5以及小米note,后续会继续完善. 对于这样一个贴 ...
最新文章
- 刚进园子,广州的冬天像夏天
- android 重启后进入安全模式_图文详细教程:台式电脑或笔记本也可以运行Android系统,凤凰OS...
- iOS定位服务与地图应用开发:高德地图开发
- android 实现表格横向混动_Flutter混合开发和Android动态更新实践
- java的本地文件操作
- offsetTop、offsetLeft、offsetWidth、offsetHeight、style中的样式
- 域控制器部署组策略,立即下发强制更新,显示“远程过程调用被取消”,错误代码 8007071a;以及RPC服务器不可用,800706ba【解决方案】
- 检查Python列表项是否在另一个字符串中包含一个字符串
- docker 删除镜像
- socket通信压力测试
- Qt对图像的二值化处理
- 测试服务器带宽的几种常用方法
- c语言中字符型char数据在内存中占,互联网常识:在c语言中char型数据在内存中的储存形式为什么...
- html3d建模,数百个 HTML5 例子学习 HT 图形组件 – 3D 建模篇
- ccboot最新服务器配置,使用CCBoot如何优化服务器和客户端网卡
- 苹果电脑上装Windows7
- SpringBoot笔记系列:(十)数据持久化Spring Data JPA
- cx_Freez打包Python批处理
- 比赛即实战!中国软件杯发布全新产业创新赛项,校企可联合参赛
- 甄别客户需求,提高解决问题的效率
热门文章
- Java开发微信公众平台之浅谈微信(一)
- 高校宿舍用电预付费及用电安全管理系统-安科瑞张宇洁
- 树莓派用WiringPi和Bcm2835库和PCA9685输出PWM
- 二氧化钛纳米片负载MIL100(Fe)|bmim][Tf2N]离子液体(IL)负载UiO-66-PEI(齐岳)
- 计算机考试一级的打开方式,小编教你计算机等级考试题库:一级MS Office考点(1)的正确打开方式...
- 云从科技:详解跨镜笔记
- 零基础python视频教程谁的好_零基础自学Python!自我提升视频教程,新手必看
- 教你使用 Reflector中的Reflexil插件反编译.NET,修复Help Viewer v2.0 - Error: .cab未经Microsoft签名
- python 使用代理ip爬虫
- 快速 fieldset 属性详解