iOS 系统中如何实现跨应用共享屏幕

本文介绍了 iOS 系统中如何实现跨应用共享屏幕,以及可能出现的问题及解决方案。

前置条件

在 iOS 系统中实现跨应用共享屏幕之前,请确保:

  • iOS 操作系统版本为 iOS 12.2 或以上版本。

  • 已创建可用的 App Group。

接入步骤

这部分分别介绍了如何接入 Extension 和主 App,实现手机屏幕的共享。

接入 Extension

  1. 创建 Broadcast Upload Extension。

    1. 通过 File > New > Target 菜单项,打开如下对话框。选择 Broadcast Upload Extension,创建新的 Extension Target。并点击 Next 按钮进行下一步的相关配置,本示例中将 Broadcast Upload Extension 的名称定义为 MRTCScreenShare

      78dc6ed6feafdccbc376623d14dd6a76.png
    2. 点击 TARGET > Signing & Capabilities,选择 Apple Delevelper 账号,并配置 Bundle Identifier。

    3. TARGETS 下选中已创建的 Target,如本示例中的 MRTCScreenShare,点击 +Capability > App Groups,选择目标 App Group。

      image.png
    4. 在主 App 中进行验证。如果在屏幕录制选项中存在已创建的 MRTCScreenShare,即表示一切正常;否则请参见 系统屏幕录制弹出框中不包含当前的 Extension 以排除异常。

      123
  2. 实现 Extension 逻辑。

    1. 为 Extension Target 添加依赖库。 将 APCocoaAsyncSocket、APScreenShare、libyuv 库,以及系统依赖库 libc++.tbd 添加到 Extension Target 中。

    2. 在 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

  1. 通过 TARGET > Signing & Capabilities > Capability > App Groups,选择目标 App Group。

    重要

    此处选中的目标 App Group 必须与 Extension 中选中的 App Group 相同。

  2. 引入依赖库 APCocoaAsyncSocket、APScreenShare、libyuv,并引入头文件。

    #import <ReplayKit/ReplayKit.h>#import <APScreenShare/MRTCDeviceScreenVideoCapturer.h>
  3. 初始化 MRTCDeviceScreenVideoCapturer 以接收 Extension 的数据。

     MRTCDeviceScreenVideoCapturer *screenCapturer = [[MRTCDeviceScreenVideoCapturer alloc] initWithAppGroupIdentifier:"你的App Group Identifier" delegate:self];
     [screenCapturer startCapture];
     self.screenCapturer = screenCapturer;
  4. 发布自定义流。

     ARTVCPublishConfig* config = [[ARTVCPublishConfig alloc] init];
     config.videoSource = ARTVCVideoSourceType_Custom;
     ARTVCVideoProfileType videoProfile = [self screenShareVideoProfileFromSetting];
     config.videoProfile = videoProfile;
     self.customPublishConfig = config;
     [_artvcEgnine publish:self.customPublishConfig];
  5. 准备自定义推流 customCapturer。

     _artvcEgnine.autoPublish = NO;
     ARTVCCreateCustomVideoCaputurerParams* params = [[ARTVCCreateCustomVideoCaputurerParams alloc] init];
     params.provideRenderView = YES;
     self.customCapturer = [_artvcEgnine createCustomVideoCapturer:params];
  6. 调用系统 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];
  7. 处理回调中自定义推流和错误。

    - (void)capturer:(MRTCDeviceScreenVideoCapturer *)capturer didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer orientation:(CGImagePropertyOrientation)orientation {
        [self.customCapturer provideCustomVideoFramePeriodlyWith:CMSampleBufferGetImageBuffer(sampleBuffer)];
    }
    
    - (void)capturer:(MRTCDeviceScreenVideoCapturer *)capturer didEncounterError:(NSError *)error {
        [self  stopScreenSharing];
    }
  8. 结束推流。

     //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 PhasesEmbed App Extensions 中添加您的 Extension。

当 App 和 Extension 一方停止共享后,另外一方需要停止,却没有停止

解决方案:

检查 App 和 Extension 的 App Groups 设置,并确保:

  • App 和 Extension 中使用的 App Group 相同。

  • Debug/Release 模式下,均添加 App Group。

    image.png