iOS

业务流程

本节汇总了秀场直播中一些常见的业务流程,帮助您更好地理解整个场景的实现流程。
主播开播及关播
主播跨房 PK 连麦
RTC 观众进房连麦
下图展示了主播(房主)本地预览、创建房间、进房开播、退房关播的流程。



下图展示了主播 A 邀请主播 B 进行跨房 PK 连麦的流程。跨房 PK 过程中,两个房间内的观众都可以看到两个房主 PK 连麦直播的画面。



下图展示了 RTC 实时互动直播间观众进入房间、申请连麦、结束连麦、退出房间的流程。




接入准备

步骤一:开通服务

秀场直播场景通常需要依赖 RTC Engine美颜 AR 两项付费 PaaS 服务构建。其中 RTC Engine 负责提供实时音视频互动能力,美颜 AR 负责提供美颜特效能力。如果您使用第三方美颜产品,可忽略美颜 AR 集成部分。
开通 RTC Engine 服务
开通美颜 AR 服务
1. 首先,您需要登录 RTC Engine 控制台 创建应用,您可根据需要选择升级 RTC Engine 应用版本,例如专业版可解锁更多增值功能服务。



说明:
建议创建两个应用分别用于测试环境和生产环境,一年内每个腾讯云账号(UIN)每月赠送10,000分钟免费时长。
RTC Engine 包月套餐分为体验版(默认)、轻量版、标准版、专业版,可解锁不同的增值功能服务,详情可见 版本功能与包月套餐说明
2. 应用创建完毕之后,您可以在应用管理-应用概览栏目看到该应用的基本信息,其中需要您保管好 SDKAppIDSDKSecretKey 便于后续使用,同时应避免密钥泄露造成流量盗刷。



1. 登录 美颜 AR 控制台 > 移动端 License,单击新建测试 License(测试版 License 免费测试有效期为14天,可续期1次,共28天)。选择移动,并根据实际需填写App Name, Package NameBundle ID。勾您想所有美颜功虚拟背景人脸识别手势识别礼物动画特效然后单击确认

2. 激活后,您可以在当前页面查看您信息,并参考上方集成指南进行集成可在 集成指南 中查看 License Key License URL 如何使用。


步骤二:导入 SDK

RTC Engine SDK 和美颜 AR SDK 已经发布到 CocoaPods 库,您可以通过 CocoaPods 集成。
1. 安装 CocoaPods。在终端窗口中输入如下命令(需要提前在 Mac 中安装 Ruby 环境)。
sudo gem install cocoapods
2. 创建 Podfile 文件。进入项目所在路径,输入以下命令行之后项目路径下会出现一个 Podfile 文件。
pod init
3. 编辑 Podfile 文件。根据您的项目需要选择合适的版本,并编辑 Podfile 文件。
platform :ios, '8.0'
target 'App' do
# RTC Engine 精简版
# 安装包体积增量最小,但仅支持 RTC Engine 和 直播播放器(TXLivePlayer)两项功能。
pod 'TXLiteAVSDK_TRTC', :podspec => 'https://liteav.sdk.qcloud.com/pod/liteavsdkspec/TXLiteAVSDK_TRTC.podspec'
# Professional 专业版
# 包含 RTC Engine、直播播放器(TXLivePlayer)、RTMP 推流(TXLivePusher)、点播播放器(TXVodPlayer)和短视频录制和编辑(UGSV)等众多功能。
# pod 'TXLiteAVSDK_Professional', :podspec => 'https://liteav.sdk.qcloud.com/pod/liteavsdkspec/TXLiteAVSDK_Professional.podspec'
# 美颜 AR SDK 例如:S1-07套餐如下
pod 'TencentEffect_S1-07'

end
4. 更新并安装 SDK。
在终端窗口中输入如下命令以更新本地库文件,并安装 SDK。
pod install
或使用以下命令更新本地库版本:
pod update
pod 命令执行完后,会生成集成了 SDK 的 .xcworkspace 后缀的工程文件,双击打开即可。
说明:
若 pod 搜索失败,建议尝试更新 pod 的本地 repo 缓存。更新命令如下:
pod setup
pod repo update
rm ~/Library/Caches/CocoaPods/search_index.json
除了推荐的自动加载方式,您还可以选择下载 SDK 并手动导入,详见 手动集成 RTC Engine SDK手动集成美颜 AR SDK
5. 添加美颜资源到实际项目工程中。
5.1 下载并解压对应套餐的 SDK 和美颜资源 ,将 resources/motionRes 文件夹下 bundle 资源添加到实际工程中。
5.2 在 Build Settings 中的 Other Linker Flags 添加 -ObjC
6. 将 Bundle Identifier 修改成与申请的测试授权一致。

步骤三:工程配置

1. 权限配置。
秀场直播场景下 RTC Engine SDK 及美颜 AR SDK 需要以下权限。在 App 的 Info.plist 中添加以下两项,分别对应麦克风和摄像头在系统弹出授权对话框时的提示信息。
Privacy - Microphone Usage Description,并填入麦克风使用目的提示语。
Privacy - Camera Usage Description,并填入摄像头使用目的提示语。



2. 如需 App 进入后台仍然运行相关功能,可在 Xcode 中选中当前工程项目,并在 Capabilities 下将设置项 Background Modes 设定为 ON,并勾选 Audio,AirPlay and Picture in Picture,如下图所示:




步骤四:鉴权与许可

RTC Engine 鉴权凭证
美颜 AR 鉴权许可
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权,RTC Engine 在进房时校验该鉴权凭证。
调试跑通阶段:可以通过 客户端示例代码控制台获取 两种方法计算生成 UserSig,仅用于调试测试。
正式运行阶段:推荐安全等级更高的服务端计算 UserSig 方案,防止客户端被逆向破解泄露密钥。
具体实现流程如下:
1. 您的 App 在调用 SDK 的初始化函数之前,首先要向您的服务器请求 UserSig。
2. 您的服务器根据 SDKAppID 和 UserID 计算 UserSig。
3. 服务器将计算好的 UserSig 返回给您的 App。
4. 您的 App 将获得的 UserSig 通过特定 API 传递给 SDK。
5. SDK 将 SDKAppID + UserID + UserSig 提交给腾讯云服务器进行校验。
6. 腾讯云校验 UserSig,确认合法性。
7. 校验通过后,会向 RTC Engine SDK 提供实时音视频服务。



注意:
调试跑通阶段的本地 UserSig 计算方式不推荐应用到线上环境,容易被逆向破解导致密钥泄露。
我们提供了多个语言版本(Java/Go/PHP/Node.js/Python/C#/C++)的 UserSig 服务端计算源代码,详见 服务端计算 UserSig
使用美颜 AR 之前,需要向腾讯云校验许可凭证。设置 License 需要用到 License Key 和 License URL例代码如下。
[TELicenseCheck setTELicense:LicenseURL key:LicenseKey completion:^(NSInteger authresult, NSString * _Nonnull errorMsg) {
if (authresult == TELicenseCheckOk) {
NSLog(@"鉴权成功");
} else {
NSLog(@"鉴权失败");
}
}];
注意:
建议在相关业务模块的初始化代码中触发鉴权许可,避免在使用前才临时去下载 License,同时鉴权时应具有网络权限。
实际应用的 Bundle ID 必须和创建 License 时绑定的 Bundle ID 完全匹配,否则会导致 License 校验失败,详情可参见 鉴权错误码

步骤五:初始化 SDK

初始化 RTC Engine SDK
初始化美颜 AR SDK

// 创建 RTC Engine SDK 实例(单例模式)
self.trtcCloud = [TRTCCloud sharedInstance];
// 设置事件监听器
self.trtcCloud.delegate = self;

// 来自 SDK 的各类事件通知(比如:错误码,警告码,音视频状态参数等)
- (void)onError:(TXLiteAVError)errCode errMsg:(nullable NSString *)errMsg extInfo:(nullable NSDictionary *)extInfo {
NSLog(@"%d: %@", errCode, errMsg);
}

- (void)onWarning:(TXLiteAVWarning)warningCode warningMsg:(nullable NSString *)warningMsg extInfo:(nullable NSDictionary *)extInfo {
NSLog(@"%d: %@", warningCode, warningMsg);
}

// 移除事件监听器
self.trtcCloud.delegate = nil;
// 销毁 RTC Engine SDK 实例(单例模式)
[TRTCCloud destroySharedIntance];
说明:
建议监听 SDK 事件通知,对一些常见错误进行日志打印和处理,详见 错误码表
// 加载美颜相关资源
NSDictionary *assetsDict = @{@"core_name":@"LightCore.bundle",
@"root_path":[[NSBundle mainBundle] bundlePath]
};

// 初始化腾讯特效 SDK
self.beautyKit = [[XMagic alloc] initWithRenderSize:previewSize assetsDict:assetsDict];

// 释放腾讯特效 SDK
[self.beautyKit deinit]
说明:
初始化美颜 AR SDK 之前,还需进行资源拷贝等准备工作,详细步骤可参考 美颜 AR SDK 集成步骤

接入过程

API 时序图





步骤一:主播进房推流

1. 主播进房前开启本地视频预览及音频采集。
// 获取用于展示主播本地画面预览的视频渲染控件
@property (nonatomic, strong) UIView *anchorPreviewView;
@property (nonatomic, strong) TRTCCloud *trtcCloud;

- (void)setupTRTC {
self.trtcCloud = [TRTCCloud sharedInstance];
self.trtcCloud.delegate = self;
// 设置视频编码参数,决定远端用户看到的画面质量
TRTCVideoEncParam *encParam = [[TRTCVideoEncParam alloc] init];
encParam.videoResolution = TRTCVideoResolution_960_540;
encParam.videoFps = 15;
encParam.videoBitrate = 1300;
encParam.resMode = TRTCVideoResolutionModePortrait;
[self.trtcCloud setVideoEncoderParam:encParam];
// isFrontCamera 可指定使用前置/后置摄像头进行视频采集
[self.trtcCloud startLocalPreview:self.isFrontCamera view:self.anchorPreviewView];

// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSIC
[self.trtcCloud startLocalAudio:TRTCAudioQualityDefault];
}
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率和码率搭配详见 分辨率码率参照表
enterRoom 之前调用以上接口,SDK 只会开启摄像头预览和音频采集,并一直等到您调用 enterRoom 之后才开始推流。
enterRoom 之后调用以上接口,SDK 会开启摄像头预览和音频采集,并自动开始推流。
2. 主播设置本地画面的渲染参数,以及编码器输出画面模式(可选项)。
- (void)setupRenderParams {
TRTCRenderParams *params = [[TRTCRenderParams alloc] init];
// 画面镜像模式
params.mirrorType = TRTCVideoMirrorTypeAuto;
// 画面填充模式
params.fillMode = TRTCVideoFillMode_Fill;
// 画面旋转角度
params.rotation = TRTCVideoRotation_0;
// 设置本地画面的渲染参数
[self.trtcCloud setLocalRenderParams:params];

// 设置编码器输出的画面镜像模式
[self.trtcCloud setVideoEncoderMirror:YES];
// 设置视频编码器输出的画面方向
[self.trtcCloud setVideoEncoderRotation:TRTCVideoRotation_0];
}
注意:
设置本地画面渲染参数仅影响本地画面的渲染效果。
设置编码器输出模式会影响房间中其他用户所观看到(以及云端录制文件)的画面效果。
3. 主播正式开始直播,进房推流。
- (void)enterRoomByAnchorWithUserId:(NSString *)userId roomId:(NSString *)roomId {
TRTCParams *params = [[TRTCParams alloc] init];
// 以字符串房间号为例
params.strRoomId = roomId;
params.userId = userId;
// 从业务后台获取到的 UserSig
params.userSig = @"userSig";
// 替换成您的 SDKAppID
params.sdkAppId = 0;
// 指定主播角色
params.role = TRTCRoleAnchor;
// 以互动直播场景进房
[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];
}

// 进房结果事件回调
- (void)onEnterRoom:(NSInteger)result {
if (result > 0) {
// result 代表加入房间所消耗的时间(毫秒)
NSLog(@"Enter room succeed!");
} else {
// result 代表进房失败的错误码
NSLog(@"Enter room failed!");
}
}
注意:
RTC Engine 房间号分为整型 roomId 和字符串类型 strRoomId,两种类型的房间不互通,建议统一房间号类型。
RTC Engine 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
秀场直播场景下,进房模式建议选用 TRTCAppSceneLIVE

步骤二:观众进房拉流

1. 观众进入 RTC Engine 房间。
- (void)enterRoomByAudienceWithUserId:(NSString *)userId roomId:(NSString *)roomId {
TRTCParams *params = [[TRTCParams alloc] init];
// 以字符串房间号为例
params.strRoomId = roomId;
params.userId = userId;
// 从业务后台获取到的 UserSig
params.userSig = @"userSig";
// 替换成您的 SDKAppID
params.sdkAppId = 0;
// 指定观众角色
params.role = TRTCRoleAudience;
// 以互动直播场景进房
[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];
}

// 进房结果事件回调
- (void)onEnterRoom:(NSInteger)result {
if (result > 0) {
// result 代表加入房间所消耗的时间(毫秒)
NSLog(@"Enter room succeed!");
} else {
// result 代表进房失败的错误码
NSLog(@"Enter room failed!");
}
}
2. 观众订阅主播音视频流。
- (void)onUserAudioAvailable:(NSString *)userId available:(BOOL)available {
// 某远端用户发布/取消了自己的音频
// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频
}

- (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {
// 某远端用户发布/取消了主路视频画面
if (available) {
// 订阅远端用户的视频流,并绑定视频渲染控件
[self.trtcCloud startRemoteView:userId streamType:TRTCVideoStreamTypeBig view:self.remoteView];
} else {
// 停止订阅远端用户的视频流,并释放渲染控件
[self.trtcCloud stopRemoteView:userId streamType:TRTCVideoStreamTypeBig];
}
}
3. 观众设置远端画面的渲染模式(可选项)。
- (void)setupRemoteRenderParams {
TRTCRenderParams *params = [[TRTCRenderParams alloc] init];
// 画面镜像模式
params.mirrorType = TRTCVideoMirrorTypeAuto;
// 画面填充模式
params.fillMode = TRTCVideoFillMode_Fill;
// 画面旋转角度
params.rotation = TRTCVideoRotation_0;
// 设置远端画面的渲染模式
[self.trtcCloud setRemoteRenderParams:@"userId" streamType:TRTCVideoStreamTypeBig params:params];
}

步骤三:观众连麦互动

1. 观众切换为主播角色。
- (void)switchToAnchor {
// 切换为主播角色
[self.trtcCloud switchRole:TRTCRoleAnchor];
}

// 切换角色事件回调
- (void)onSwitchRole:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {
if (errCode == ERR_NULL) {
// 切换角色成功
}
}
2. 观众开始本地音视频采集和推流。
- (void)setupTRTC {
// 设置视频编码参数,决定远端用户看到的画面质量
TRTCVideoEncParam *encParam = [[TRTCVideoEncParam alloc] init];
encParam.videoResolution = TRTCVideoResolution_480_270;
encParam.videoFps = 15;
encParam.videoBitrate = 550;
encParam.resMode = TRTCVideoResolutionModePortrait;
[self.trtcCloud setVideoEncoderParam:encParam];
// isFrontCamera 可指定使用前置/后置摄像头进行视频采集
[self.trtcCloud startLocalPreview:self.isFrontCamera view:self.audiencePreviewView];
// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSIC
[self.trtcCloud startLocalAudio:TRTCAudioQualityDefault];
}
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率和码率搭配详见 分辨率码率参照表
3. 观众下麦停止推流。
- (void)switchToAudience {
// 切换为观众角色
[self.trtcCloud switchRole:TRTCRoleAudience];
}

// 切换角色事件回调
- (void)onSwitchRole:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {
if (errCode == ERR_NULL) {
// 停止摄像头采集推流
[self.trtcCloud stopLocalPreview];
// 停止麦克风采集推流
[self.trtcCloud stopLocalAudio];
}
}

步骤四:退出与解散房间

1. 退出房间。
- (void)exitRoom {
[self.trtcCloud stopLocalAudio];
[self.trtcCloud stopLocalPreview];
[self.trtcCloud exitRoom];
}

// 离开房间事件回调
- (void)onExitRoom:(NSInteger)reason {
if (reason == 0) {
NSLog(@"主动调用 exitRoom 退出房间");
} else if (reason == 1) {
NSLog(@"被服务器踢出当前房间");
} else if (reason == 2) {
NSLog(@"当前房间整个被解散");
}
}
注意:
待 SDK 占用的所有资源释放完毕后,SDK 会抛出 onExitRoom 回调通知到您。
如果您要再次调用 enterRoom 或者切换到其他的音视频 SDK,请等待 onExitRoom 回调到来后再执行相关操作。否则可能会遇到例如摄像头、麦克风设备被强占等各种异常问题。
2. 解散房间
服务端解散:RTC Engine 提供了服务端解散房间 API DismissRoom(区分数字房间 ID 和字符串房间 ID),您可以调用此接口把房间所有用户从房间移出,并解散房间。
客户端解散:通过各个客户端的退出房间 exitRoom 接口,将房间内的所有主播和听众完成退房,退房后,根据 RTC Engine 房间生命周期规则,房间将会自动解散,详情请参见 退出房间
注意:
建议您当一次直播结束之后,可以在服务端调用解散房间 API 确保房间解散,防止观众意外进房导致产生非期望的费用。

高级功能

主播跨房 PK 连麦

1. 任意一方发起跨房 PK 连麦。
- (void)connectOtherRoom:(NSString *)roomId {
NSMutableDictionary *jsonDict = [[NSMutableDictionary alloc] init];
// 数字房间号为 roomId
[jsonDict setObject:roomId forKey:@"strRoomId"];
[jsonDict setObject:self.userId forKey:@"userId"];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[self.trtcCloud connectOtherRoom:jsonString];
}

// 请求跨房连麦的结果回调
- (void)onConnectOtherRoom:(NSString *)userId errCode:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {
// 要跨房通话的另一个房间中的主播的用户 ID
// 错误码,ERR_NULL 代表请求成功
// 错误信息
}
注意:
跨房 PK 连麦的本地用户和对端用户必须都为主播角色,且必须都有音频或视频上行。
2. 两个房间中的所有用户都会收到来自另一个房间中的 PK 主播的音视频流可用回调。
- (void)onUserAudioAvailable:(NSString *)userId available:(BOOL)available {
// 某远端用户发布/取消了自己的音频
// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频
}

- (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {
// 某远端用户发布/取消了主路视频画面
if (available) {
// 订阅远端用户的视频流,并绑定视频渲染控件
[self.trtcCloud startRemoteView:userId streamType:TRTCVideoStreamTypeBig view:self.remoteView];
} else {
// 停止订阅远端用户的视频流,并释放渲染控件
[self.trtcCloud stopRemoteView:userId streamType:TRTCVideoStreamTypeBig];
}
}
3. 任意一方退出跨房 PK 连麦。
// 退出跨房连麦
[self.trtcCloud disconnectOtherRoom];

// 退出跨房连麦的结果回调
- (void)onDisconnectOtherRoom:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {
}
注意:
调用 DisconnectOtherRoom() 后,即退出与所有其他房间主播的跨房 PK 连麦。
跨房 PK 连麦的发起端和接收端任意一端均可调用 DisconnectOtherRoom() 退出跨房 PK 连麦。

第三方美颜接入

RTC Engine 支持接入第三方美颜特效产品,下面以美颜 AR 为例,展示第三方美颜接入流程。
1. 集成美颜 AR SDK、申请授权 License,详情请参见 接入准备 步骤实现。
2. 设置 SDK 素材资源路径(如有)。
NSString *beautyConfigPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
beautyConfigPath = [beautyConfigPath stringByAppendingPathComponent:@"beauty_config.json"];
NSFileManager *localFileManager=[[NSFileManager alloc] init];
BOOL isDir = YES;
NSDictionary * beautyConfigJson = @{};
if ([localFileManager fileExistsAtPath:beautyConfigPath isDirectory:&isDir] && !isDir) {
NSString *beautyConfigJsonStr = [NSString stringWithContentsOfFile:beautyConfigPath encoding:NSUTF8StringEncoding error:nil];
NSError *jsonError;
NSData *objectData = [beautyConfigJsonStr dataUsingEncoding:NSUTF8StringEncoding];
beautyConfigJson = [NSJSONSerialization JSONObjectWithData:objectData
options:NSJSONReadingMutableContainers
error:&jsonError];
}
NSDictionary *assetsDict = @{@"core_name":@"LightCore.bundle",
@"root_path":[[NSBundle mainBundle] bundlePath],
@"tnn_"
@"beauty_config":beautyConfigJson
};
// 初始化SDK:width和height分别是texture的宽高
self.xMagicKit = [[XMagic alloc] initWithRenderSize:CGSizeMake(width,height) assetsDict:assetsDict];
3. 设置第三方美颜的视频数据回调,将美颜 SDK 处理每帧数据结果传入 RTC Engine SDK 内部做渲染处理。
// RTC Engine SDK 设置第三方美颜的视频数据回调
[self.trtcCloud setLocalVideoProcessDelegete:self pixelFormat:TRTCVideoPixelFormat_Texture_2D bufferType:TRTCVideoBufferType_Texture];

#pragma mark - TRTCVideoFrameDelegate

// 构造 YTProcessInput 传入到 SDK 内做渲染处理
- (uint32_t)onProcessVideoFrame:(TRTCVideoFrame *_Nonnull)srcFrame dstFrame:(TRTCVideoFrame *_Nonnull)dstFrame {
if (!self.xMagicKit) {
[self buildBeautySDK:srcFrame.width and:srcFrame.height texture:srcFrame.textureId];//初始化XMagic SDK
self.heightF = srcFrame.height;
self.widthF = srcFrame.width;
}
if(self.xMagicKit!=nil && (self.heightF!=srcFrame.height || self.widthF!=srcFrame.width)){
self.heightF = srcFrame.height;
self.widthF = srcFrame.width;
[self.xMagicKit setRenderSize:CGSizeMake(srcFrame.width, srcFrame.height)];
}
YTProcessInput *input = [[YTProcessInput alloc] init];
input.textureData = [[YTTextureData alloc] init];
input.textureData.texture = srcFrame.textureId;
input.textureData.textureWidth = srcFrame.width;
input.textureData.textureHeight = srcFrame.height;
input.dataType = kYTTextureData;
YTProcessOutput *output = [self.xMagicKit process:input withOrigin:YtLightImageOriginTopLeft withOrientation:YtLightCameraRotation0];
dstFrame.textureId = output.textureData.texture;
return 0;
}
注意:
步骤1、步骤2根据不同的第三方美颜产品实现方式有所不同,而步骤3是 RTC Engine 集成第三方美颜的通用且重要步骤

双路编码模式

开启双路编码模式后,当前用户的编码器会同时输出【高清大画面】和【低清小画面】两路视频流(但只有一路音频流)。这样,房间中的其他用户就可以根据自身的网络情况或屏幕大小选择订阅【高清大画面】或是【低清小画面】。
1. 开启大小画面双路编码模式。
- (void)enableDualStreamMode:(BOOL)enable {
// 小流的视频编码参数(可自定义)
TRTCVideoEncParam *smallVideoEncParam = [[TRTCVideoEncParam alloc] init];
smallVideoEncParam.videoResolution = TRTCVideoResolution_480_270;
smallVideoEncParam.videoFps = 15;
smallVideoEncParam.videoBitrate = 550;
smallVideoEncParam.resMode = TRTCVideoResolutionModePortrait;
[self.trtcCloud enableEncSmallVideoStream:enable withQuality:smallVideoEncParam];
}
注意:
双路编码开启后,会消耗更多的 CPU 和 网络带宽,所以 Mac、Windows 或者高性能 Pad 可以考虑开启,不建议手机端开启。
2. 选择拉取远端用户视频流类型。
// 订阅远端用户视频流时可选视频流类型
[self.trtcCloud startRemoteView:userId streamType:TRTCVideoStreamTypeBig view:view];

// 亦可随时切换指定远端用户的大小画面
[self.trtcCloud setRemoteVideoStreamType:userId type:TRTCVideoStreamTypeSmall];
注意:
双路编码开启后,可通过指定 streamType 视频流类型为 TRTCVideoStreamTypeSmall 来拉取低清小画面观看。

视图渲染控件

如果您业务涉及到切换显示区域的交互场景,可以使用 RTC Engine SDK 更新本地预览画面、更新远端用户视频渲染控件功能实现。
// 更新本地预览画面渲染控件
[self.trtcCloud updateLocalView:view];

// 更新远端用户视频渲染控件
[self.trtcCloud updateRemoteView:view streamType:TRTCVideoStreamTypeBig forUser:userId];
注意:
传参 view 为目标视频渲染控件,streamType 仅支持 TRTCVideoStreamTypeBigTRTCVideoStreamTypeSub

直播互动消息

直播互动在直播场景中尤为重要,用户通过 点赞消息礼物消息弹幕消息 等方式与主播进行互动。实现直播互动功能的前提是开通 Chat 服务,并导入 Chat SDK,详细指引请参考 语聊房接入指引-接入准备

点赞消息

1. 点赞者在客户端发送点赞相关的群组自定义消息,发送成功后业务方在本地渲染点赞效果。
// 构造点赞消息体
NSDictionary *msgDict = @{
@"type": @1, // 点赞类型
@"likeCount": @10 // 点赞数量
};
NSDictionary *dataDict = @{
@"cmd": @"like_msg",
@"msg": msgDict
};
NSError *error;
NSData *data = [NSJSONSerialization dataWithJSONObject:dataDict options:0 error:&error];

// 发送群自定义消息(点赞消息建议设置为低优先级)
[[V2TIMManager sharedInstance] sendGroupCustomMessage:data to:groupID priority:V2TIM_PRIORITY_LOW succ:^{
// 发送点赞消息成功
// 本地渲染点赞效果
} fail:^(int code, NSString *desc) {
// 发送点赞消息失败
}];
2. 房间内其他用户客户端收到群自定义消息回调,然后进行消息解析和点赞效果渲染。
// 收到群自定义消息
[[V2TIMManager sharedInstance] addSimpleMsgListener:self];
- (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data {
if (data.length > 0) {
NSError *error;
NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!error) {
NSString *command = dataDict[@"cmd"];
NSDictionary *msgDict = dataDict[@"msg"];
if ([command isEqualToString:@"like_msg"]) {
NSNumber *type = msgDict[@"type"]; // 点赞类型
NSNumber *likeCount = msgDict[@"likeCount"]; // 点赞数量
// 根据点赞类型和数量渲染点赞效果
}
} else {
NSLog(@"解析错误: %@", error.localizedDescription);
}
}
}

礼物消息

1. 送礼者向业务服务端发起请求,业务服务端完成计费结算后调用 REST API 向群组中发送自定义消息。
1.1 请求 URL 示例。
https://xxxxxx/v4/group_open_http_svc/send_group_msg?sdkappid=88888888&identifier=admin&usersig=xxx&random=99999999&contenttype=json
1.2 请求包体示例。
{
"GroupId": "@TGS#12DEVUDHQ",
"Random": 2784275388,
"MsgPriority": "High", // 消息的优先级,礼物消息应设置为高优先级
"MsgBody": [
{
"MsgType": "TIMCustomElem",
"MsgContent": {
// type: 礼物类型; giftUrl: 礼物资源地址; giftName: 礼物名称; giftCount: 礼物数量
"Data": "{\"cmd\": \"gift_msg\", \"msg\": {\"type\": 1, \"giftUrl\": \"xxx\", \"giftName\": \"xxx\", \"giftCount\": 1}}"
}
}
]
}
2. 房间内其他用户客户端收到群自定义消息回调,然后进行消息解析和礼物特效渲染。
// 收到群自定义消息
[[V2TIMManager sharedInstance] addSimpleMsgListener:self];
- (void)onRecvGroupCustomMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info customData:(NSData *)data {
if (data.length > 0) {
NSError *error;
NSDictionary *dataDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!error) {
NSString *command = dataDict[@"cmd"];
NSDictionary *msgDict = dataDict[@"msg"];
if ([command isEqualToString:@"gift_msg"]) {
NSNumber *type = msgDict[@"type"]; // 礼物类型
NSNumber *giftCount = msgDict[@"giftCount"]; // 礼物数量
NSString *giftUrl = msgDict[@"giftUrl"]; // 礼物资源地址
NSString *giftName = msgDict[@"giftName"]; // 礼物名称
// 根据礼物类型、礼物数量、礼物资源地址、礼物名称渲染礼物特效
}
} else {
NSLog(@"解析错误: %@", error.localizedDescription);
}
}
}

弹幕消息

秀场直播间通常会有文本形式的弹幕消息互动,这里可以通过 Chat 的发送及接收群聊普通文本消息来实现。
// 发送公屏弹幕消息
[[V2TIMManager sharedInstance] sendGroupTextMessage:text to:groupID priority:V2TIM_PRIORITY_NORMAL succ:^{
// 发送弹幕消息成功
// 本地展示消息文本
} fail:^(int code, NSString *desc) {
// 发送弹幕消息失败
}];

// 接收公屏弹幕消息
[[V2TIMManager sharedInstance] addSimpleMsgListener:self];
- (void)onRecvGroupTextMessage:(NSString *)msgID groupID:(NSString *)groupID sender:(V2TIMGroupMemberInfo *)info text:(NSString *)text {
// 根据发送者信息 info 和消息文本 text 渲染弹幕消息
}
注意:
建议将礼物消息设置为高优先级;弹幕消息设置为正常优先级;点赞消息设置为低优先级。
本人在客户端发送群聊消息,不会触发消息接收回调,只有群组内的其他用户可以接收到。

异常处理

异常错误处理

RTC Engine SDK 遇到不可恢复的错误会在 onError 回调中抛出,详见 错误码表
1. UserSig 相关。UserSig 校验失败会导致进房失败,您可使用 UserSig 工具 进行校验。
枚举
取值
描述
ERR_TRTC_INVALID_USER_SIG
-3320
进房参数 userSig 不正确,请检查 TRTCParams.userSig 是否为空。
ERR_TRTC_USER_SIG_CHECK_FAILED
-100018
UserSig 校验失败,请检查参数 TRTCParams.userSig 是否填写正确或已经过期。
2. 进退房相关。进房失败请先检查进房参数是否正确,且进退房接口必须成对调用,即便进房失败也需要调用退房接口。
枚举
取值
描述
ERR_TRTC_CONNECT_SERVER_TIMEOUT
-3308
请求进房超时,请检查是否断网或者是否开启 VPN,您也可以切换4G进行测试。
ERR_TRTC_INVALID_SDK_APPID
-3317
进房参数 sdkAppId 错误,请检查 TRTCParams.sdkAppId 是否为空
ERR_TRTC_INVALID_ROOM_ID
-3318
进房参数 roomId 错误,请检查 TRTCParams.roomIdTRTCParams.strRoomId 是否为空,注意 roomId 和 strRoomId 不可混用。
ERR_TRTC_INVALID_USER_ID
-3319
进房参数 userId 不正确,请检查 TRTCParams.userId 是否为空。
ERR_TRTC_ENTER_ROOM_REFUSED
-3340
进房请求被拒绝,请检查是否连续调用 enterRoom 进入相同 Id 的房间。
3. 设备相关。可监听设备相关错误,在出现相关错误时 UI 提示用户。
枚举
取值
描述
ERR_CAMERA_START_FAIL
-1301
打开摄像头失败,例如在 Windows 或 Mac 设备,摄像头的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。
ERR_MIC_START_FAIL
-1302
打开麦克风失败,例如在 Windows 或 Mac 设备,麦克风的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。
ERR_CAMERA_NOT_AUTHORIZED
-1314
摄像头设备未授权,通常在移动设备出现,可能是权限被用户拒绝了。
ERR_MIC_NOT_AUTHORIZED
-1317
麦克风设备未授权,通常在移动设备出现,可能是权限被用户拒绝了。
ERR_CAMERA_OCCUPY
-1316
摄像头正在被占用中,可尝试打开其他摄像头。
ERR_MIC_OCCUPY
-1319
麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。

远端镜像模式无效问题

RTC Engine 设置画面镜像分为本地预览镜像 setLocalRenderParams 和视频编码镜像 setVideoEncoderMirror,两者分别影响本地预览画面镜像效果,以及视频编码输出画面的镜像效果(远端观众及云端录制的镜像模式)。如果希望本地预览的镜像效果同时在远端观众侧生效,请按照如下方法进行编码。
// 设置本地画面的渲染参数
TRTCRenderParams *params = [[TRTCRenderParams alloc] init];
params.mirrorType = TRTCVideoMirrorTypeEnable; // 画面镜像模式
params.fillMode = TRTCVideoFillMode_Fill; // 画面填充模式
params.rotation = TRTCVideoRotation_0; // 画面旋转角度
[self.trtcCloud setLocalRenderParams:params];
// 设置编码器输出的画面镜像模式
[self.trtcCloud setVideoEncoderMirror:YES];

摄像头缩放/对焦/切换问题

秀场直播场景下,主播可能会对摄像头有自定义调整的需求,RTC Engine SDK 设备管理类下也有解决此类需求的相关接口。
1. 查询、设置摄像头的缩放倍数。
// 获取摄像头的最大缩放倍数(仅适用于移动端)
CGFloat zoomRatio = [[self.trtcCloud getDeviceManager] getCameraZoomMaxRatio];
// 设置摄像头的缩放倍数(仅适用于移动端)
// 取值范围1-5,取值为1表示最远视角(正常镜头),取值为5表示最近视角(放大镜头);最大值推荐为5,若超过5,视频数据会变得模糊不清
[[self.trtcCloud getDeviceManager] setCameraZoomRatio:zoomRatio];
2. 设置摄像头的对焦功能及位置。
// 开启或关闭摄像头的自动对焦功能(仅适用于移动端)
[[self.trtcCloud getDeviceManager] enableCameraAutoFocus:NO];
// 设置摄像头的对焦位置(仅适用于移动端)
// 使用该接口的前提是先通过 enableCameraAutoFocus 关闭自动对焦功能
[[self.trtcCloud getDeviceManager] setCameraFocusPosition:CGPointMake(x, y)];
3. 判断、切换前置或后置摄像头。
// 判断当前是否为前置摄像头(仅适用于移动端)
BOOL isFrontCamera = [[self.trtcCloud getDeviceManager] isFrontCamera];
// 切换前置或后置摄像头(仅适用于移动端)
// 传入true: 切换为前置;传入false: 切换为后置
[[self.trtcCloud getDeviceManager] switchCamera:!isFrontCamera];