本文介绍了 iOS 系统中如何实现跨应用共享屏幕,以及可能出现的问题及解决方案。
前置条件
在 iOS 系统中实现跨应用共享屏幕之前,请确保:
iOS 操作系统版本为 iOS 12.2 或以上版本。
已创建可用的 App Group。
接入步骤
这部分分别介绍了如何接入 Extension 和主 App,实现手机屏幕的共享。
接入 Extension
创建 Broadcast Upload Extension。
通过 File > New > Target 菜单项,打开如下对话框。选择 Broadcast Upload Extension,创建新的 Extension Target。并点击 Next 按钮进行下一步的相关配置,本示例中将 Broadcast Upload Extension 的名称定义为 MRTCScreenShare。
点击 TARGET > Signing & Capabilities,选择 Apple Delevelper 账号,并配置 Bundle Identifier。
在 TARGETS 下选中已创建的 Target,如本示例中的 MRTCScreenShare,点击 +Capability > App Groups,选择目标 App Group。
在主 App 中进行验证。如果在屏幕录制选项中存在已创建的 MRTCScreenShare,即表示一切正常;否则请参见 系统屏幕录制弹出框中不包含当前的 Extension 以排除异常。
实现 Extension 逻辑。
为 Extension Target 添加依赖库。 将 APCocoaAsyncSocket、APScreenShare、libyuv 库,以及系统依赖库 libc++.tbd 添加到 Extension Target 中。
在 SampleHandler.m 中调用 APScreenShare.framework 以实现业务逻辑,即将系统回调委托给 MRTCBroadcastSampleHandler,同时 MRTCBroadcastSampleHandler 内部将帧数据传递到宿主 SDK。
#import "SampleHandler.h" #import <APScreenShare/MRTCDeviceScreenVideoCapturer.h> #import <APScreenShare/MRTCBroadcastSampleHandler.h> #import <APScreenShare/MRTCScreenShareCommom.h> @interface SampleHandler()<MRTCBroadcastSampleHandlerDelegate> @property (nonatomic, strong) MRTCBroadcastSampleHandler *sampleHandler; @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. MRTCLog(@"broadcastStartedWithSetupInfo"); if (!self.sampleHandler) { NSString *appGroupIdentifier = "你的App Group Identifier"; self.sampleHandler = [[MRTCBroadcastSampleHandler alloc] init]; [self.sampleHandler broadcastStartedWithAppGroupIdentifier:appGroupIdentifier delegate:self]; } } - (void)broadcastPaused { [self.sampleHandler broadcastPaused]; } - (void)broadcastResumed { [self.sampleHandler broadcastResumed]; } - (void)broadcastFinished { [self.sampleHandler broadcastFinished]; } - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { [self.sampleHandler processSampleBuffer:sampleBuffer withType:sampleBufferType]; } - (void)sampleHandler:(MRTCBroadcastSampleHandler *)sampleHandler didEncounterError:(NSError *)error { [self finishBroadcastWithError:error]; } @end
接入主 App
通过 TARGET > Signing & Capabilities > Capability > App Groups,选择目标 App Group。
重要此处选中的目标 App Group 必须与 Extension 中选中的 App Group 相同。
引入依赖库 APCocoaAsyncSocket、APScreenShare、libyuv,并引入头文件。
#import <ReplayKit/ReplayKit.h>#import <APScreenShare/MRTCDeviceScreenVideoCapturer.h>
初始化 MRTCDeviceScreenVideoCapturer 以接收 Extension 的数据。
MRTCDeviceScreenVideoCapturer *screenCapturer = [[MRTCDeviceScreenVideoCapturer alloc] initWithAppGroupIdentifier:"你的App Group Identifier" delegate:self]; [screenCapturer startCapture]; self.screenCapturer = screenCapturer;
发布自定义流。
ARTVCPublishConfig* config = [[ARTVCPublishConfig alloc] init]; config.videoSource = ARTVCVideoSourceType_Custom; ARTVCVideoProfileType videoProfile = [self screenShareVideoProfileFromSetting]; config.videoProfile = videoProfile; self.customPublishConfig = config; [_artvcEgnine publish:self.customPublishConfig];
准备自定义推流 customCapturer。
_artvcEgnine.autoPublish = NO; ARTVCCreateCustomVideoCaputurerParams* params = [[ARTVCCreateCustomVideoCaputurerParams alloc] init]; params.provideRenderView = YES; self.customCapturer = [_artvcEgnine createCustomVideoCapturer:params];
调用系统 Extension。 将 preferredExtension 设置为 Extension 的 Bundle Identifier。
APM_INFO(@"start screen sharing ios 12.2"); RPSystemBroadcastPickerView *picker = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; picker.center = self.view.center; self.broadcastPickerView = picker; picker.showsMicrophoneButton = NO; // 配置 自己的 id picker.preferredExtension = "您的extension bundle identifier"; [self.view addSubview:picker];
处理回调中自定义推流和错误。
- (void)capturer:(MRTCDeviceScreenVideoCapturer *)capturer didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer orientation:(CGImagePropertyOrientation)orientation { [self.customCapturer provideCustomVideoFramePeriodlyWith:CMSampleBufferGetImageBuffer(sampleBuffer)]; } - (void)capturer:(MRTCDeviceScreenVideoCapturer *)capturer didEncounterError:(NSError *)error { [self stopScreenSharing]; }
结束推流。
//1. 停止接受屏幕共享数据 [self.screenCapturer stopCapture]; //2. unpublish feed ARTVCUnpublishConfig* config = [[ARTVCUnpublishConfig alloc] init]; config.feed = self.customLocalFeed; [_artvcEgnine unpublish:config]; //3. 清理broadcastPickerView if (self.broadcastPickerView && self.broadcastPickerView.superview) { [self.broadcastPickerView removeFromSuperview]; self.broadcastPickerView = nil; }
常见问题
系统屏幕录制弹出框中出现多个 Extension
解决方案:将 picker.preferredExtension 的值设置为您的 Extension Bundle Identifier。
picker.preferredExtension = "您的extension bundle identifier";
系统屏幕录制弹出框中不包含当前的 Extension
解决方案:请检查 Extension 是否被打包到您的 App 中。如果没有,请在 TARGET > Build Phases 的 Embed App Extensions 中添加您的 Extension。
当 App 和 Extension 一方停止共享后,另外一方需要停止,却没有停止
解决方案:
检查 App 和 Extension 的 App Groups 设置,并确保:
App 和 Extension 中使用的 App Group 相同。
Debug/Release 模式下,均添加 App Group。