开启屏幕共享

腾讯云 TRTC 在 iOS 平台下支持两种不同的屏幕分享方案:
应用内分享 即只能分享当前 App 的画面,该特性需要 iOS 13 及以上版本的操作系统才能支持。由于无法分享当前 App 之外的屏幕内容,因此适用于对隐私保护要求高的场景。
跨应用分享 基于苹果的 Replaykit 方案,能够分享整个系统的屏幕内容,但需要当前 App 额外提供一个 Extension 扩展组件,因此对接步骤也相对应用内分享要多一点。

支持的平台

iOS
Android
Mac OS
Windows
Electron
Chrome 浏览器

应用内分享

应用内分享的方案非常简单,只需要调用 TRTC SDK 提供的接口 startScreenCaptureInApp 并传入编码参数TRTCVideoEncParam 即可。该参数可以设置为 nil,此时 SDK 会沿用开始屏幕分享之前的编码参数。
我们推荐的用于 iOS 屏幕分享的编码参数是:
参数项
参数名称
常规推荐值
文字教学场景
分辨率
videoResolution
1280 × 720
1920 × 1080
帧率
videoFps
10 FPS
8 FPS
最高码率
videoBitrate
1600 kbps
2000 kbps
分辨率自适应
enableAdjustRes
NO
NO
由于屏幕分享的内容一般不会剧烈变动,所以设置较高的 FPS 并不经济,推荐10 FPS即可。
如果您要分享的屏幕内容包含大量文字,可以适当提高分辨率和码率设置。
最高码率(videoBitrate)是指画面在剧烈变化时的最高输出码率,如果屏幕内容变化较少,实际编码码率会比较低。

跨应用分享

iOS 系统上的跨应用屏幕分享,需要增加 Broadcast Upload Extension 录屏进程以配合主 App 进程进行推流。Extension 录屏进程由系统在需要录屏的时候创建,并负责接收系统采集到屏幕图像。因此需要:
1. 创建 App Group,并在 XCode 中进行配置(可选)。这一步的目的是让 Extension 录屏进程可以同主 App 进程进行跨进程通信。
2. 在您的工程中,新建一个 Broadcast Upload Extension 的 Target,并在其中集成 SDK 压缩包中专门为扩展模块定制的 TXLiteAVSDK_ReplayKitExt.framework
3. 对接主 App 端的接收逻辑,让主 App 等待来自 Broadcast Upload Extension 的录屏数据。
注意:
如果跳过步骤1,也就是不配置 App Group(接口传 nil),屏幕分享依然可以运行,但稳定性要打折扣,故虽然步骤较多,但请尽量配置正确的 App Group 以保障屏幕分享功能的稳定性。

步骤1:创建 App Group

使用您的帐号登录 https://developer.apple.com/ ,进行以下操作,注意完成后需要重新下载对应的 Provisioning Profile
1. 单击 Certificates, IDs & Profiles
2. 在右侧的界面中单击加号。
3. 选择 App Groups,单击 Continue
4. 在弹出的表单中填写 Description 和 Identifier,其中 Identifier 需要传入接口中的对应的 AppGroup 参数。完成后单击 Continue



5. 回到 Identifier 页面,左上边的菜单中选择 App IDs,然后单击您的 App ID(主 App 与 Extension 的 AppID 需要进行同样的配置)。
6. 选中 App Groups 并单击 Edit
7. 在弹出的表单中选择您之前创建的 App Group,单击 Continue 返回编辑页,单击 Save 保存。



8. 重新下载 Provisioning Profile 并配置到 XCode 中。

步骤2:创建 Broadcast Upload Extension

1. 在 Xcode 菜单依次单击 FileNewTarget...,选择 Broadcast Upload Extension
2. 在弹出的对话框中填写相关信息,不用勾选 Include UI Extension,单击 Finish 完成创建。
3. 将下载到的 SDK 压缩包中的 TXLiteAVSDK_ReplayKitExt.framework 拖动到工程中,勾选刚创建的 Target。
4. 选中新增加的 Target,依次单击 + Capability,双击 App Groups,如下图:

AddCapability


操作完成后,会在文件列表中生成一个名为 Target名.entitlements 的文件,如下图所示,选中该文件并单击 + 号填写上述步骤中的 App Group 即可。

AddGroup


5. 选中主 App 的 Target ,并按照上述步骤对主 App 的 Target 做同样的处理。
6. 在新创建的 Target 中,Xcode 会自动创建一个名为 "SampleHandler.h" 的文件,用如下代码进行替换。需将代码中的 APPGROUP 改为上文中的创建的 App Group Identifier
#import "SampleHandler.h"
@import TXLiteAVSDK_ReplayKitExt;

#define APPGROUP @"group.com.tencent.liteav.RPLiveStreamShare"

@interface SampleHandler() <TXReplayKitExtDelegate>

@end

@implementation SampleHandler
// 注意:此处的 APPGROUP 需要改成上文中的创建的 App Group Identifier。
- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
[[TXReplayKitExt sharedInstance] setupWithAppGroup:APPGROUP delegate:self];
}

- (void)broadcastPaused {
// User has requested to pause the broadcast. Samples will stop being delivered.
}

- (void)broadcastResumed {
// User has requested to resume the broadcast. Samples delivery will resume.
}

- (void)broadcastFinished {
[[TXReplayKitExt sharedInstance] finishBroadcast];
// User has requested to finish the broadcast.
}

#pragma mark - TXReplayKitExtDelegate
- (void)broadcastFinished:(TXReplayKitExt *)broadcast reason:(TXReplayKitExtReason)reason
{
NSString *tip = @"";
switch (reason) {
case TXReplayKitExtReasonRequestedByMain:
tip = @"屏幕共享已结束";
break;
case TXReplayKitExtReasonDisconnected:
tip = @"应用断开";
break;
case TXReplayKitExtReasonVersionMismatch:
tip = @"集成错误(SDK 版本号不相符合)";
break;
}

NSError *error = [NSError errorWithDomain:NSStringFromClass(self.class)
code:0
userInfo:@{
NSLocalizedFailureReasonErrorKey:tip
}];
[self finishBroadcastWithError:error];
}

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
switch (sampleBufferType) {
case RPSampleBufferTypeVideo:
[[TXReplayKitExt sharedInstance] sendVideoSampleBuffer:sampleBuffer];
break;
case RPSampleBufferTypeAudioApp:
// Handle audio sample buffer for app audio
break;
case RPSampleBufferTypeAudioMic:
// Handle audio sample buffer for mic audio
break;

default:
break;
}
}
@end


步骤3:对接主 App 端的接收逻辑

按照如下步骤,对接主 App 端的接收逻辑。也就是在用户触发屏幕分享之前,要让主 App 处于“等待”状态,以便随时接收来自 Broadcast Upload Extension 进程的录屏数据。
1. 确保 TRTCCloud 已经关闭了摄像头采集,如果尚未关闭,请调用 stopLocalPreview 关闭摄像头采集。
2. 调用 startScreenCaptureByReplaykit:appGroup: 方法,并传入 步骤1 中设置的 AppGroup,让 SDK 进入“等待”状态。
3. 等待用户触发屏幕分享。如果不实现 步骤4 中的“触发按钮”,屏幕分享就需要用户在 iOS 系统的控制中心,通过长按录屏按钮来触发,这一操作步骤如下图所示:
4. 通过调用 stopScreenCapture 接口可以随时中止屏幕分享。
// 开始屏幕分享,需要将 APPGROUP 替换为上述步骤中创建的 App Group Identifier。
- (void)startScreenCapture {
TRTCVideoEncParam *videoEncConfig = [[TRTCVideoEncParam alloc] init];
videoEncConfig.videoResolution = TRTCVideoResolution_1280_720;
videoEncConfig.videoFps = 10;
videoEncConfig.videoBitrate = 2000;
//需要将 APPGROUP 替换为上述步骤中创建的 App Group Identifier:
[[TRTCCloud sharedInstance] startScreenCaptureByReplaykit:videoEncConfig
appGroup:APPGROUP];
}

// 停止屏幕分享
- (void)stopScreenCapture {
[[TRTCCloud sharedInstance] stopScreenCapture];
}

// 屏幕分享的启动事件通知,可以通过 TRTCCloudDelegate 进行接收
- (void)onScreenCaptureStarted {
[self showTip:@"屏幕分享开始"];
}


步骤4:增加屏幕分享的触发按钮(可选)

截止到 步骤3 ,我们的屏幕分享还必须要用户从控制中心中长按录屏按钮来手动启动。您可通过下述方法实现类似腾讯会议的单击按钮即可触发的效果:
1. Demo 中的 TRTCBroadcastExtensionLauncher 文件实现了唤起屏幕分享,找到并将其加入到您的工程中。
2. 在您的界面上放置一个按钮,并在按钮的响应函数中调用 TRTCBroadcastExtensionLauncher 中的 launch 函数,就可以唤起屏幕分享功能了。
// 自定义按钮响应方法
- (IBAction)onScreenButtonTapped:(id)sender {
[TRTCBroadcastExtensionLauncher launch];
}

注意:
苹果在 iOS 12.0 中增加了 RPSystemBroadcastPickerView 可以从应用中弹出启动器供用户确认启动屏幕分享,到目前为止, RPSystemBroadcastPickerView 尚不支持自定义界面,也没有官方的唤起方法。
TRTCBroadcastExtensionLauncher 的原理就是遍历 RPSystemBroadcastPickerView 的子 View 寻找 UIButton 并触发了其点击事件。
但该方案不被苹果官方推荐,并可能在新一轮的系统更新中失效,因此 步骤4 只是一个可选方案,您需要自行承担风险来选用此方案。

观看屏幕分享

观看 Mac / Windows 屏幕分享 当房间里有一个 Mac / Windows 用户启动了屏幕分享,会通过辅流进行分享。房间里的其他用户会通过 TRTCCloudDelegate 中的 onUserSubStreamAvailable 事件获得这个通知。 希望观看屏幕分享的用户可以通过 startRemoteSubStreamView 接口来启动渲染远端用户辅流画面。
观看 Android / iOS 屏幕分享 若用户通过 Android / iOS 进行屏幕分享,会通过主流进行分享。房间里的其他用户会通过 TRTCCloudDelegate 中的 onUserVideoAvailable 事件获得这个通知。 希望观看屏幕分享的用户可以通过 startRemoteView 接口来启动渲染远端用户主流画面。