屏幕共享
更新时间: 2023/10/19 07:52:37
在大型会议或在线教育等场景中,为了满足提升沟通效率的需求,主讲人或老师需要将本端的屏幕内容分享给远端参会者或在线学生观看。NERoom 支持屏幕共享功能,帮助您实时分享本端设备的屏幕内容,满足在线教育、互动娱乐、金融面签、视频会议等低延时高互动场景的需求。
功能介绍
通过 NERoom SDK 可以在视频通话中实现屏幕共享和音频共享,主播或连麦者可以将自己的屏幕内容、本地播放的音频流,分享给远端参会者或在线观众观看,从而提升沟通效率,一般适用于多人视频聊天、在线会议以及在线教育场景。
-
视频会议场景中,参会者可以在会议中将本地的文件、数据、网页、PPT 等画面分享给其他与会者,也可以通过共享音频,将本地的音频文件、PPT 背景音等所有系统声音分享给其他与会者,让其他与会者更加直观的了解讨论的内容和主题。
-
在线课堂场景中,老师可以通过屏幕共享将课件、笔记、教学内容等画面展示给远端的其他学生观看,也可以通过共享音频,将课件、教学视频的背景音等所有系统声音分享给远端的学生,降低传统教学模式下的沟通成本,提升教育场景的用户体验。
NERoom SDK 以辅流的形式实现屏幕共享,即单独为屏幕共享开启一路上行的视频流,摄像头的视频流作为主流,屏幕共享的视频流作为辅流,两路视频流并行,主播同时上行摄像头画面和屏幕画面两路画面。
实现流程
基于 iOS 系统的屏幕共享功能,您需要在 App Extension 中通过 iOS 原生的 ReplayKit 特性实现录屏进程,并配合主 App 进程进行推流。因此您在需要进行屏幕共享的时候,使用 Apple ReplayKit 框架进行屏幕录制,接收系统采集的屏幕图像,并将其发送给 SDK 以传输视频流数据。
屏幕共享的主要流程包括:
- 创建 App Group。 App Group 用于在主 App 进程和扩展程序之间进行视频数据和控制指令的传输。
- 通过 Xcode 在工程中创建一个 Target,类型为 Broadcast Upload Extension, 用于开启屏幕共享的进程。
- 添加 ReplayKit 扩展,并使用 Apple ReplayKit 框架进行屏幕录制。
- 将录屏数据作为自定义视频源发送给 SDK,并使用 SDK 进行视频流的传输。
注意事项
- 您需要先调用 joinRoom 进入房间,再通过 joinRtcChannel 加入音视频房间,才能使用屏幕共享功能。
- 屏幕共享功能目前仅适用于 iOS 12.0 及以上版本的系统。
- 主 App 进程的屏幕共享任务和系统录屏需使用相同的 App Group 名称。
添加 ReplayKit
步骤一 创建 App Group
-
在 Certificates, Identifiers & Profiles 页面注册 App Group。
操作步骤请参考 注册 App Group
-
为您的 App ID 启用 App Group 功能。 操作步骤请参考启用 App Group。
-
重新下载 Provisioning Profile 并配置到 XCode 中。
步骤二 创建 Extension 录屏进程
创建一个类型为 Broadcast Upload Extension 的 Target,用于存放屏幕共享功能的实现代码。
- 在 Xcode 中打开项目的工程文件。
- 在菜单中选择 Editor > Add Target。
- 在 iOS 页签中 选择 Broadcast Upload Extension,并单击 Next。
- 在 Product Name 中为 Extension 命名,然后单击 Finish。
步骤三 pod 依赖
-
V 1.16.0及之后版本: 在主工程的 podfile 文件中引入 NERoomKit/ScreenShare 库。
target 'Extension工程' do pod 'NERoomKit/ScreenShare', '~> 1.16.0' end
-
V 1.15.0及之前版本:
在主工程的 podfile 文件中引入 NEScreenShareBroadcaster 库。
pod 'NEScreenShareBroadcaster', '0.5.3'
步骤四 Extension工程
-
V 1.16.0及之后版本: Extension中的示例代码如下:
// .h 内容 #import <ReplayKit/ReplayKit.h> @interface SampleHandler : RPBroadcastSampleHandler @end // .m内容 #import "SampleHandler.h" #import <NERtcReplayKit/NERtcReplayKit.h> static NSString *kAppGroup = @"配置AppGroup"; @interface SampleHandler () <NEScreenShareSampleHandlerDelegate> @end @implementation SampleHandler - (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *, NSObject *> *)setupInfo { // User has requested to start the broadcast. Setup info from the UI extension can be supplied but // optional. NEScreenShareBroadcasterOptions *options = [[NEScreenShareBroadcasterOptions alloc] init]; options.appGroup = kAppGroup; // 设置采集帧率30帧 options.frameRate = 30; // 设置需要采集系统音频数据 options.needAudioSampleBuffer = YES; [[NEScreenShareSampleHandler sharedInstance] broadcastStartedWithSetupInfo:options]; NEScreenShareSampleHandler.sharedInstance.delegate = self; } - (void)broadcastPaused { // User has requested to pause the broadcast. Samples will stop being delivered. [[NEScreenShareSampleHandler sharedInstance] broadcastPaused]; } - (void)broadcastResumed { // User has requested to resume the broadcast. Samples delivery will resume. [[NEScreenShareSampleHandler sharedInstance] broadcastResumed]; } - (void)broadcastFinished { // User has requested to finish the broadcast. [[NEScreenShareSampleHandler sharedInstance] broadcastFinished]; } - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { [[NEScreenShareSampleHandler sharedInstance] processSampleBuffer:sampleBuffer withType:sampleBufferType]; } - (void)onRequestToFinishBroadcastWithError:(NSError *)error { [self finishBroadcastWithError:error]; } @end
-
V 1.15.0及之前版本:
在extension中创建继承自 NEScreenShareSampleHandler 的类。
// .h文件内容 #import <ReplayKit/ReplayKit.h> #import <NEScreenShareBroadcaster/NEScreenShareBroadcaster.h> @interface SampleHandler : NEScreenShareSampleHandler @end // .m内容 @implementation SampleHandler - (void)setupWithOptions:(NEScreenShareBroadcasterOptions *)options { options.appGroup = @"your App Group"; options.frameRate = 10; options.targetFrameSize = CGSizeMake(720, 0); } @end
共享本地屏幕
配置步骤
-
调用 getRoomContext 方法获取房间上下文。
调用此方法时,您需要将
roomUid
设置为您所加入房间的 ID。 -
调用 setupShareKit 方法初始化屏幕共享任务。
调用此方法时,您需要设置
appGroup
参数为您注册的 App Groups Identifier。加入房间后,只有第一次启用屏幕共享功能前需要先初始化屏幕共享任务。
-
在加入房间之后调用 startScreenShare 方法开启屏幕共享,以辅流形式发送屏幕共享内容。
开启屏幕共享后,房间内其他成员会收到 onMemberScreenShareStateChanged 回调,通知屏幕共享状态的变更。
/** 成员屏幕共享状态变更。 isSharing参数: - true: 房间内成员开启屏幕共享。 - false: 房间内成员关闭屏幕共享。 */ func onMemberScreenShareStateChanged(member: NERoomMember, isSharing: Bool)
-
若您要结束屏幕共享,请调用 stopScreenShare 方法关闭辅流形式的屏幕共享。
结束屏幕共享后,房间内其他成员会收到 onMemberScreenShareStateChanged 回调,通知屏幕共享状态的变更。
示例代码
初始化屏幕共享任务的示例代码如下:
// 加入RtcChannel成功后,需要调用 应用外屏幕共享初始化接口
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
roomContext.rtcController.setupShareKit(appGroup: "注册的App Groups Identifier")
开启屏幕共享功能的示例代码如下:
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
roomContext.rtcController.startScreenShare { code, string in
if code == 0 {
print("Successfully start screen share")
} else {
print("Failed to start screen share. Rease: \(string ?? "")")
}
}
关闭屏幕共享功能的示例代码如下:
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
roomContext.rtcController.stopScreenShare { code, string in
if code == 0 {
print("Successfully stop screen share")
} else {
print("Failed to stop screen share. Rease: \(string ?? "")")
}
}
观看远端屏幕共享
注意事项
必须在指定用户开启了屏幕共享辅流通道后,即收到 onMemberScreenShareStateChanged 回调后,才能订阅该远端用户的辅流。
配置步骤
- 远端用户加入音视频房间。
- 收到 onMemberScreenShareStateChanged 其他用户开启屏幕共享辅流通道的回调。
- 调用 subscribeRemoteSubStreamVideo 方法订阅远端的屏幕共享辅流视频,订阅之后才能接收远端的辅流视频数据。调用此方法时,您需要设置指定用户的 uid,同时设置 subscribe 参数为 YES。
- 收到 onMemberScreenShareStateChanged 其他用户关闭屏幕共享辅流通道的回调,结束屏幕共享。
示例代码
if member.isSharingScreen {
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
let code = roomContext.rtcController.subscribeRemoteSubStreamVideo(userUuid: "成员ID")
if code == 0 {
print("Success")
}
}
关闭某成员的屏幕共享
在会议等场景中,主持人可以关闭某成员的屏幕共享。
您可以在云信控制台上配置某角色是否具备该权限,只有具备该权限的角色才可以执行此操作。
配置步骤
- 调用
stopMemberScreenShare
关闭房间内某成员的屏幕共享。相关的参数说明如下表所示。
参数 | 说明 |
---|---|
userUuid | 待关闭屏幕共享的成员的 ID |
callback | 回调 |
- 房间内其他成员会收到
onMemberScreenShareStateChanged
回调,通知房间内其他成员关于屏幕共享状态的变更。
示例代码
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
roomContext.rtcController.stopMemberScreenShare(userUuid: "xxx") { code, str in
if code == 0 {
print("关闭成员屏幕共享成功")
} else {
print("关闭成员屏幕共享失败")
}
}
音频共享
配置步骤
- 在加入房间后调用
enableLoopbackRecording
方法。调用此方法时,您需要设置enable
参数为 true 开启音频共享。 - 需要结束音频共享时,调用
enableLoopbackRecording
方法并设置enable
参数为 false 停止音频共享。
示例代码
let roomContext = NERoomKit.shared().roomService.getRoomContext(roomUuid: "房间ID")
guard let roomContext = roomContext else {
return
}
let code = roomContext.rtcController.enableLoopbackRecording(enable: true)
if code == 0 {
print("开启音频共享成功")
} else {
print("开启音频共享失败")
}
相关API文档
方法 | 功能描述 |
---|---|
joinRoom | 加入 NERoom 的房间。 |
joinRtcChannel | 加入音视频房间。 |
getRoomContext | 获取房间上下文。 |
setupShareKit | 初始化屏幕共享任务。 |
startScreenShare | 开始屏幕共享。 |
stopScreenShare | 结束屏幕共享。 |
onMemberScreenShareStateChanged | 成员屏幕共享状态回调。 |
subscribeRemoteSubStreamVideo | 订阅或取消订阅指定远端用户辅流视频。 |
stopMemberScreenShare | 关闭房间内某成员的屏幕共享。 |
enableLoopbackRecording | 开启/关闭音频共享 |