iOS 屏幕共享

该章节主要介绍和屏幕共享相关的功能。 包括:建立屏幕共享、开启屏幕共享、关闭屏幕共享;ReplayKit 2仅支持iOS 11.0 以上共享系统屏幕。基本流程是添加ReplayKit扩展、云信SDK创建加入房间并使用自定义采集、使用Socket在宿主App(主工程,这里是教育Demo)和扩展ReplayKit程序之间进行视频数据(音频使用宿主APP中云信SDK采集)和控制指令传输;

添加ReplayKit

1. 添加 Broadcast Upload Extension

选择Target,添加 Extension 添加Target

选择Broadcast Upload Extension

2. 在宿主App和Extension之间建立Socket连接,用于进程间通信

宿主App

- (void)setupServerSocket
{
    if (replayKitCapture) {
        self.serverSocket = [[NTESSocket alloc] initWithPort:@"8898" IP:@"127.0.0.1"];
        // 收到的数据通过 Delegate 接收
        self.serverSocket.delegate = self;
        // 开始接受客户端的连接请求
        [self.serverSocket startAcceptClient];
    }
}

SampleHandler 创建Socket用于发送视频数据

// 连接到宿主 Socket
- (BOOL)connectToHostApp {
    self.clientSocket = [[NTESSocket alloc] initWithPort:self.clientPort IP:self.ip];
    // 设置委托对象,用于接受对端数据
    self.clientSocket.delegate = self;

    // 向Server Socket发起连接
    BOOL success = [self.clientSocket connectToServerWithPort:@"8898"
                                                   IP:@"127.0.0.1"];
    if (success) {
        // 开始接收控制命令数据
       [self.clientSocket startRecv];
    }
    return success;
}

3. 压缩裁剪采集图片,发送到宿主App

ReplayKit 采集到的屏幕视频数据通过 processSampleBuffer:withType:给用户,忽略音频数据回调(我们使用云信SDK音频采集),将视频数据通过Socket发送到宿主App,然后再通过SDK自定义视频数据进行发送,

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {

    switch (sampleBufferType) {
        case RPSampleBufferTypeVideo:
            if(!self.connected) {
                return;
            }
            [self sendVideoBufferToHostApp:sampleBuffer];
            break;
        case RPSampleBufferTypeAudioApp:
            // Handle audio sample buffer for app audio
            break;
        case RPSampleBufferTypeAudioMic:
            // Handle audio sample buffer for mic audio
            break;

        default:
            break;
    }
}
- (void)sendVideoBufferToHostApp:(CMSampleBufferRef)sampleBuffer {
    CFRetain(sampleBuffer);
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // 原始视频数据过大(ReplayKit限制内存50M),裁剪视频数据
    NTESI420Frame *videoFrame = nil;
    videoFrame = [NTESYUVConverter pixelBufferToI420:pixelBuffer
                                                    withCrop:self.cropRate
                                                  targetSize:self.targetSize
                                              andOrientation:self.orientation];
    CFRelease(sampleBuffer);

    // To Host App
    if (videoFrame) {
        [self.clientSocket sendData:[videoFrame bytes]];
    }
}

4. 宿主App 通过SDK发送视频数据

#pragma mark - NTESSocketDelegate

- (void)onRecvData:(NSData *)data
{
    NTESI420Frame *frame = [NTESI420Frame initWithData:data];
    CMSampleBufferRef sampleBuffer = [frame convertToSampleBuffer];
    // SDK自定义视频数据发送接口
    [[NIMAVChatSDK sharedSDK].netCallManager sendVideoSampleBuffer:sampleBuffer];
    CFRelease(sampleBuffer);
}

云信SDK自定义采集

自定义采集开启并加入房间后,再启动ReplayKit

- (void)start {
 // _meeting 一些基本设置
 ...
 if(replayKitCapture) {
        // SDK采集对象置nil
        _meeting.option.videoCaptureParam = nil;
        // 创建自定义采集
        _meeting.option.customVideoParam = [[NIMNetCallCustomVideoParam alloc] init];
        _meeting.option.customVideoParam.videoFrameRate = 15;
    }
    // 加入房间
    [[NIMAVChatSDK sharedSDK].netCallManager joinMeeting:_meeting completion:nil];
}

开启屏幕共享

控制中心 -> 屏幕共享 -> 长按选择云信屏幕录制

控制中心启动屏幕共享

如果没有该按钮,则去系统设置-> 控制中心-> 自定义控制里添加上,

添加到界面上

选择扩展,开启(Demo上的逻辑:需要先加入房间,再开启ReplayKit)

开启

关闭屏幕共享

控制中心 -> 屏幕共享 -> 点击退出