重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

通过阅读本文,您可以了解 iOS 如何实现屏幕共享。DingRTC 的屏幕共享方案是基于 iOS 系统的 ReplayKit,能够分享整个系统的屏幕内容,但需要在您的 App 里额外创建一个 Extension 扩展组件。

一、创建 Broadcast Upload Extension

  1. 在 Xcode 菜单依次单击 File -> New -> Target,选择 Broadcast Upload Extension。

image

  1. 选中新建的 Target,单击 Capability添加 App Groups,如下图:

image

3. 填写 App Group Id,如下图。这一步要求您需要持有 Apple 的开发证书或个人账号。image

  1. 选中主 App 的 Target ,并按照上述的步骤 2、3 对主 App 的 Target 做同样的处理。

  2. 选中新建的 Target,添加 DingRTC.framework 到 Framework and Libraries,如下图:

image

6. 在新建的 Target 中,Xcode 会自动创建一个名为 SampleHandler.m 的文件,用如下代码进行替换。需将代码中的 kAppGroup 修改为上文中您填写的 App Group Id

#import "SampleHandler.h"
#import <DingRtc/DingRTC.h>

static NSString * _Nonnull kAppGroup = @"group.com.dingtalk.DingRTCSample";

@interface SampleHandler() <DingScreenShareExtDelegate>

@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.
    [[DingRtcScreenShareExt sharedInstance] setupWithAppGroup:kAppGroup 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 {
    // User has requested to finish the broadcast.
}

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    @autoreleasepool {
        [[DingRtcScreenShareExt sharedInstance] sendSampleBuffer:sampleBuffer type:sampleBufferType];
    }
}

#pragma mark - DingRtcScreenShareExtDelegate

- (void)finishBroadcastWithError:(DingRtcScreenShareExt *)broadcast error:(NSError *)error {
    [self finishBroadcastWithError:error];
}

@end

二、主 App 接收来自 Broadcast Upload Extension 进程的录屏数据

1. 在主 App 调用 -startScreenShare:mode: 传入上文的 App Group Id 来启动屏幕共享。

[DingRTCClient.instance.rtcEngine startScreenShare:@"group.com.dingtalk.DingRTCSample" mode:DingRtcScreenShareAll];

2. 等待用户触发屏幕分享(一般需要用户在 iOS 系统的控制中心,长按录屏按钮来触发),此外您也可以参考如下代码,在主 App 通过 RPSystemBroadcastPickerView 实现弹出一个启动器供用户确认启动屏幕分享。

警告
  • 苹果在 iOS 12.0 中增加了 RPSystemBroadcastPickerView 可以在应用中弹出启动器供用户确认启动屏幕分享。到目前为止 RPSystemBroadcastPickerView 尚不支持自定义界面,也没有官方的唤起方法。

  • 示例代码的原理是通过遍历 RPSystemBroadcastPickerView 的 Subviews 寻找 UIButton 并触发其点击事件。

  • 该方案不被苹果官方推荐,并可能在新一轮的系统更新中失效,因此仅供您参考,您需要自行承担选用此方案带来的风险。

@property (strong, nonatomic) RPSystemBroadcastPickerView *broadcastPickerView;

- (void)launchBroadcastPickerView {
    if (@available(iOS 12.0, *)) {
        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];
            }
        }
    }
}

3. 调用 -stopScreenShare 可以随时停止屏幕共享。

[DingRTCClient.instance.rtcEngine stopScreenShare];