如何基于 PANO SDK 实现 iOS 端屏幕共享互动
- 技术文章
屏幕共享作为视频通话功能中一项核心的技术能力,被广泛应用于教育、金融、视频会议等行业场景,提升在线互动的效率和执行力。通过屏幕共享,能解决在线教育、智慧课堂等场景中共享教学内容的痛点;也能满足在线理财、视频客服、金融双录场景中共享金融应用的痛点;还能高效连接企业内外,打破时间空间的限制,提供内外部会议、员工培训、企业直播等全场景解决方案。
拍乐云音视频PaaS云平台除了提供桌面端和 Android 端屏幕共享能力外,新增 iOS 端屏幕共享 SDK,本篇技术教程将教大家如何快速在 iOS 端接入屏幕共享功能,接入即用、高效开发,实现高清 ScreenShare。

iOS 上实现系统级屏幕共享需要通过 Broadcast Upload Extension 录屏扩展配合主 App 进行。录屏扩展负责接收屏幕图像数据,并通过 Socket 通信将数据回传给主 App,由主 App 负责发送数据。
接入流程
一、 配置 App Group(可选)
Apps within a group can communicate with other members in the group using IPC mechanisms including Mach IPC, POSIX semaphores and shared memory, and UNIX domain sockets.
注意:选择不配置 App Group 依然可以使用屏幕共享的功能,但可能影响数据传输稳定性。 所以建议尽量正确配置 App Group 以获得最佳体验。
3、在弹出框中双击选择App Groups;



4、如果选择配置 App Group ID 模式,则选中刚创建的 Extension 的 Target,执行在步骤 1 配置 App Group 对主 App 相同的设置(可选)。
- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
[PanoScreenSharingExt.sharedInstance setupWithAppGroup:kAppGroupId delegate:self];
}
- (void)broadcastFinished {
// User has requested to finish the broadcast.
[PanoScreenSharingExt.sharedInstance finishScreenSharing];
}
- (void)processSampleBuffer: (CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
switch (sampleBufferType) {
case RPSampleBufferTypeVideo:
// Handle video sample buffer
[PanoScreenSharingExt.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;
}
}
注意: 扩展 SDK 只需要接收 Video 数据
- (void)screenSharingFinished: (PanoScreenSharingResult)reason {
NSString *log;
switch (reason) {
case PanoScreenSharingResultVersionMismatch:
log = @"PanoReplayKitExt SDK version is mismatch with Pano SDK";
break;
case PanoScreenSharingResultCloseByHost:
log = @"Screen share is closed by host app";
break;
case PanoScreenSharingResultDisconnected:
log = @"The connect with host app is abnormally disconnected";
break;
default:
break;
}
NSError *error = [NSError errorWithDomain:NSStringFromClass([self classForCoder]) code:0 userInfo:@{ NSLocalizedFailureReasonErrorKey : log }];
[self finishBroadcastWithError:error];
}
四、 对接主 App 数据接收
在用户触发屏幕共享前,需要先在主 App 侧,使用 Pano SDK 开启屏幕共享,让主 App 准备接收 Extension 的录屏数据,按照如下步骤进行对接:
1、确保调用屏幕共享接口前,已经加入频道中。
2、调用startScreenWithAppGroupId:方法,传入步骤1配置 App Group中配置的 App Group ID, 如果没有则传入nil。调用成功后 SDK 则进入等待屏幕共享数据状态。
3、在onScreenStartResult:代理回调方法中判断屏幕共享状态。
4、等待用户通过 iOS 系统控制中心触发屏幕录制,或者通过步骤 5 使用系统提供的RPSystemBroadcastPickerView类来启动。控制中心启动需要用户长按屏幕录制按钮,并在弹出列表中选中当前 Extension 来启动,如下图示例:
5、通过系统提供的RPSystemBroadcastPickerView类可以实现从主 App 内唤起屏幕录制页面,将如下代码添加至需要启动屏幕录制的地方(可选):
-(void)launchBroadcastPickerView API_AVAILABLE(ios(12.0)){
if (!self.broadcastPickerView) { RPSystemBroadcastPickerView *pickerView = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
pickerView.showsMicrophoneButton = NO;
pickerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
NSString *pluginPath = [NSBundle mainBundle].builtInPlugInsPath;
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginPath error:nil];
for (NSString *content in contents) {
if (![content hasSuffix:@".appex"]) {
continue;
}
NSBundle *bundle = [NSBundle bundleWithPath:[[NSURL fileURLWithPath:pluginPath] URLByAppendingPathComponent:content].path];
if (bundle) {
NSString *identifier = [bundle.infoDictionary valueForKeyPath:@"NSExtension.NSExtensionPointIdentifier"];
if ([identifier isEqualToString:@"com.apple.broadcast-services-upload"]) {
pickerView.preferredExtension = bundle.bundleIdentifier;
}
}
}
self.broadcastPickerView = pickerView;
}
for (UIView *view in self.broadcastPickerView.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
[(UIButton *)view sendActionsForControlEvents:UIControlEventAllEvents];
}
}
}
注意: RPSystemBroadcastPicker-View 需要iOS 12 以上支持。通过 RPSystemBroadcastPicker-View 目前不支持自定义界面,仅能唤起屏幕录制启动页面供用户启动。 苹果官方并不推荐此方案,可能随着系统更新被禁止,因此您需要自行承担风险来选用此方案。
6、通过调用 stopScreen 来停止屏幕共享,用户也可以手动通过系统控制中心来停止。
五 、频道中其他用户观看屏幕共享
1、在 Pano SDK 代理回调onUserScreen-Start: 中接收用户开启屏幕共享事件:
- (void)onUserScreenStart:(UInt64)userId {
dispatch_async(dispatch_get_main_queue(), ^{
// Must be called from main thread.
[self.engineKit subscribeScreen:userId withView:self.playView];
});
}
2、实现 Pano SDK 代理回调接受其他屏幕共享事件:
- (void)onUserScreenStop:(UInt64)userId {
// Handle user stop screen sharing
}
- (void)onUserScreenMute:(UInt64)userId {
// Handle user mute screen sharing
}
- (void)onUserScreenUnmute:(UInt64)userId {
// Handle user unmute screen sharing
}
注意:当用户停止屏幕共享时,Pano SDK 会自动取消订阅,不需要再调用unsubscribeScreen:。
示例代码
