iOS

最近更新时间:2024-07-01 09:53:11

我的收藏

业务流程

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



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



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



下图展示了 RTC 旁路直播间 CDN 观众进入房间、申请连麦、结束连麦、退出房间的流程。




接入准备

步骤一:开通服务

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



说明:
建议创建两个应用分别用于测试环境和生产环境,首次开通 TRTC 服务可前往 试用中心 免费领取 10000 分钟试用时长包。
TRTC 包月套餐(入门版、基础版、尊享版、旗舰版)可以解锁不同的增值功能服务,详情可见 包月套餐说明
2. 应用创建完毕之后,您可以在应用管理-应用概览栏目看到该应用的基本信息,其中需要您保管好 SDKAppIDSDKSecretKey 便于后续使用,同时应避免密钥泄露造成流量盗刷。



1. 首先,您需要登录 腾讯云视立方控制台 > 移动端 License,单击新建测试 License。测试版 License 免费测试有效期为14天,可续期一次共28天。根据实际需求填写 App NamePackage NameBundle ID,选择腾讯特效,选择所需测试的能力:高级套餐 S1-07原子能力 X1-01原子能力 X1-02、原子能力 X1-03,勾选后准确填写 公司名称、所属行业类型,上传公司营业执照单击确定提交审核申请,等待人工审核流程。



2. 测试版 License 成功创建后,页面会显示生成的 License 信息。此时 Key 和 LicenseURL 两个参数暂未生效,等待提交审核通过后方可生效使用。在 SDK 初始化配置时需要传入 License URL 和 License Key 两个参数,请妥善保存以下信息




步骤二:导入 SDK

TRTC SDK 和腾讯特效 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

# TRTC 精简版
# 安装包体积增量最小,但仅支持实时音视频(TRTC)和 直播播放器(TXLivePlayer)两项功能。
pod 'TXLiteAVSDK_TRTC', :podspec => 'https://liteav.sdk.qcloud.com/pod/liteavsdkspec/TXLiteAVSDK_TRTC.podspec'

# Professional 专业版
# 包含实时音视频(TRTC)、直播播放器(TXLivePlayer)、RTMP 推流(TXLivePusher)、点播播放器(TXVodPlayer)和短视频录制和编辑(UGSV)等众多功能。
pod 'TXLiteAVSDK_Professional', :podspec => 'https://liteav.sdk.qcloud.com/pod/liteavsdkspec/TXLiteAVSDK_Professional.podspec'

# 腾讯特效 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 并手动导入,详见 手动导入 TRTC SDK手动导入腾讯特效 SDK
5. 添加美颜资源到实际项目工程中
下载并解压对应套餐的 SDK 和美颜资源,将 resources/motionRes 文件夹下 bundle 资源添加到实际工程中。
在 Build Settings 中的Other Linker Flags添加 -ObjC
6. 将 Bundle Identifier 修改成与申请的测试授权一致。

步骤三:工程配置

1. 权限配置
秀场直播场景下 TRTC SDK 及腾讯特效 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 ,如下图所示:




步骤四:鉴权与许可

TRTC 鉴权凭证
腾讯特效鉴权许可
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权,TRTC 在进房时校验该鉴权凭证。
调试跑通阶段:可以通过 客户端示例代码控制台 两种方法计算生成 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. 校验通过后,会向 TRTC SDK 提供实时音视频服务。



注意:
调试跑通阶段的本地 UserSig 计算方式不推荐应用到线上环境,容易被逆向破解导致密钥泄露。
我们提供了多个语言版本(Java/GO/PHP/Nodejs/Python/C#/C++)的 UserSig 服务端计算源代码,详见 UserSig 计算源码

使用腾讯美颜特效之前,需要向腾讯云校验许可凭证。设置 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

初始化 TRTC SDK
初始化腾讯特效 SDK
// 创建 TRTC 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;
// 销毁 TRTC 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]
说明:
初始化腾讯特效 SDK 之前,还需进行资源拷贝等准备工作,详细步骤可参考 腾讯特效 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,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
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!");
    }
}
注意:
TRTC 房间号分为整型 roomId 和字符串类型 strRoomId,两种类型的房间不互通,建议统一房间号类型。
TRTC 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
秀场直播场景下,进房模式建议选用 TRTCAppSceneLIVE。

步骤二:观众进房拉流

1. 观众进入 TRTC 房间。
- (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,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
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. 解散房间
服务端解散:TRTC 提供了服务端解散房间 API DismissRoom(区分数字房间 ID 和字符串房间 ID),您可以调用此接口把房间所有用户从房间移出,并解散房间。
客户端解散:各个客户端调用退出房间 exitRoom 接口,当房间内的所有主播和观众完成退房后,根据 TRTC 房间生命周期规则,房间将会自动解散,详情请参见 退出房间
警告:
建议您当一次直播结束之后,可以在服务端调用解散房间 API 确保房间解散,防止观众意外进房导致产生非期望的费用。

备选方案

API 时序图





步骤一:主播旁路推流

1. 前置条件:打开 TRTC 控制台,在应用管理页选择目标应用,点击配置进入基础功能页面,点击开启旁路转推,同时建议选择指定流旁路。旁路转推域名可在 云直播控制台 添加,也可使用系统默认的推流域名。



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


- (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,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
enterRoom 之前调用以上接口,SDK 只会开启摄像头预览和音频采集,并一直等到您调用 enterRoom 之后才开始推流。
enterRoom 之后调用以上接口,SDK 会开启摄像头预览和音频采集,并自动开始推流。
3. 主播设置本地画面的渲染参数,以及编码器输出画面模式。
- (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];
}
注意:
设置本地画面渲染参数仅影响本地画面的渲染效果。
设置编码器输出模式会影响房间中其他用户所观看到(以及云端录制文件)的画面效果。
4. 主播正式开始直播,进房推流。
- (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!");
    }
}
注意:
TRTC 房间号分为整型 roomId 和字符串类型 strRoomId,两种类型的房间不互通,建议统一房间号类型。
TRTC 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
秀场直播场景下,进房模式建议选用 TRTCAppSceneLIVE
5. 主播将音视频流转推到直播 CDN。
- (void)startPublishMediaToCDN:(NSString *)streamName {
    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0];
    // 设定推流地址过期时间
    NSTimeInterval time = [date timeIntervalSince1970] + (24 * 60 * 60);
    // 生成鉴权信息,getSafeUrl 方法可在云直播控制台-域名管理-推流配置-推流地址示例代码获取
    NSString *secretParam = [self getSafeUrl:LIVE_URL_KEY streamName:streamName time:time];
    
    // 媒体流发布的目标地址
    TRTCPublishTarget* target = [[TRTCPublishTarget alloc] init];
    // 目标地址设定为旁路转推到 CDN
    target.mode = TRTCPublishBigStreamToCdn;
    TRTCPublishCdnUrl* cdnUrl = [[TRTCPublishCdnUrl alloc] init];
    // 拼接发布到直播服务商的推流地址(RTMP 格式)
    cdnUrl.rtmpUrl = [NSString stringWithFormat:@"rtmp://%@/live/%@?%@", PUSH_DOMAIN, streamName, secretParam];
    // 腾讯云直播推流地址为 true,第三方为 false
    cdnUrl.isInternalLine = YES;
    NSMutableArray* cdnUrlList = [NSMutableArray array];
    // 可以添加多个 CDN 推流地址
    [cdnUrlList addObject:cdnUrl];
    target.cdnUrlList = cdnUrlList;
    
    // 设置媒体流编码输出参数(可根据业务需求自定义)
    TRTCStreamEncoderParam* encoderParam = [[TRTCStreamEncoderParam alloc] init];
    encoderParam.audioEncodedSampleRate = 48000;
    encoderParam.audioEncodedChannelNum = 1;
    encoderParam.audioEncodedKbps = 50;
    encoderParam.audioEncodedCodecType = 0;
    encoderParam.videoEncodedWidth = 540;
    encoderParam.videoEncodedHeight = 960;
    encoderParam.videoEncodedFPS = 15;
    encoderParam.videoEncodedGOP = 2;
    encoderParam.videoEncodedKbps = 1300;

    // 开始发布媒体流
    [self.trtcCloud startPublishMediaStream:target encoderParam:encoderParam mixingConfig:nil];
}
注意:
单主播直播时仅需发起旁路转推任务,待有观众连麦或主播 PK 时再将此任务更新为混流转码任务。
推流鉴权 KEY LIVE_URL_KEY 及推流域名 PUSH_DOMAIN 等信息需在 云直播控制台-域名管理 获取。
国际站用户、旧版架构用户如调用 startPublishMediaStream 报错,请 联系我们 协助发布配置。
发布媒体流后,SDK 会通过回调 onStartPublishMediaStream 带给您后台启动的任务标识(即 taskId)。
- (void)onStartPublishMediaStream:(NSString *)taskId code:(int)code message:(NSString *)message extraInfo:(NSDictionary *)extraInfo {
    // taskId: 当请求成功时,TRTC 后台会在回调中提供给您这项任务的 taskId,后续您可以通过该 taskId 结合 updatePublishMediaStream 和 stopPublishMediaStream 进行更新和停止
    // code: 回调结果,0 表示成功,其余值表示失败
}

步骤二:观众拉流播放

CDN 观众无需进入 TRTC 房间,可直接拉取主播旁路 CDN 流播放。
// 初始化播放器
self.livePlayer = [[V2TXLivePlayer alloc] init];
// 设置播放器回调监听
[self.livePlayer setObserver:self];
// 设置播放器的视频渲染控件
[self.livePlayer setRenderView:self.remoteView];
// 设置延迟调节模式(可选项)
[self.livePlayer setCacheParams:1.f maxTime:5.f]; // 自动模式
[self.livePlayer setCacheParams:1.f maxTime:1.f]; // 极速模式
[self.livePlayer setCacheParams:5.f maxTime:5.f]; // 流畅模式

// 拼接拉流播放地址
NSString *flvUrl = [NSString stringWithFormat:@"http://%@/live/%@.flv", PLAY_DOMAIN, streamName];
NSString *hlsUrl = [NSString stringWithFormat:@"http://%@/live/%@.m3u8", PLAY_DOMAIN, streamName];
NSString *rtmpUrl = [NSString stringWithFormat:@"rtmp://%@/live/%@", PLAY_DOMAIN, streamName];
NSString *webrtcUrl = [NSString stringWithFormat:@"webrtc://%@/live/%@", PLAY_DOMAIN, streamName];

// 启动播放
[self.livePlayer startLivePlay:flvUrl];

// 自定义设置填充模式(可选项)
[self.livePlayer setRenderFillMode:V2TXLiveFillModeFit];
// 自定义设置画面渲染方向(可选项)
[self.livePlayer setRenderRotation:V2TXLiveRotation0];
注意:
播放域名 PLAY_DOMAIN 需要您在 云直播控制台 添加自有已备案域名进行直播播放,并需 配置域名 CNAME
直播播放功能需设置 Licence 后方可成功播放,否则将播放失败(黑屏),全局仅设置一次即可。若您暂未获取 Licence,可 快速免费申请测试版 Licence 以正常播放,正式版 Licence 需 购买
[TXLiveBase setLicenceURL:LICENSEURL key:LICENSEURLKEY];

步骤三:观众连麦互动

1. 连麦观众需要进入 TRTC 房间与主播实时互动。
// 进入 TRTC 房间并开始推流
- (void)enterRoomWithUserId:(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 startLocalMedia];
    // 以互动直播场景进房
    [self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];
}

// 开启本地视频预览及音频采集
- (void)startLocalMedia {
    // 设置视频编码参数,决定远端用户看到的画面质量
    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];
}

// 进房结果事件回调
- (void)onEnterRoom:(NSInteger)result {
    if (result > 0) {
        // result 代表加入房间所消耗的时间(毫秒)
        NSLog(@"Enter room succeed!");
    } else {
        // result 代表进房失败的错误码
        NSLog(@"Enter room failed!");
    }
}
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
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];
    }
}

- (void)onFirstVideoFrame:(NSString *)userId streamType:(TRTCVideoStreamType)streamType width:(int)width height:(int)height {
    // SDK 开始渲染本地或远端用户的首帧画面
    if (![userId isEqualToString:@""]) {
        // 收到主播首帧画面,停止播放 CDN 流
        [self.livePlayer stopPlay];
    }
}
注意:
TRTC 拉流 startRemoteView 可直接复用之前 CDN 拉流 setRenderView 的视频渲染控件。
为避免在切换拉流器时画面中断,建议等待收到 TRTC 首帧回调 onFirstVideoFrame 后再停止 CDN 拉流。
3. 主播更新发布混合媒体流。
// 连麦观众进房事件回调
- (void)onRemoteUserEnterRoom:(NSString *)userId {
    if (![self.mixUserList containsObject:userId]) {
        [self.mixUserList addObject:userId];
    }
    [self updatePublishMediaToCDN];
}

// 更新发布混合媒体流到直播 CDN
- (void)updatePublishMediaToCDN {
    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0];
    // 设定推流地址过期时间
    NSTimeInterval time = [date timeIntervalSince1970] + (24 * 60 * 60);
    // 生成鉴权信息,getSafeUrl 方法可在云直播控制台-域名管理-推流配置-推流地址示例代码获取
    NSString *secretParam = [self getSafeUrl:LIVE_URL_KEY streamName:self.streamName time:time];
    
    // 媒体流发布的目标地址
    TRTCPublishTarget* target = [[TRTCPublishTarget alloc] init];
    // 目标地址设定为混流转推到 CDN
    target.mode = TRTCPublishMixStreamToCdn;
    TRTCPublishCdnUrl* cdnUrl = [[TRTCPublishCdnUrl alloc] init];
    // 拼接发布到直播服务商的推流地址(RTMP 格式)
    cdnUrl.rtmpUrl = [NSString stringWithFormat:@"rtmp://%@/live/%@?%@", PUSH_DOMAIN, self.streamName, secretParam];
    // 腾讯云直播推流地址为 true,第三方为 false
    cdnUrl.isInternalLine = YES;
    NSMutableArray* cdnUrlList = [NSMutableArray array];
    // 可以添加多个 CDN 推流地址
    [cdnUrlList addObject:cdnUrl];
    target.cdnUrlList = cdnUrlList;
    
    // 设置媒体流编码输出参数
    TRTCStreamEncoderParam* encoderParam = [[TRTCStreamEncoderParam alloc] init];
    encoderParam.audioEncodedSampleRate = 48000;
    encoderParam.audioEncodedChannelNum = 1;
    encoderParam.audioEncodedKbps = 50;
    encoderParam.audioEncodedCodecType = 0;
    encoderParam.videoEncodedWidth = 540;
    encoderParam.videoEncodedHeight = 960;
    encoderParam.videoEncodedFPS = 15;
    encoderParam.videoEncodedGOP = 2;
    encoderParam.videoEncodedKbps = 1300;
    
    TRTCStreamMixingConfig *config = [[TRTCStreamMixingConfig alloc] init];
    if (self.mixUserList.count) {
        NSMutableArray<TRTCUser *> *userList = [NSMutableArray array];
        NSMutableArray<TRTCVideoLayout *> *layoutList = [NSMutableArray array];
        for (int i = 1; i < MIN(self.mixUserList.count, 16); i++) {
            TRTCUser *user = [[TRTCUser alloc] init];
            // 整型房间号为 intRoomId
            user.strRoomId = self.roomId;
            user.userId = self.mixUserList[i];
            [userList addObject:user];
            TRTCVideoLayout *layout = [[TRTCVideoLayout alloc] init];
            if ([self.mixUserList[i] isEqualToString:self.userId]) {
                // 主播画面布局
                layout.rect = CGRectMake(0, 0, 540, 960);
                layout.zOrder = 0;
            } else {
                // 连麦观众画面布局
                layout.rect = CGRectMake(400, 5 + i * 245, 135, 240);
                layout.zOrder = 1;
            }
            layout.fixedVideoUser = user;
            layout.fixedVideoStreamType = TRTCVideoStreamTypeBig;
            [layoutList addObject:layout];
        }
        // 指定转码流中的每一路输入音频的信息
        config.audioMixUserList = [userList copy];
        // 指定混合画面的中每一路视频画面的位置、大小、图层以及流类型等信息
        config.videoLayoutList = [layoutList copy];
    }
    // 更新发布媒体流
    [self.trtcCloud updatePublishMediaStream:self.taskId publishTarget:target encoderParam:encoderParam mixingConfig:config];
}

// 更新媒体流的事件回调
- (void)onUpdatePublishMediaStream:(NSString *)taskId code:(int)code message:(NSString *)message extraInfo:(NSDictionary *)extraInfo {
    // 您调用媒体流发布接口 (updatePublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次更新请求
    // code: 回调结果,0 表示成功,其余值表示失败
}
注意:
为了保证 CDN 播放连续并且不会发生断流,您需要保持媒体流编码输出参数 encoderParam 以及流名称 streamName 不变。
媒体流编码输出参数、混合画面布局参数可根据业务需求自定义;当前仅支持最高16路音视频输入,如果用户只有音频也会被算作一路。
同一个任务不支持纯音频、音视频、纯视频之间的切换。
4. 观众下麦退房,主播更新混流任务。

// 设置播放器回调监听
[self.livePlayer setObserver:self];
// 可复用 TRTC 视频渲染控件
[self.livePlayer setRenderView:self.remoteView];
// 重新开始播放 CDN 媒体流
[self.livePlayer startLivePlay:flvUrl];


- (void)onVideoLoading:(id<V2TXLivePlayer>)player extraInfo:(NSDictionary *)extraInfo {
    // 视频加载事件
}

// 视频播放事件
- (void)onVideoPlaying:(id<V2TXLivePlayer>)player firstPlay:(BOOL)firstPlay extraInfo:(NSDictionary *)extraInfo {
    if (firstPlay) {
        [self.trtcCloud stopAllRemoteView];
        [self.trtcCloud stopLocalAudio];
        [self.trtcCloud stopLocalPreview];
        [self.trtcCloud exitRoom];
    }
}
注意:
为避免在切换拉流器时画面中断,建议等待收到播放器视频播放事件 onVideoPlaying 后再退出 TRTC 房间。
// 连麦观众退房事件回调
- (void)onRemoteUserLeaveRoom:(NSString *)userId reason:(NSInteger)reason {
    if ([self.mixUserList containsObject:userId]) {
        [self.mixUserList removeObject:userId];
    }
    // 主播更新混流任务
    [self updatePublishMediaToCDN];
}

// 更新媒体流的事件回调
- (void)onUpdatePublishMediaStream:(NSString *)taskId code:(int)code message:(NSString *)message extraInfo:(NSDictionary *)extraInfo {
    // 您调用媒体流发布接口 (updatePublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次更新请求
    // code: 回调结果,0 表示成功,其余值表示失败
}

步骤四:主播关播与退房

- (void)exitRoom {
    // 停止所有发布的媒体流
    [self.trtcCloud stopPublishMediaStream:@""];
    [self.trtcCloud stopLocalAudio];
    [self.trtcCloud stopLocalPreview];
    [self.trtcCloud exitRoom];
}

// 停止媒体流的事件回调
- (void)onStopPublishMediaStream:(NSString *)taskId code:(int)code message:(NSString *)message extraInfo:(NSDictionary *)extraInfo {
    // 您调用停止发布媒体流 (stopPublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次停止请求
    // code: 回调结果,0 表示成功,其余值表示失败
}

// 离开房间事件回调
- (void)onExitRoom:(NSInteger)reason {
    if (reason == 0) {
        NSLog(@"主动调用 exitRoom 退出房间");
    } else if (reason == 1) {
        NSLog(@"被服务器踢出当前房间");
    } else if (reason == 2) {
        NSLog(@"当前房间整个被解散");
    }
}
注意:
停止发布媒体流传参 taskId 填写空字符串,将会停止所有您发布的媒体流。
待 SDK 占用的所有资源释放完毕后,SDK 会抛出 onExitRoom 回调通知到您。

高级功能

主播跨房 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 连麦的本地用户和对端用户必须都为主播角色,且必须都有音频/视频上行。
可通过多次调用 ConnectOtherRoom() 来实现与多个房间主播跨房连麦,目前限制一个房间最多可以和其他三个房间的主播跨房连麦,一个房间中最多10个主播可与其他房间的主播跨房连麦。
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 连麦。

第三方美颜接入

TRTC 支持接入第三方美颜特效产品,下面以腾讯特效为例,展示第三方美颜接入流程。
1. 集成腾讯特效 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 处理每帧数据结果传入 TRTC SDK 内部做渲染处理。
// TRTC 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是 TRTC 集成第三方美颜的通用且重要步骤
场景化集成腾讯美颜特效指引详见 TRTC SDK 集成腾讯特效,独立集成腾讯美颜特效指引详见 独立集成腾讯特效

双路编码模式

开启双路编码模式后,当前用户的编码器会同时输出【高清大画面】和【低清小画面】两路视频流(但只有一路音频流)。这样,房间中的其他用户就可以根据自身的网络情况或屏幕大小选择订阅【高清大画面】或是【低清小画面】。
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 来拉取低清小画面观看。

视图渲染控件

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

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

直播互动消息

直播互动在直播场景中尤为重要,用户通过点赞、赠送礼物、发送弹幕等方式与主播进行互动。实现直播互动功能的前提是开通 即时通信 IM 服务,并导入 IM 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);
}
}
}

弹幕消息

秀场直播间通常会有文本形式的弹幕消息互动,这里可以通过 IM 的发送及接收群聊普通文本消息来实现。
// 发送公屏弹幕消息
[[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 渲染弹幕消息
}
注意:
建议将礼物消息设置为高优先级;弹幕消息设置为正常优先级;点赞消息设置为低优先级。
本人在客户端发送群聊消息,不会触发消息接收回调,只有群组内的其他用户可以接收到。

异常处理

异常错误处理

TRTC SDK 遇到不可恢复的错误会在 onError 回调中抛出,详见 TRTC 错误码表
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
麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。

本地和远端镜像模式设置

默认情况下,TRTC 本地和远端画面的镜像模式策略如下:
使用前置摄像头采集:本地预览画面无镜像效果,远端观众观看有镜像效果。
使用后置摄像头采集:本地预览画面无镜像效果,远端观众观看无镜像效果。
当然,您也可以通过 setLocalRenderParamssetVideoEncoderMirror 分别自定义本地预览画面镜像效果,以及视频编码输出画面的镜像效果(远端观众及云端录制的镜像模式)。如果您希望同时设置本地预览的镜像效果和远端观众观看的镜像效果,请按照如下方法进行编码。
// 设置本地画面的渲染参数
TRTCRenderParams *params = [[TRTCRenderParams alloc] init];
params.mirrorType = TRTCVideoMirrorTypeEnable; // 画面镜像模式
params.fillMode = TRTCVideoFillMode_Fill; // 画面填充模式
params.rotation = TRTCVideoRotation_0; // 画面旋转角度
[self.trtcCloud setLocalRenderParams:params];

// 设置编码器输出的画面镜像模式
[self.trtcCloud setVideoEncoderMirror:YES];

采集设备旋转导致观众画面旋转

如果您遇到采集设备(主播端手机)在旋转翻转时,观众端拉流观看的画面渲染方向发生变化,您可以通过关闭 SDK 重力感应来避免这种现象。
[self.trtcCloud setGravitySensorAdaptiveMode:TRTCGravitySensorAdaptiveMode_Disable];
说明:
该接口只需在采集端(主播端)调用,更多关于横竖屏视频画面旋转的控制方案详见 视频画面旋转

RTMP 与 TRTC 互通问题

为降低客户接入门槛,TRTC 支持 RTMP 标准协议推拉流。您可根据实际情况选择安装 OBSFFmpeg 或其他 RTMP 库进行推流。
本功能目前已结束免费内测,接入的 RTMP 流会作为房间中的虚拟用户产生正常的通话费用。具体接入指引详见 实时音视频 通过 RTMP 与 TRTC 互通
RTMP 属于 TRTC 一个子模块,能与 TRTC 其他端互通,互通延迟正常情况下小于600ms,也可使用 TRTC 录制、转推等已有能力。网络架构如下图所示。




推流地址生成

rtmp://rtmp.rtc.qq.com/push/房间号?sdkappid=应用&userid=用户名&usersig=签名
注意:
本功能自2023年02月15日起结束免费内测,如需使用该功能需要订阅 TRTC 应用包月套餐尊享版或旗舰版来解锁功能。
本功能默认只支持字符串房间号,不超过64个字符;如需使用数字房间号,则需在推拉流 URL 地址中拼接参数 &use_number_room_id=1

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

秀场直播场景下,主播可能会对摄像头有自定义调整的需求,TRTC 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];