You need to enable JavaScript to run this app.
导航
进阶功能
最近更新时间:2024.11.13 11:25:47首次发布时间:2023.06.05 21:38:35

本文介绍如何实现 iOS 开播 SDK 的进阶功能。

前提条件

您已集成 iOS 开播 SDK。详见 集成 iOS 开播 SDK

录屏直播

主持人可以实时共享屏幕画面,而无需有线或无线连接的繁琐步骤,大大降低了直播门槛。录屏直播适用于游戏直播或演示效果直播等场景。

注意

  • 该功能仅适用于 iOS 12 及以上版本。
  • iOS 16.4 硬编存在问题,会导致直播画面模糊。

完成以下步骤实现该功能:

  1. 在工程的 Podfile 文件中添加 ScreenShare 依赖。

    # 只集成开播 SDK
      pod'BDLive', '1.47.0', :subspecs => [
        'LiveStreaming',
        'ScreenShare' 
      ]
      
    # 同时集成开播 SDK 和观播 SDK
      pod'BDLive', '1.47.0', :subspecs => [
       'LiveStreaming',
       'Viewer',
       'ScreenShare'
      ]
    end
    
  2. 打开终端窗口,执行 cd 命令进入您的工程目录。执行 pod install 命令安装依赖。

  3. 在 Xcode 中,完成以下步骤,创建屏幕共享扩展的 Target。

    1. 单击 File > New > Target
      Image
    2. iOS 页签下,选择 Broadcast Upload Extension,即屏幕共享扩展。单击 Next
      Image
    3. 输入 Target 名称并单击 Finish
      Image

      说明

      • 录屏直播功能暂不支持 Broadcast Setup UI Extension 扩展。如需自定义录屏直播的界面 UI,您必须实现 Broadcast Setup UI Extension 扩展的必要逻辑并选择 Include UI Extension 复选框。
      • 请将屏幕共享扩展的 Bundle Identifier 记录在某个安全的地方。在步骤 7 中,您必须将该 Bundle Identifier 传入 SDK。
  4. 在 Xcode 中完成以下步骤,实现屏幕共享扩展和 App 之间的数据共享。

    1. 进入步骤 3 中创建的扩展 Target 中。

    2. 单击 Signing & Capabilities 页签并单击 + Capability
      Image

    3. 双击对话框中的 App Groups
      Image

    4. 单击 App Groups 区域的 +

      说明

      如果您有可用的 App Group,可直接选中该 App Group 而无需新建。请将该 App Group 的 Container ID 记录在某个安全的地方。在步骤 6 和 7 中,您必须将该 Container ID 传入 SDK。

    5. 在弹出的对话框中,输入以 group. 开头的 Container ID 并单击 OK

      注意

      请将该 Container ID 记录在某个安全的地方。在步骤 6 和 7 中,您必须将该 Container ID 传入 SDK。

      Image
      App Group 新建成功后,会自动在 App Groups 区域中被选中。

    6. 进入 App Target 中,重复步骤 4-b4-c。在 App Groups 区域中,选择在扩展 Target 中被选中的 App Group。

    详见 Configuring App Groups

  5. 在 Xcode 中,完成以下步骤,实现屏幕采集的逻辑。

    1. 进入步骤 3 中创建的扩展 Target 中。
    2. General 页签下,单击 Frameworks and Libraries 区域的 +
      Image
    3. 在弹出的对话框中,选择工程下的 VolcEngineRTCScreenCapturer.xcframework 并单击 Add
      Image
    4. VolcEngineRTCScreenCapturer.xcframework 的嵌入方法设置为 Do Not Embed
      Image

      说明

      录屏直播功能仅对 Deployment Info 区域选定版本及以上的 iOS 设备可见。

  6. 在步骤 3 中创建的扩展 Target 中,Xcode 自动创建以下文件:SampleHandler.hSampleHandler.m。在 Xcode 中打开 SampleHandler.m 文件,实现屏幕采集逻辑。示例代码如下所示。

    #import "SampleHandler.h"
    #import <VolcEngineRTCScreenCapturer/ByteRTCScreenCapturerExt.h>
    
    @interface SampleHandler () <ByteRtcScreenCapturerExtDelegate>
    
    @property (nonatomic, assign) BOOL shouldScreenShare;
    
    @end
    
    @implementation SampleHandler
    
    - (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
        // groupId:步骤 4 中选中的 App Group 的 Container ID
        [[ByteRtcScreenCapturerExt shared] startWithDelegate:self groupId:xxx];
        // 如果主持人在 App 的预览页(即可选择录屏直播的页面)中,屏幕共享扩展将收到 App 发出的 onNotifyAppRunning 回调。如果扩展在 2 秒内没有收到 onNotifyAppRunning 回调,则认为主持人未在 App 的预览页,您应该通过调用 finishBroadcastWithError: 方法停止屏幕采集
        // 按需自定义 NSLocalizedFailureReasonErrorKey 的值
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (!self.shouldScreenShare) {
                NSDictionary *dic = @{
                    NSLocalizedFailureReasonErrorKey : @"The host is not in the live room"};
                NSError *error = [NSError errorWithDomain:RPRecordingErrorDomain
                                                     code:RPRecordingErrorUserDeclined
                                                 userInfo:dic];
                [self finishBroadcastWithError:error];
            }
        });
    }
    
    
    - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    
        // 混流
        switch (sampleBufferType) {
            case RPSampleBufferTypeVideo: // 采集到的屏幕视频流
            case RPSampleBufferTypeAudioApp: // 采集到的设备音频流
                [[ByteRtcScreenCapturerExt shared] processSampleBuffer:sampleBuffer withType:sampleBufferType]; // 使用该方法,将采集到的屏幕视频流和设备音频流推送至观看页
                break;
            case RPSampleBufferTypeAudioMic:
                // 采集到的麦克风音频流
                // 开播 SDK 实现了麦克风音频流的采集并将其推送至观看页。因此,您无需在此处理麦克风音频流
                break;
    
            default:
                break;
        }
    }
    
    /// 在主持人停止共享屏幕时触发该回调,通知您停止屏幕采集
    - (void)onQuitFromApp {
        // 按需自定义 NSLocalizedFailureReasonErrorKey 的值
        NSDictionary *dic = @{
            NSLocalizedFailureReasonErrorKey : @"You stopped sharing the screen"};
        NSError *error = [NSError errorWithDomain:RPRecordingErrorDomain
                                             code:RPRecordingErrorUserDeclined
                                         userInfo:dic];
        [self finishBroadcastWithError:error];
    }
    
    /// App 在后台被终止时触发该回调,通知您停止屏幕采集
    - (void)onSocketDisconnect {
        // 按需自定义 NSLocalizedFailureReasonErrorKey 的值
        NSDictionary *dic = @{
            NSLocalizedFailureReasonErrorKey : @"Disconnected"};
        NSError *error = [NSError errorWithDomain:RPRecordingErrorDomain
                                             code:RPRecordingErrorUserDeclined
                                         userInfo:dic];
        [self finishBroadcastWithError:error];
    }
    
    /// 检测到 App 正在运行时触发该回调
    - (void)onNotifyAppRunning {
        self.shouldScreenShare = YES;
    }
    
    @end
    
  7. 在 Xcode 的 App Target 中,打开定义如何进入直播间的文件并添加以下代码。详见进入直播间

    if (@available(iOS 12.0, *)) {
       model.extensionBundleId = @"BUNDLE_ID"; // 将 BUNDLE_ID 替换为屏幕共享扩展的 Bundle Identifier
       model.groupId = @"GROUP_ID"; // 将 GROUP_ID 替换为步骤 4 中选中的 App Group 的 Container ID
    }
    

自定义消息

BDLCustomIMService

收到自定义的 IM(长链接消息)信令。
IM 信令消息目前适用于发送自定义消息的场景,您可以通过监听自定义消息实现自己的业务逻辑。

onReceiveIMString:

自定义的 IM 信令收到回调。

- (void)onReceiveIMString:(NSString *)string;

参数

名称

类型

说明

string

NSString

通过 SendCustomSystemMessageAPI 接口发送的自定义的 IM 信令。

onConnected

IM 建立连接成功回调。

- (void)onConnected;

onConnectFailed

IM 建立连接失败回调。

- (void)onConnectFailed;

自定义美颜

新增、修改或删除美颜。

注意

  • 如需增加美颜,请联系 CV 技术支持获取美颜文件。
  • 修改美颜指修改美颜在开播页的图标名称和样式。

以下示例代码新增、修改、删除了指定美颜:

model.effectConfig.customizeBeauties = ^(NSMutableArray<BDLEffectBeautyModel *> * _Nonnull beauties) {
    // 美颜文件的 bundle 目录
    NSString *beautyPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"ComposeMakeup.bundle"];
    
    [beauties enumerateObjectsUsingBlock:^(BDLEffectBeautyModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 修改美颜在开播页的图标名称和样式。此处以修改 beautyKey 为 whiten 的美颜为例。请联系企业直播技术支持获取美颜对应的 beautyKey
        if ([obj.beautyKey isEqualToString:@"whiten"]) {
            obj.name = [NSString stringWithFormat:@"TT %@", beauties.firstObject.name]; // 美颜图标在开播页展示的名称
            // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置美颜图标。此处以通过 iconName 设置美颜图标为例
            obj.iconURL = nil;
            obj.iconName = @"uncheck";
            *stop = YES;
        }
    }];
    
    [beauties.copy enumerateObjectsUsingBlock:^(BDLEffectBeautyModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 删除美颜。此处以删除 beautyKey 为 Internal_Deform_Overall 的美颜为例。请联系企业直播技术支持获取美颜对应的 beautyKey
        if ([obj.beautyKey isEqualToString:@"Internal_Deform_Overall"]) {
            [beauties removeObject:obj];
            *stop = YES;
        }
    }];
    
    // 新增美颜
    BDLEffectBeautyModel *model = [[BDLEffectBeautyModel alloc] init];
    // 美颜图标在开播页展示的名称
    model.name = @"Name";
    // 美颜文件的资源目录。请联系 CV 技术支持获取美颜文件
    model.resFilePath = [beautyPath stringByAppendingPathComponent:@"ComposeMakeup/reshape_lite"];
    // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置美颜图标。此处以通过 iconName 设置美颜图标为例
    model.iconName = @"bdl_bottom_button_product";
// 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置美颜图标。此处以通过 iconURL 设置美颜图标为例
//  model.iconURL = [NSURL fileURLWithPath:[beautyPath stringByAppendingPathComponent:@"icon/icon_naixiong.png"]];
    // 美颜对应的 beautyKey。请联系企业直播技术支持获取
    model.beautyKey = @"Internal_Deform_Overall";
    // 美颜配置是否正确
    BOOL isValid = [model isValid];
    if (!isValid) {
        NSLog(@"Model is not Valid, model: %@", model);
    }
    // 将美颜添加至美颜列表中的第一个
    [beauties insertObject:model atIndex:0];
};

自定义滤镜

新增、修改或删除滤镜。

注意

  • 如需增加滤镜,请联系 CV 技术支持获取滤镜文件。
  • 修改滤镜指修改滤镜在开播页的图标名称和样式。

以下示例代码新增、修改、删除了指定滤镜:

model.effectConfig.customizeFilters = ^(NSMutableArray<BDLEffectFilterModel *> * _Nonnull filters) {
    // 滤镜文件的 bundle 目录
    NSString *filterPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"FilterResource.bundle"];
    
 
    [filters enumerateObjectsUsingBlock:^(BDLEffectFilterModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       // 修改滤镜在开播页的图标名称和样式。此处以修改滤镜 ID 为 01 的滤镜为例
        if ([obj.filterId isEqualToString:@"01"]) {
            obj.name = [NSString stringWithFormat:@"TT %@", filters.firstObject.name]; // 滤镜图标在开播页展示的名称
            // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置滤镜图标。此处以通过 iconName 设置滤镜图标为例
            obj.iconURL = nil;
            obj.iconName = @"AppIcon";
            *stop = YES;
        }
    }];
    
    // 删除滤镜。此处以删除滤镜 ID 为 33 的滤镜为例
    [filters.copy enumerateObjectsUsingBlock:^(BDLEffectFilterModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.filterId isEqualToString:@"33"]) {
            [filters removeObject:obj];
            *stop = YES;
        }
    }];
    
    // 新增滤镜
    BDLEffectFilterModel *model = [[BDLEffectFilterModel alloc] init];
    // 滤镜图标在开播页展示的名称
    model.name = @"Name";
    // 滤镜文件的资源目录。请联系 CV 技术支持获取滤镜文件
    model.resFilePath = [filterPath stringByAppendingPathComponent:@"Filter/Filter_33_L1"];
    // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置滤镜图标。此处以通过 iconName 设置滤镜图标为例
    model.iconName = @"AppIcon";
// 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置滤镜图标。此处以通过 iconURL 设置滤镜图标为例
//  model.iconURL = [NSURL fileURLWithPath:[filterPath stringByAppendingPathComponent:@"icon/xx.png"]];
    // 滤镜配置是否正确
    BOOL isValid = [model isValid];
    if (!isValid) {
        NSLog(@"Model is not Valid, model: %@", model);
    }
    // 将滤镜添加至滤镜列表中的第一个
    [filters insertObject:model atIndex:0];
};

自定义道具贴纸

新增、修改或删除道具贴纸。

注意

  • 如需增加道具贴纸,请联系 CV 技术支持获取贴纸文件。
  • 修改道具贴纸指修改贴纸在开播页的图标样式。

以下示例代码新增、修改、删除了指定道具贴纸:

model.effectConfig.customizeStickers = ^(NSMutableArray<BDLEffectStickerModel *> * _Nonnull stickers) {
    // 贴纸文件的 bundle 目录
    NSString *stickerPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"StickerResource.bundle"];
    // 修改道具贴纸在开播页的图标样式。此处以修改文件目录包含 stickers_sd_gan 的贴纸为例
    [stickers enumerateObjectsUsingBlock:^(BDLEffectStickerModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.resFilePath containsString:@"stickers_sd_gan"]) {
            // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置贴纸图标。此处以通过 iconName 设置贴纸图标为例
            obj.iconURL = nil;
            obj.iconName = @"check";
            *stop = YES;
        }
    }];
    
    // 删除道具贴纸。此处以删除文件目录包含 tryon_nail_zhuanhongse 的贴纸为例
    [stickers.copy enumerateObjectsUsingBlock:^(BDLEffectStickerModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.resFilePath containsString:@"tryon_nail_zhuanhongse"]) {
            [stickers removeObject:obj];
            *stop = YES;
        }
    }];
    
    // 新增道具贴纸
    BDLEffectStickerModel *model = [[BDLEffectStickerModel alloc] init];
    // 贴纸文件的资源目录。请联系 CV 技术支持获取贴纸文件
    model.resFilePath = [stickerPath stringByAppendingPathComponent:@"stickers/tryon_nail_zhuanhongse"];
// 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置贴纸图标。此处以通过 iconName 设置贴纸图标为例
// model.iconName = @"AppIcon";
    // 您可以通过 iconURL(图片的本地路径或网络地址)或 iconName(assets 中的图片名称)设置贴纸图标。此处以通过 iconURL 设置贴纸图标为例
    model.iconURL = [NSURL fileURLWithPath:[stickerPath stringByAppendingPathComponent:@"icon/xx.png"]];
    // 贴纸配置是否正确
    BOOL isValid = [model isValid];
    if (!isValid) {
        NSLog(@"Model is not Valid, model: %@", model);
    }
    // 将贴纸添加至贴纸列表中的第一个
    [stickers insertObject:model atIndex:0];
};