集成 RTC SDK 后,你可以使用其中接口快速构建基础应用,实现基本实时音视频通话;你也能通过阅读代码,了解音视频通话的最佳实践。
如果你想了解完整的项目实现,参看示例项目。
注意:
面向 iOS 平台的 SDK 仅以 .xcframework 形式提供。如果需要使用 .framework 的 SDK,你可以使用以下脚本,将 .xcframework 转为 .framework:
如果使用了 .framework,且需要将 App 上架 App Store,你必须在提交的工程中去除模拟器。可以使用以下脚本:
本步骤为如何创建一个新项目,如集成到已有项目,请直接查看步骤 2。
打开 Xcode,点击 File > New > Project
选择 iOS > App,点击 Next
输入项目名称、团队名称、选择开发语言(这里为 Objective-C ) ,点击 Next
选择项目存储位置,点击 Create
签名设置:进入 TARGETS > Project Name > Signing & Capabilities ,查看 Automatically manage signing 是否勾选,若没有勾选请勾选 , 并在弹出菜单中选择 Enable Automatic
将下载的 VolcEngineRTC.xcframework
和 RealXBase.xcframework
拖入到工程中。
配置音视频权限
找到项目中的 info.plist 文件
点击 “+” 添加音频和视频设备权限:
关闭 Bitcode
SDK 配置
应苹果公司的要求,你的 iOS App 如需要上线 App Store,必须准确描述 App 本身和集成的第三方 SDK 使用指定范围内系统接口的原因。自 2024 年 5 月 1 日起,如果你未提供相关描述,你的 App 将无法通过 App Store Connect 的审核。详见 Describing use of required reason API。
如果你在 App 中集成了 3.58 及之前版本的 RTC SDK,你必须添加相关说明:
获取 RTC SDK 的隐私清单文件:
根据场景需要,为你的项目创建音视频通话的用户界面。若已有用户界面,请直接进入下一步。
如果你想实现基本的音视频通话,我们建议你在项目中添加如下元素:
可参考以下代码构建用户界面:
- (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); }]; }
在用到 SDK API 的文件中引入头文件。
#import <VolcEngineRTC/objc/ByteRTCVideo.h> #import <VolcEngineRTC/objc/ByteRTCRoom.h>
+ (ByteRTCVideo * _Nullable) createRTCVideo:(NSString * _Nonnull)appId delegate:(id<ByteRTCVideoDelegate> _Nullable)delegate parameters:(NSDictionary * _Nonnull)parameters;
首先你需要调用类 ByteRTCVideo
中的 createRTCVideo:delegate:parameters:
接口,创建一个引擎实例,以使用 RTC 提供的各种音视频能力。
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
appId | NSString* | 每个应用的唯一标识符,由 RTC 控制台随机生成的。 不同的 AppId 生成的实例在 RTC 中进行音视频通话完全独立,无法互通。 |
delegate | id | SDK 回调给应用层的 delegate,详见 ByteRTCVideoDelegate |
parameters | NSDictionary* | 用以覆盖默认参数的本引擎实例参数。JSON 字符串格式。 |
- (int)setMaxVideoEncoderConfig:(ByteRTCVideoEncoderConfig * _Nullable) max_solution;
创建引擎实例后,你可以设置视频编码参数修改推送的视频流属性。
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
max_solution | ByteRTCVideoEncoderConfig* | 期望发布的最大分辨率视频流参数。参看 ByteRTCVideoEncoderConfig。 |
- (int)setLocalVideoCanvas:(ByteRTCStreamIndex)streamIndex withCanvas:(ByteRTCVideoCanvas * _Nullable)canvas;
加入房间前,你需要设置本地视图以在通话中看到本地图像。
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
streamIndex | ByteRTCStreamIndex | 流属性,参看 ByteRTCStreamIndex |
canvas | ByteRTCVideoCanvas* | 视图信息和渲染模式,参看 ByteRTCVideoCanvas |
- (void)startVideoCapture;
创建引擎实例后,你需要开启视频采集,以在通话中使用视频功能。
- (void)startAudioCapture;
创建引擎实例后,你需要开启音频采集,以在通话中使用音频功能。
- ( ByteRTCRoom * _Nullable)createRTCRoom:(NSString * _Nonnull)roomId;
创建一个房间实例,以使用房间相关的功能。
roomId
应符合正则表达式:[a-zA-Z0-9_@\-]{1,128}
。
- (void)setRtcRoomDelegate:(id<ByteRTCRoomDelegate> _Nullable)roomDelegate;
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
roomDelegate | id | 参看 ByteRTCRoomDelegate。 |
- (int)joinRoom:(NSString *_Nullable)token userInfo:(ByteRTCUserInfo *_Nonnull)userInfo roomConfig:(ByteRTCRoomConfig *_Nonnull)roomConfig;
创建房间实例后,你就可以调用 ByteRTCRoom
类中的 joinRoom:userInfo:roomConfig:
方法创建/加入房间。
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
token | NSString* | 动态密钥,用于对进房用户进行鉴权验证。 |
userInfo | ByteRTCUserInfo* | 用户 ID。参看 ByteRTCUserInfo。 |
roomConfig | ByteRTCRoomConfig* | 房间参数配置,设置房间模式以及是否自动发布或订阅流。具体配置模式参看 ByteRTCRoomConfig。 |
- (void)rtcRoom:(ByteRTCRoom *_Nonnull)rtcRoom onRoomStateChanged:(NSString *_Nonnull)roomId withUid:(nonnull NSString *)uid state:(NSInteger)state extraInfo:(NSString *_Nonnull)extraInfo;
加入房间后,你需要在此回调中处理首次加入房间/重连加入房间的事件。
回调参数说明:
参数名 | 类型 | 说明 |
---|---|---|
roomId | NSString* | 房间 ID。 |
uid | NSString* | 用户 ID。 |
state | NSInteger | 房间状态码。 |
extraInfo | NSString* | 额外信息,如 |
- (void)rtcRoom:( ByteRTCRoom *_Nonnull)rtcRoom onUserJoined:(ByteRTCUserInfo *_Nonnull)userInfo elapsed:(NSInteger)elapsed;
加入房间后,你需要在此回调中处理远端用户加入房间的事件。
回调参数说明:
参数名 | 类型 | 说明 |
---|---|---|
rtcRoom | ByteRTCRoom* | ByteRTCRoom 对象。 |
userInfo | ByteRTCUserInfo* | 用户信息,参看 ByteRTCUserInfo。 |
elapsed | NSInteger | 保留字段,无意义 |
- (void)rtcEngine:(ByteRTCVideo * _Nonnull)engine onFirstRemoteVideoFrameDecoded:(ByteRTCRemoteStreamKey * _Nonnull)streamKey withFrameInfo:(ByteRTCVideoFrameInfo * _Nonnull)frameInfo;
加入房间后,你需要在此回调中处理第一帧远端视频流解码成功后的事件。
回调参数说明:
参数名 | 类型 | 说明 |
---|---|---|
engine | ByteRTCVideo* | ByteRTCVideo 对象 |
streamKey | ByteRTCRemoteStreamKey* | 远端流信息,参看 ByteRTCRemoteStreamKey |
frameInfo | ByteRTCVideoFrameInfo* | 视频帧信息,参看 ByteRTCVideoFrameInfo |
- (void)setRemoteVideoCanvas:(ByteRTCRemoteStreamKey * _Nonnull)key withCanvas:(ByteRTCVideoCanvas * _Nullable)canvas;
在确认收到远端用户的第一帧视频解码回调后,你需要设置远端视图以在通话中查看远端图像。
你需要在该方法中传入以下参数:
参数名 | 类型 | 说明 |
---|---|---|
key | ByteRTCRemoteStreamKey* | 远端流信息, 详见 ByteRTCRemoteStreamKey |
canvas | ByteRTCVideoCanvas* | 视图信息和渲染模式,参看 ByteRTCVideoCanvas |
- (void)rtcRoom:( ByteRTCRoom *_Nonnull)rtcRoom onUserLeave:(NSString *_Nonnull)uid reason:(ByteRTCUserOfflineReason)reason;
加入房间后,你需要在此回调中处理远端用户离开房间的事件。
回调参数说明:
参数名 | 类型 | 说明 |
---|---|---|
uid | NSString* | 离开房间的远端用户 ID 。 |
reason | ByteRTCUserOfflineReason | 用户离开房间的原因,参看 ByteRTCUserOfflineReason。 |
- (int)leaveRoom;
在结束通话等场景下,你需要调用 leaveRoom
离开房间,结束通话过程,释放所有通话相关的资源。
+ (void)destroyEngine;
在 RTC 引擎实例相关的业务场景全部结束后,你可调用 destroyRTCVideo
销毁由 createRTCVideo:delegate:parameters:
所创建引擎实例,并释放所有相关资源。
至此,我们实现了基本的音视频通话。