屏幕共享
更新时间: 2024/03/15 17:20:42
在大型会议或在线教育等场景中,主讲人或老师可以将本端的屏幕内容分享给远端参会者或在线学生观看,从而提升沟通效率。NERTC 支持屏幕共享功能,用户可以实时分享本端设备的屏幕内容。
功能介绍
NERTC SDK 以辅流的形式实现屏幕共享,即单独为屏幕共享开启一路上行的视频流,摄像头的视频流作为主流,屏幕共享的视频流作为辅流,两路视频流并行,主播同时上行摄像头画面和屏幕画面两路画面。
通过网易云信 NERTC SDK 的屏幕共享功能,您可以快速实现如下功能:
-
共享整个屏幕
共享整个屏幕,包括屏幕中的所有信息。
-
共享 App 窗口
共享已打开的某个 App 的窗口。
-
共享指定区域
支持用户设置共享屏幕画面中的一部分固定区域,例如应用窗口中的部分画面,特定幻灯片内容区域等。
-
高亮显示共享窗口的边框
支持开启高亮显示共享窗口的边框,方便共享者快速识别当前正在共享的窗口。界面效果类似下图所示。
-
屏蔽指定 App 窗口
支持设置屏蔽指定应用程序窗口,以便在共享屏幕时保护敏感信息和隐私。
-
重新共享新的窗口
支持在屏幕共享过程中快速切换共享的窗口。
共享屏幕的应用场景包括:
场景 | 描述 |
---|---|
视频会议 | 参会者可以在会议中将本地的文件、数据、网页、PPT 等画面分享给其他与会者,让其他与会者更加直观的了解讨论的内容和主题。 |
在线教育 | 老师可以通过屏幕共享将课件、笔记、教学内容等画面展示给远端的其他学生观看,降低传统教学模式下的沟通成本,提升教育场景的用户体验。 |
此外 NERTC SDK 还支持在共享屏幕的同时,也共享本地播放的系统背景音。具体请参考音频共享。
示例项目
网易云信提供 ScreenShare 示例项目源码,您可以参考该源码实现屏幕共享。
注意事项
对于 V5.3.0 及之后版本,如果当前正在使用本地视频辅流通道进行本地摄像头采集或者外部自定义视频输入,调用 startScreenCaptureByDisplayId
或 startScreenCaptureByWindowId
开启屏幕共享时,需要先调用enableLocalVideo
停止辅流。如果当前正在屏幕共享,调用 enableLocalVideo
开启辅流时,需要调用 stopScreenCapture
先停止屏幕共享。
API 调用时序
sequenceDiagram
participant 应用A as 共享者
participant NERtcSDK
participant 应用B as 观看者
Note over 应用A, NERtcSDK: 获取可共享的窗口列表
应用A->>NERtcSDK: getScreenCaptureSources
NERtcSDK-->>应用A: 返回窗口信息列表
Note over 应用A, NERtcSDK: 设置画布并开启屏幕共享
应用A->>NERtcSDK: setupLocalSubStreamVideoCanvas
应用B->>NERtcSDK: setRemoteSubSteamRenderMode
应用A->>NERtcSDK: 开始共享 <br> startScreenCaptureByDisplayId <br>或 startScreenCaptureByWindowId
NERtcSDK-->>应用A: onScreenCaptureStatus
NERtcSDK-->>应用B: 收到远端用户开启屏幕共享的回调 <br> onUserSubStreamVideoStart
应用B->>NERtcSDK: 订阅远端的屏幕共享辅流视频 <br> subscribeRemoteVideoSubStream
Note over 应用A, NERtcSDK: 管理屏幕共享任务
应用A ->>NERtcSDK: updateScreenCaptureParameters 等
Note over 应用A, NERtcSDK: 关闭屏幕共享
应用A ->>NERtcSDK: stopScreenCapture
NERtcSDK -->>应用A: onScreenCaptureStatus
NERtcSDK-->>应用B: onUserSubStreamVideoStop
本端共享屏幕
1. 获取可共享的窗口列表
调用 getScreenCaptureSources
接口,获取可共享窗口和屏幕的列表,列表中包含窗口 ID 和屏幕 ID 等重要信息。
根据获取到的窗口信息,您可以实现一个缩略图列表页面,方便用户通过列表中的缩略图选择共享某个显示器的屏幕或某个窗口。页面效果类似如下图所示。
示例代码如下:
int count = 0;
NERtcSize thumb_size = nertc::NERtcSize(128, 72);
NERtcSize icon_size = nertc::NERtcSize(32, 32);
bool include_screen = true;
auto source_list = nrtc_engine_->getScreenCaptureSources(thumb_size, icon_size, include_screen);
if (source_list) {
count = source_list->getCount();
}
for (int index = 0; index < count; index++) {
NERtcScreenCaptureSourceInfo info = source_list->getSourceInfo(index);
// 使用 info 做app层 UI 的上信息的展示
}
if (source_list) {
source_list->release();
}
V5.4.10 版本开始支持获取窗口列表。
2. 获取共享区域的坐标
获取指定的待共享区域在整个显示器屏幕中的 Rectangle 坐标。如果要共享整个显示器,那么这个可以直接传 nertc::NERtcRectangle(0,0,0,0)。
示例代码如下:
bool NRTCEngine::startMonitorShare(const uint32_t &screenIndex)
{
auto screens = QGuiApplication::screens();
auto screen = screens.at(screenIndex);
nertc::NERtcRectangle sourceRectangle;
sourceRectangle.x = screen->geometry().x();
sourceRectangle.y = screen->geometry().y();
sourceRectangle.width = screen->geometry().width() * screen->devicePixelRatio();
sourceRectangle.height = screen->geometry().height() * screen->devicePixelRatio();
nertc::NERtcRectangle regionRectangle = { 0, 0, 0, 0 };
nertc::NERtcScreenCaptureParameters params;
params.bitrate = 0;
params.frame_rate = 5;
params.profile = nertc::kNERtcScreenProfileMAX;
params.capture_mouse_cursor = true;
params.dimensions.width = sourceRectangle.width - sourceRectangle.x;
params.dimensions.height = sourceRectangle.height - sourceRectangle.y;
}
3. 开启屏幕共享
-
初始化后通过
setupLocalSubStreamVideoCanvas
设置本端的辅流视频回放画布。 -
(可选)通过
setLocalSubStreamRenderMode
修改本端的辅流渲染缩放模式。 -
加入房间后,开启屏幕共享并设置屏幕共享的方式,屏幕共享内容以辅流形式发送。
对于 V5.3.0 及之后版本:
- 如果当前正在使用本地视频辅流通道进行本地摄像头采集或者外部自定义视频输入,调用
startScreenCaptureByDisplayId
或startScreenCaptureByWindowId
开启屏幕共享时,需要先调用enableLocalVideo
停止辅流。 - 如果当前正在屏幕共享,调用
enableLocalVideo
开启辅流时,需要调用stopScreenCapture
先停止屏幕共享。
场景 接口 关键参数配置 共享整个屏幕 startScreenCaptureByDisplayId
display_id
设置为getScreenCaptureSources
返回的屏幕 ID。region_rect
设置为{0,0,0,0},表示采集整个屏幕大小。
共享整个 App 窗口 startScreenCaptureByWindowId
window_id
设置为getScreenCaptureSources
返回的窗口 ID。region_rect
设置为{0,0,0,0},表示采集整个窗口大小。
共享指定区域 startScreenCaptureByWindowId
-
window_id
设置为getScreenCaptureSources
返回的窗口 ID。 region_rect
参数的坐标值请通过步骤2获取,具体如何设置请参考设置屏幕共享的窗口范围。
startScreenCaptureByDisplayId
display_id
设置为getScreenCaptureSources
返回的屏幕 ID。region_rect
参数的坐标值请通过步骤2获取,具体如何设置请参考设置屏幕共享的窗口范围。
其中
capture_params
参数的说明如下表所示。capture_params 参数说明
参数 参数说明 prefer 屏幕共享编码策略倾向: kNERtcSubStreamContentPreferMotion
(默认):内容类型为动画。当用户共享的内容是视频、电影或游戏等动态画面时,推荐选择此枚举值;此时frame_rate
参数完全按照您的设置处理。kNERtcSubStreamContentPreferDetails
:内容类型为细节。当用户共享的内容是图片、文字或 PPT 等静态画面时,推荐选择此枚举值;此时frame_rate
参数最高可设置为 10 帧。
profile 视频编码的分辨率。
具体请参考NERtcScreenProfileType
。- 建议直接设置为
kNERtcScreenProfileCustom
,以确保所有设置符合您的真实业务场景。 - 若您需要使用自定义的尺寸(
dimensions
)和帧率(frame_rate
),请务必设置此参数为kNERtcScreenProfileCustom
。 - 若设置
profile
为kNERtcScreenProfileCustom
之外的值,尺寸dimensions
会自动对应为profile
指定的大小,帧率frame_rate
固定为 5 fps。
dimensions 视频编码的最大像素值,可以设置视频尺寸的宽、高。 此参数仅当 profile
参数设置为kNERtcScreenProfileCustom
有效。frame_rate 视频编码的帧率。
单位为 fps,默认值为 5,建议此参数的值不要超过 15。min_framerate 视频编码的最小帧率。
默认值为 0,表示使用默认的最小帧率。bitrate 视频编码的码率,单位为 Kbps。
若设置的码率为 0 或超出合理范围,SDK 会自行计算出合理区间处理码率,具体请参考分辨率、帧率、码率参照表。min_bitrate 视频编码的最小码率,单位为 Kbps。 excluded_window_count 待屏蔽窗口的数量。 待屏蔽窗口数量建议不要超过 10 个,最好是小于 4 个。如果要屏蔽的窗口数量太多,您可以多次调用 `setExcludeWindowList` 接口。 excluded_window_list 待屏蔽窗口的列表。 capture_mouse_cursor 屏幕共享时是否采集鼠标。 window_focus 调用 startScreenCaptureByWindowId
方法共享窗口时,是否将该窗口前置。degradation_preference 带宽受限时的视频编码降级偏好。详细信息请参考 NERtcDegradationPreference。 enable_high_performance 是否开启高性能模式(只在分享屏幕时会生效),开启后屏幕采集性能最佳,但无法过滤远端的高亮边框,默认为 true。 目前仅针对macOS生效。 常见场景的参数推荐搭配如下表所示。
参数名称 共享视频 共享 PPT prefer kNERtcSubStreamContentPreferMotion kNERtcSubStreamContentPreferDetails profile kNERtcScreenProfileCustom kNERtcScreenProfileCustom dimensions {1920, 1080} {1920, 1080} frame_rate 15 5 - 如果当前正在使用本地视频辅流通道进行本地摄像头采集或者外部自定义视频输入,调用
开启屏幕共享之后,本地会触发 onScreenCaptureStatus
回调,远端会触发 onUserSubStreamVideoStart
回调。
4. (可选)设置屏幕共享的窗口范围
屏幕共享时,您可能需要限制共享屏幕的窗口范围,例如部分涉及敏感信息的窗口区域不进行屏幕共享。NERTC SDK 支持设置共享屏幕的区域范围,目前可通过以下方式实现:
-
开启屏幕共享时,仅共享指定窗口的部分区域。
通过
startScreenCaptureByWindowId
开启窗口维度的屏幕共享,并通过参数region_rect
指定共享的窗口范围。 -
开启屏幕共享时,仅共享指定屏幕的部分区域,并排除部分窗口。
-
屏幕共享过程中,动态调整需要屏蔽的窗口列表。
共享整个屏幕时,您可以通过
setExcludeWindowList
设置窗口过滤,将一个或多个需要过滤的窗口或应用排除出共享范围,其他观看端只能看到整个屏幕中指定窗口以外的其他内容。
5. (可选)高亮显示共享窗口的边框
您可以开启高亮显示共享窗口的边框,方便共享者快速识别当前正在共享的窗口。界面效果类似下图所示。
V5.4.10 版本开始支持该功能。
在调用 startScreenCaptureByDisplayId
或 startScreenCaptureByWindowId
开启屏幕共享时,将 capture_params
中的 enable_high_light
设置为 true
,开启共享屏幕边框高亮显示。并设置 high_light_width
、high_light_color
和 high_light_length
。
屏幕分享过程中,不支持通过 updateScreenCaptureParameters
动态修改高亮边框的参数。
推荐使用 SDK 提供的接口实现高亮框。如果您的应用想要自行实现自定义高亮框,请将 enable_high_light
设置为 false
,此时会有一个 onScreenCaptureSourceDataUpdate
回调。
参数说明如下表所示。
参数 | 参数说明 |
---|---|
enable_high_light | 是否开启共享窗口的边框高亮显示,本场景中请设置为 true ,默认为 false 。 |
high_light_width | 高亮边框的线条宽度,单位为 px,默认为 6 px。 |
high_light_color | 高亮边框的颜色,使用0xAABBGGRR 格式,默认值是 0xFF7EDE00 |
high_light_length | 高亮边框的线条长度,从窗口的任何一个角(共4个角)作为原点,到相邻两边延伸的长度。单位为 px,默认为 120 px。 设置为 -1 表示全包围的高亮框。 |
5. 管理屏幕共享任务
updateScreenCaptureParameters
:更新屏幕共享参数。pauseScreenCapture
:暂停屏幕共享。resumeScreenCapture
:恢复屏幕共享。updateScreenCaptureRegion
:更新屏幕共享区域。setExcludeWindowList
:设置共享指定屏幕区域时,需要屏蔽的窗口列表。setScreenCaptureMouseCursor
:设置屏幕共享时是否显示鼠标。setScreenCaptureSource
:在屏幕共享过程中快速切换共享的窗口。
6. 关闭屏幕共享
通过 stopScreenCapture
关闭辅流形式的屏幕共享。此时本地会触发 onScreenCaptureStatus
回调,远端会触发 onUserSubStreamVideoStop
回调。
若您在屏幕共享过程中,SDK 触发 kScreenCaptureStatusAbort
回调,这是由于当前共享的窗口被关闭、进程崩溃等原因导致目标窗口无效。您需要在此事件的响应函数中调用 stopScreenCapture
结束屏幕共享进程。
示例代码
cpp// 获取当前可共享的采集源
int count = 0;
NERtcSize thumb_size = nertc::NERtcSize(128, 72); // 设置缩略图尺寸
NERtcSize icon_size = nertc::NERtcSize(32, 32); // 设置图标尺寸
bool include_screen = true; // 设置获取到的采集源中是否包含显示器,默认为 `true`。
auto source_list = nrtc_engine_->getScreenCaptureSources(thumb_size, icon_size, include_screen);
if (source_list) {
count = source_list->getCount();
}
for (int index = 0; index < count; index++) {
NERtcScreenCaptureSourceInfo info = source_list->getSourceInfo(index);
// 使用 info 做app层 UI 的上信息的展示,参考后面选择启动屏幕分享的代码中,关于 `info的使用方法
}
// 设置本地辅流画布
nertc::NERtcVideoCanvas canvas;
canvas.cb = nullptr;
canvas.user_data = nullptr;
canvas.window = window;
canvas.scaling_mode = nertc::kNERtcVideoScaleFit;
rtc_engine_->setupLocalSubStreamVideoCanvas(&canvas);
// 更新辅流画布缩放模式
rtc_engine_->setLocalSubStreamRenderMode(nertc::kNERtcVideoScaleCropFill);
// 获取本地辅流启动参数,下面举了两种类型的例子:动画模式和细节模式
// case 1: 动画模式 (流畅度优先),共享视频等高帧率场景
nertc::NERtcScreenCaptureParameters capture_params;
capture_params.profile = nertc::kNERtcScreenProfileCustom; // 建议全部使用custom模式,不要使用预置的profile。
capture_params.dimensions = {1920, 1080};
capture_params.frame_rate = 15; // 动画模式最高支持30fps,具体设置多少帧,可参考实际业务和测试效果
capture_params.bitrate = 0;
capture_params.capture_mouse_cursor = true;
capture_params.window_focus = true;
// 设置过滤窗口,这里假设exclude_wnd_list_中保存了客户代码中设置的过滤窗口列表
HWND* wnd_list = nullptr;
int index = 0;
if (!exclude_wnd_list_.empty()) {
// 记得在调用了 startScreenCaptureBy*** 系列函数之后,释放内存 wnd_list
wnd_list = new HWND[exclude_wnd_list_.size()];
for (auto e : exclude_wnd_list_) {
*(wnd_list + index++) = e;
}
}
capture_params.excluded_window_list = (nertc::source_id_t*)wnd_list;
capture_params.excluded_window_count = exclude_wnd_list_.size(); //指定排除窗口的个数
capture_params.prefer = nertc::kNERtcSubStreamContentPreferMotion; // 设置动画模式
// case 2: 细节模式 (清晰度优先)共享文档/ppt推荐
nertc::NERtcScreenCaptureParameters capture_params;
capture_params.profile = nertc::kNERtcScreenProfileCustom;
capture_params.dimensions = {1920, 1080};
capture_params.frame_rate = 5; // 细节模式最高支持10fps
capture_params.bitrate = 0;
capture_params.capture_mouse_cursor = true;
capture_params.window_focus = true;
// 设置过滤窗口,这里假设exclude_wnd_list_中保存了客户代码中设置的过滤窗口列表
HWND* wnd_list = nullptr;
int index = 0;
if (!exclude_wnd_list_.empty()) {
// 记得在调用了 startScreenCaptureBy*** 系列函数之后,释放内存 wnd_list
wnd_list = new HWND[exclude_wnd_list_.size()];
for (auto e : exclude_wnd_list_) {
*(wnd_list + index++) = e;
}
}
capture_params.excluded_window_list = (nertc::source_id_t*)wnd_list;
capture_params.excluded_window_count = exclude_wnd_list_.size(); //指定排除窗口的个数
capture_params.prefer = nertc::kNERtcSubStreamContentPreferDetails;// 设置细节模式
// 高亮框设置,目前不支持通过 `updateScreenCaptureParameters` 来更新。
capture_params.enable_high_light = true; // 使用SDK内置的高亮框方案
capture_params.high_light_length = 120; // 默认尺寸,如果想要设置全包围高亮框,可以设置为 -1.
capture_params.high_light_color = 0xFF7EDE00; // 颜色格式为 0xAABBGGRR。
capture_params.high_light_width = 6; // 默认高亮框宽度
// 启动辅流 case 1:根据指定显示器ID来启动屏幕分享
// 比如当前系统接入了 2 台显示器,坐标分别是 display1: {x=0, y=0, width=1920, height=1080}, display2: {x=1920, y=0, width=1920, height=1080}
// 当 region_rect 设置为 {0,0,0,0} 时,表示采集整个显示器;如果设置了某一个范围,比如 {x=200,y=30, width=800, height=600},则表示在 screen_rect 内,采集 左下角坐标为 {x=200, y=30},大小为 {width=800, height=600} 的范围。
// 采集显示器, 适用于Windows和macOS。通过 `startScreenCaptureByDisplayId` 结合 `getScreenCaptureSources` 返回的数据,得到 `nertc::NERtcScreenCaptureSourceInfo` 类型对象,
// 假定用户选中采集源为 `info`,其类型为显示器 `nertc::kScreen`。
// 以共享 `info.source_id` 整个区域为例
nrtc_engine_->startScreenCaptureByDisplayId(info.source_id, { 0, 0, 0, 0 }, capture_params);
// 以共享 `info.source_id` 的部分区域为例
nrtc_engine_->startScreenCaptureByDisplayId(info.source_id, { 200, 30, 800, 600 }, capture_params);
// 启动辅流 case 2:根据特定窗口句柄,选择应用窗口分享
// 当 region_rect 设置为 {0,0,0,0} 时,表示采集整个窗口大小。当设置了一个范围,则表示仅分享设定范围内的窗口画面
// 假定用户选中采集源为 `info`,其类型为显示器 `nertc::kWindow`。
nrtc_engine_->startScreenCaptureByWindowId(info.source_id, { 0,0,0,0 }, capture_params);
// 如果设置了过滤窗口,那么这里可能是一个比较好的释放内存的地方
if (wnd_list) {
delete[] wnd_list;
wnd_list = nullptr;
}
// 开启屏幕分享之后,就可以释放 source_list.
if (source_list) {
source_list->release();
}
//更新屏幕共享参数
nrtc_engine_->updateScreenCaptureParameters(captureParams);
// 暂停屏幕共享
nrtc_engine_->pauseScreenCapture();
// 恢复屏幕共享
nrtc_engine_->resumeScreenCapture();
// 更新取屏区域
nrtc_engine_->updateScreenCaptureRegion({ 0,0,640,480 });
// 更新屏幕共享时的鼠标显示状态
nrtc_engine_->setScreenCaptureMouseCursor(true);
// 如果想在共享的过程中切换其他采集源,可以通过 `getScreenCaptureSources` 获取到的数据,得到新的采集源 `info`,然后按照下面的方法做快速切换,效果上等同于先调用 `stopScreenCapture`,然后再调用 `startScreenCaptureByXXX`。
nrtc_engine_->setScreenCaptureSource(info, {0,0,0,0}, capture_params);
// 停止屏幕共享
nrtc_engine_->stopScreenCapture();
观看远端屏幕共享
实现方法
-
设置远端辅流画布。
- 远端用户加入房间时,可以通过
onUserJoined
事件获取远端用户 ID,并通过setupRemoteSubStreamVideoCanvas
设置指定远端用户的的辅流视频画布。 - (可选)通过
setRemoteSubSteamRenderMode
设置远端的屏幕共享辅流视频渲染缩放模式。
- 远端用户加入房间时,可以通过
-
订阅远端用户的屏幕共享流。
- 收到
onUserSubStreamVideoStart
远端用户开启屏幕共享辅流通道的回调。 - 通过
subscribeRemoteVideoSubStream
订阅远端的屏幕共享辅流视频,订阅之后才能接收远端的辅流视频数据。
- 收到
-
结束屏幕共享。
收到
onUserSubStreamVideoStop
其他用户关闭辅流的回调,结束屏幕共享。
示例代码
cpp// 远端辅流处理-------------------------------------------
// 设置远端辅流画布
nertc::NERtcVideoCanvas canvas;
canvas.cb = nullptr;
canvas.user_data = nullptr;
canvas.window = window;
canvas.scaling_mode = kNERtcVideoScaleFit;
rtc_engine_->setupRemoteSubStreamVideoCanvas(uid, &canvas);
// 更新远端辅流画布缩放模式
rtc_engine_->setRemoteSubSteamRenderMode(uid, kNERtcVideoScaleCropFill);
// 监听远端辅流开启
void onUserSubStreamVideoStart(nertc::uid_t uid, nertc::NERtcVideoProfileType max_profile) override{
//订阅远端辅流
rtc_engine_->subscribeRemoteVideoSubStream(uid, true);
//取消订阅远端辅流
rtc_engine_->subscribeRemoteVideoSubStream(uid, false);
}
//监听远端辅流停止
void onUserSubStreamVideoStop(uid_t uid) override{
// 取消远端辅流画布
rtc_engine_->setupRemoteSubStreamVideoCanvas(uid, nullptr);
}
(可选)枚举屏幕列表或窗口列表
对于 V5.4.10 之前版本,NERTC SDK 不支持枚举屏幕列表或窗口列表,您需要在应用层自行实现枚举屏幕的方法。
V5.4.10 及之后版本不需要该操作,直接调用 SDK 接口获取屏幕列表和窗口列表。
NERTC SDK 暂不支持枚举屏幕列表或窗口列表,您需要在应用层自行实现枚举屏幕的方法。 如果您基于 Qt 来开发音视频能力,请注意 Qt 中通过 QWidget::winId() 得到的 WId 类型的值在不同平台上有差异:
- Qt 在 macOS 平台上的实现返回的是窗口的句柄 HWND,即窗口 ID,可以直接用于通过 SDK 调用
startScreenCaptureByWindowId
等方法时设置 window_id 参数。 - Qt 在 macOS 平台上的实现返回的是 NSView 对象指针,而非窗口 ID,调用 SDK 方法需要的是 NSWindow 窗口的 ID,即成员 windowNumber。若您需要通过 WId 类型的值获取 macOS 平台窗口的 ID,可以参考以下代码。
/////// file: macx_helper.h
#ifndef HIDETITLEBAR_H
#define HIDETITLEBAR_H
#include <QQuickWindow>
#include <QScreen>
#include <QGuiApplication>
class MacXHelpers : public QObject
{
Q_OBJECT
public:
MacXHelpers() {}
public slots:
int getWindowId(WId wid);
long getFirstDisplayScreenDisplayID();
};
#endif // HIDETITLEBAR_H
/////// file: macx_helper.mm
#include "macx_helpers.h"
#import <AppKit/AppKit.h>
int MacXHelpers::getWindowId(WId wid)
{
NSView *nativeView = reinterpret_cast<NSView *>(wid);
NSWindow* nativeWindow = nativeView.window;
if (nativeWindow)
{
return nativeWindow.windowNumber;
}
return 0;
}
long MacXHelpers::getFirstDisplayScreenDisplayID()
{
NSArray<NSScreen*>* screens = [NSScreen screens];
for (NSScreen* screen in screens) {
NSDictionary* deviceDescription = [screen deviceDescription];
id screenNumber = [deviceDescription objectForKey:@"NSScreenNumber"];
NSString* displayID = [NSString stringWithFormat:@"%@",screenNumber];
return [displayID integerValue];
}
return 0;
}