You need to enable JavaScript to run this app.
导航

iOS (Objective-C)

最近更新时间2024.01.11 17:50:47

首次发布时间2022.04.28 16:43:35

集成 RTC SDK 后,你可以使用其中接口快速构建基础应用,实现基本实时音视频通话;你也能通过阅读代码,了解音视频通话的最佳实践。
如果你想了解完整的项目实现,参看快速开始 Demo

前提条件

  • Xcode 12.5 或以上版本(本文涉及编译器的指引及示例图均参考 Xcode 13.1 )
  • 支持 iOS 11.0 或以上版本的设备
  • 使用 Objective-C 作为开发语言
  • 请确保您的项目已设置有效的开发者签名
  • 获取 Appid
  • 已获取 RTC SDK 文件

注意:
面向 iOS 平台的 SDK 仅以 .xcframework 形式提供。如果需要使用 .framework 的 SDK,你可以使用以下脚本,将 .xcframework 转为 .framework:

xcframework_change_to_all_arch.sh
578.00Bytes

如果使用了 .framework,且需要将 App 上架 App Store,你必须在提交的工程中去除模拟器。可以使用以下脚本:
xcframework_remove_simulator_arch.sh
593.00Bytes

集成 SDK

步骤 1:(可选)创建项目

本步骤为如何创建一个新项目,如集成到已有项目,请直接查看步骤 2。

  1. 打开 Xcode,点击 File > New > Project

  2. 选择 iOS > App,点击 Next

    image.png

  3. 输入项目名称、团队名称、选择开发语言(这里为  Objective-C ) ,点击  Next

    image.png

  4. 选择项目存储位置,点击 Create

  5. 签名设置:进入 TARGETS > Project Name > Signing & Capabilities  ,查看  Automatically manage signing  是否勾选,若没有勾选请勾选 , 并在弹出菜单中选择  Enable Automatic

    image.png

步骤2: 解压 SDK,并拷贝到项目目录下

将下载的 VolcEngineRTC.xcframeworkRealXBase.xcframework 拖入到工程中。

image.png

步骤3:配置项目属性

  1. 配置音视频权限

    1. 找到项目中的 info.plist 文件

    2. 点击 “+” 添加音频和视频设备权限:

      1. Privacy - Microphone Usage Description ,并填入使用麦克风的原因(Value)
      2. Privacy - Camera Usage Description ,并填入使用摄像头的原因(Value)

    image.png

  2. 关闭 Bitcode

    1. 进入 TARGETS > Project Name > Build Setting
    2. 选择 All,搜索 bitcode
    3. Enable Bitcode 选择 NO

    image.png

  3. SDK 配置

    1. 进入 TARGETS > Project Name > General
    2. 选择 Frameworks, Libraries, and Embedded Content
    3. VolcEngineRTC.xcframeworkVolcEngineRTCScreenCapturer .xcframework 设置为 Embed & Sign

    image.png

实现音视频通话

基础音视频通话 API 时序图

alt

实现流程

1. (可选)创建用户界面

根据场景需要,为你的项目创建音视频通话的用户界面。若已有用户界面,请直接进入下一步。

如果你想实现基本的音视频通话,我们建议你在项目中添加如下元素:

  • 房间ID
  • 用户 ID
  • 本地视频窗口
  • 远端视频窗口
  • 打开麦克风按钮
  • 打开摄像头按钮
  • 结束通话按钮

image.png
可参考以下代码构建用户界面:

- (void)buildUI{
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
    if (@available(iOS 11.0, *)) {
        edgeInsets = [UIApplication sharedApplication].keyWindow.safeAreaInsets;
    }
    
    [self.view addSubview:self.headerView];
    [self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(edgeInsets.top);
        make.left.right.equalTo(self.view);
        make.height.mas_equalTo(49);
    }];
    
     self.switchCameraBtn.frame = CGRectMake(22, 14, 26, 26);
    [self.headerView addSubview:self.switchCameraBtn];
    [self.switchCameraBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(22);
        make.size.mas_equalTo(26);
        make.centerY.equalTo(self.headerView);
    }];
    
    [self.headerView addSubview:self.roomIdLabel];
    [self.roomIdLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.headerView);
        make.left.greaterThanOrEqualTo(self.switchCameraBtn.mas_right).mas_offset(22);
    }];
    self.roomIdLabel.text = [NSString stringWithFormat:@"RoomID: %@",self.roomID];
    
    [self.headerView addSubview:self.switchAudioRouteBtn];
    [self.switchAudioRouteBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(-20);
        make.centerY.equalTo(self.headerView);
    }];
    
    [self.view addSubview: self.foooterView];
    [self.foooterView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(self.view);
        make.bottom.equalTo(self.view).offset(-edgeInsets.bottom);
        make.height.mas_equalTo(49);
    }];
    
    [self.foooterView addSubview:self.localAudioBtn];
    [self.localAudioBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(42);
        make.centerY.equalTo(self.foooterView);
    }];
    
    [self.foooterView addSubview:self.localVideoBtn];
    [self.localVideoBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(-42);
        make.centerY.equalTo(self.foooterView);
    }];
    
    [self.foooterView addSubview:self.hangUpBtn];
    [self.hangUpBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.foooterView);
    }];
    
    [self.view addSubview:self.containerView];
    [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.equalTo(self.view);
        make.top.equalTo(self.headerView.mas_bottom);
        make.bottom.equalTo(self.foooterView.mas_top);
    }];
    
    [self.containerView addSubview:self.localView];
    [self.localView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.equalTo(self.containerView);
        make.width.height.equalTo(self.containerView).multipliedBy(0.5);
    }];
    
    [self.containerView addSubview:self.firstRemoteView];
    [self.firstRemoteView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.right.equalTo(self.containerView);
        make.width.height.equalTo(self.containerView).multipliedBy(0.5);
    }];
    
    [self.containerView addSubview:self.secondRemoteView];
    [self.secondRemoteView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.left.equalTo(self.containerView);
        make.width.height.equalTo(self.containerView).multipliedBy(0.5);
    }];
    
    [self.containerView addSubview:self.thirdRemoteView];
    [self.thirdRemoteView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.right.equalTo(self.containerView);
        make.width.height.equalTo(self.containerView).multipliedBy(0.5);
    }];
}

2. 引入头文件

在用到 SDK API 的文件中引入头文件。

#import <VolcEngineRTC/objc/ByteRTCVideo.h>
#import <VolcEngineRTC/objc/ByteRTCRoom.h>

3. 创建引擎 createRTCVideo

+ (ByteRTCVideo * _Nullable) createRTCVideo:(NSString * _Nonnull)appId
                                      delegate:(id<ByteRTCVideoDelegate> _Nullable)delegate
                                    parameters:(NSDictionary * _Nonnull)parameters;

首先你需要调用类 ByteRTCVideo 中的 createRTCVideo:delegate:parameters: 接口,创建一个引擎实例,以使用 RTC 提供的各种音视频能力。

你需要在该方法中传入以下参数:

参数名类型说明
appIdNSString*每个应用的唯一标识符,由 RTC 控制台随机生成的。
不同的 AppId 生成的实例在 RTC 中进行音视频通话完全独立,无法互通。
delegateidSDK 回调给应用层的 delegate,详见 ByteRTCVideoDelegate
parametersNSDictionary*用以覆盖默认参数的本引擎实例参数。JSON 字符串格式。

4. 设置视频编码参数 setMaxVideoEncoderConfig

- (int)setMaxVideoEncoderConfig:(ByteRTCVideoEncoderConfig * _Nullable) max_solution;

创建引擎实例后,你可以设置视频编码参数修改推送的视频流属性。

你需要在该方法中传入以下参数:

参数名类型说明
max_solutionByteRTCVideoEncoderConfig*期望发布的最大分辨率视频流参数。参看 ByteRTCVideoEncoderConfig

5. 设置本地视图 setLocalVideoCanvas

- (int)setLocalVideoCanvas:(ByteRTCStreamIndex)streamIndex
                withCanvas:(ByteRTCVideoCanvas * _Nullable)canvas;

加入房间前,你需要设置本地视图以在通话中看到本地图像。

你需要在该方法中传入以下参数:

参数名类型说明
streamIndexByteRTCStreamIndex流属性,参看 ByteRTCStreamIndex
canvasByteRTCVideoCanvas*视图信息和渲染模式,参看 ByteRTCVideoCanvas

6. 开始视频采集 startVideoCapture

- (void)startVideoCapture;

创建引擎实例后,你需要开启视频采集,以在通话中使用视频功能。

7. 开始音频采集 startAudioCapture

- (void)startAudioCapture;

创建引擎实例后,你需要开启音频采集,以在通话中使用音频功能。

8. 创建房间实例 createRTCRoom

- ( ByteRTCRoom * _Nullable)createRTCRoom:(NSString * _Nonnull)roomId;

创建一个房间实例,以使用房间相关的功能。

roomId 应符合正则表达式:[a-zA-Z0-9_@\-]{1,128}

9. 设置房间回调事件 handler setRtcRoomDelegate

- (void)setRtcRoomDelegate:(id<ByteRTCRoomDelegate> _Nullable)roomDelegate;

你需要在该方法中传入以下参数:

参数名类型说明
roomDelegateid参看 ByteRTCRoomDelegate

10. 加入房间 joinRoom:userInfo:roomConfig:

- (int)joinRoom:(NSString *_Nullable)token userInfo:(ByteRTCUserInfo *_Nonnull)userInfo roomConfig:(ByteRTCRoomConfig *_Nonnull)roomConfig;

创建房间实例后,你就可以调用 ByteRTCRoom 类中的 joinRoom:userInfo:roomConfig: 方法创建/加入房间。

你需要在该方法中传入以下参数:

参数名类型说明

token

NSString*

动态密钥,用于对进房用户进行鉴权验证。
进入房间需要携带 Token。测试时可使用控制台生成临时 Token,正式上线需要使用密钥 SDK 在你的服务端生成并下发 Token。
使用不同 AppID 的 App 是不能互通的。
请务必保证生成 Token 使用的 AppID 和创建引擎时使用的 AppID 相同,否则会导致加入房间失败。

userInfoByteRTCUserInfo*用户 ID。参看 ByteRTCUserInfo
roomConfigByteRTCRoomConfig*房间参数配置,设置房间模式以及是否自动发布或订阅流。具体配置模式参看 ByteRTCRoomConfig

11. 处理房间状态改变回调 onRoomStateChanged

- (void)rtcRoom:(ByteRTCRoom *_Nonnull)rtcRoom
    onRoomStateChanged:(NSString *_Nonnull)roomId
             withUid:(nonnull NSString *)uid
           state:(NSInteger)state
           extraInfo:(NSString *_Nonnull)extraInfo;

加入房间后,你需要在此回调中处理首次加入房间/重连加入房间的事件。

回调参数说明:

参数名类型说明
roomIdNSString*房间 ID。
uidNSString*用户 ID。

state

NSInteger

房间状态码。
• 0: 成功。
• !0: 失败,具体原因参看 ByteRTCErrorCodeByteRTCWarningCode

extraInfo

NSString*

额外信息,如 {"elapsed":1187,"join_type":0}
joinType 表示加入房间的类型,0为首次进房,1为重连进房。
elapsed 表示加入房间耗时,即本地用户从调用 joinRoom:userInfo:roomConfig: 到加入房间成功所经历的时间间隔,单位为 ms。

12. 处理远端用户加入房间的回调 onUserJoined

- (void)rtcRoom:( ByteRTCRoom *_Nonnull)rtcRoom onUserJoined:(ByteRTCUserInfo *_Nonnull)userInfo elapsed:(NSInteger)elapsed;

加入房间后,你需要在此回调中处理远端用户加入房间的事件。

回调参数说明:

参数名类型说明
rtcRoomByteRTCRoom*ByteRTCRoom 对象。
userInfoByteRTCUserInfo*用户信息,参看 ByteRTCUserInfo
elapsedNSInteger保留字段,无意义

13. 处理远端视频首帧解码的回调 onFirstRemoteVideoFrameDecoded

- (void)rtcEngine:(ByteRTCVideo * _Nonnull)engine onFirstRemoteVideoFrameDecoded:(ByteRTCRemoteStreamKey * _Nonnull)streamKey withFrameInfo:(ByteRTCVideoFrameInfo * _Nonnull)frameInfo;

加入房间后,你需要在此回调中处理第一帧远端视频流解码成功后的事件。

回调参数说明:

参数名类型说明
engineByteRTCVideo*ByteRTCVideo 对象
streamKeyByteRTCRemoteStreamKey*远端流信息,参看 ByteRTCRemoteStreamKey
frameInfoByteRTCVideoFrameInfo*视频帧信息,参看 ByteRTCVideoFrameInfo

14. 设置远端视图 setRemoteVideoCanvas:withCanvas:

- (void)setRemoteVideoCanvas:(ByteRTCRemoteStreamKey * _Nonnull)key withCanvas:(ByteRTCVideoCanvas * _Nullable)canvas;

在确认收到远端用户的第一帧视频解码回调后,你需要设置远端视图以在通话中查看远端图像。

你需要在该方法中传入以下参数:

参数名类型说明
keyByteRTCRemoteStreamKey*远端流信息, 详见 ByteRTCRemoteStreamKey
canvasByteRTCVideoCanvas*视图信息和渲染模式,参看 ByteRTCVideoCanvas

15. 处理远端用户离开房间的回调 onUserLeave

- (void)rtcRoom:( ByteRTCRoom *_Nonnull)rtcRoom onUserLeave:(NSString *_Nonnull)uid reason:(ByteRTCUserOfflineReason)reason;

加入房间后,你需要在此回调中处理远端用户离开房间的事件。

回调参数说明:

参数名类型说明
uidNSString*离开房间的远端用户 ID 。
reasonByteRTCUserOfflineReason用户离开房间的原因,参看 ByteRTCUserOfflineReason

16. 离开房间 leaveRoom

- (int)leaveRoom;

在结束通话等场景下,你需要调用 leaveRoom 离开房间,结束通话过程,释放所有通话相关的资源。

17. 销毁引擎实例 destroyRTCVideo

+ (void)destroyEngine;

在 RTC 引擎实例相关的业务场景全部结束后,你可调用 destroyRTCVideo 销毁由 createRTCVideo:delegate:parameters: 所创建引擎实例,并释放所有相关资源。

至此,我们实现了基本的音视频通话。