产品概述
产品优势
应用场景







curl -X POST \\http://service-mqk0mc83-1257411467.bj.apigw.tencentcs.com/release/register \\-H 'Content-Type: application/json' \\-H 'Cache-control: no-cache' \\-d '{"requestId": "test-regisiter-service","action": "Register","registerRequest": {"operateUin": <operateUin>,"userName": <customedName>,"cosConfig": {"secretId": <CosConfig.secretId>,"secretKey": <CosConfig.secretKey>,"bucket": <CosConfig.bucket>,"region": <CosConfig.region>}}}'
{"requestId": "test-regisiter-service","registerInfo": {"tmpContentId": <tmpContentId>,"tmpSecretId": <tmpSecretId>,"tmpSecretKey": <tmpSecretKey>,"apiGateSecretId": <apiGateSecretId>,"apiGateSecretKey": <apiGateSecretKey>,"demoCosPath": "UIN_demo/run_musicBeat.py","usageDescription": "请从COS桶[CosConfig.bucket]中下载python版本demo文件[UIN_demo/run_musicBeat.py], 替换demo中的输入文件后,执行python run_musicBeat.py","message": "注册成功,感谢注册","createdAt": <createdAt>,"updatedAt": <updatedAt>}}
demoCosPath 目录下生成一个以音乐鼓点识别能力为例的 python 版本的可执行 demo,请在有网络的环境下,执行命令 python run_musicBeat.py 验证。sudo gem install cocoapods
pod init
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'end
pod install
pod update
pod setuppod repo updaterm ~/Library/Caches/CocoaPods/search_index.json
Privacy - Microphone Usage Description, 同时填入麦克风使用目的提示语



// 创建 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];
- (void)enterRoomWithRoomId:(NSString *)roomId userID:(NSString *)userId {TRTCParams *params = [[TRTCParams alloc] init];// 以字符串房间号为例params.strRoomId = roomId;params.userId = userId;// 从业务后台获取到的 UserSigparams.userSig = [self generateUserSig:userId];// 替换成您的 SDKAppIDparams.sdkAppId = SDKAppID;// 建议均以观众角色进房params.role = TRTCRoleAudience;// 进房场景须选择 LIVE[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];}
TRTCAppSceneLIVE。// 进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)NSLog(@"Enter room succeed!");// 开启补黑帧的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":true}}"];} else {// result 代表进房失败的错误码NSLog(@"Enter room failed!");}}
// 切换为主播角色[self.trtcCloud switchRole:TRTCRoleAnchor];// 切换角色事件回调- (void)onSwitchRole:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {if (errCode == ERR_NULL) {// 设置媒体音量类型[self.trtcCloud setSystemVolumeType:TRTCSystemVolumeTypeMedia];// 上行本地音频流,设置音质[self.trtcCloud startLocalAudio:TRTCAudioQualityMusic];}}
// 获取音频特效管理类self.audioEffectManager = [self.trtcCloud getAudioEffectManager];// originMusicId: 自定义原唱音乐标识;originMusicUrl: 原唱音乐资源地址TXAudioMusicParam *originMusicParam = [[TXAudioMusicParam alloc] init];originMusicParam.ID = originMusicId;originMusicParam.path = originMusicUrl;// 是否将原唱发布到远端(否则仅本地播放)originMusicParam.publish = YES;// accompMusicId: 自定义伴奏音乐标识;accompMusicUrl: 伴奏音乐资源地址TXAudioMusicParam *accompMusicParam = [[TXAudioMusicParam alloc] init];accompMusicParam.ID = accompMusicId;accompMusicParam.path = accompMusicUrl;// 是否将伴奏发布到远端(否则仅本地播放)accompMusicParam.publish = YES;// 开始播放原唱音乐[self.audioEffectManager startPlayMusic:originMusicParam onStart:^(NSInteger errCode) {// onStart} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// onProgress} onComplete:^(NSInteger errCode) {// onComplete}];// 开始播放伴奏音乐[self.audioEffectManager startPlayMusic:originMusicParam onStart:^(NSInteger errCode) {// onStart} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// onProgress} onComplete:^(NSInteger errCode) {// onComplete}];// 切换至原唱音乐[self.audioEffectManager setMusicPlayoutVolume:originMusicId volume:100];[self.audioEffectManager setMusicPublishVolume:originMusicId volume:100];[self.audioEffectManager setMusicPlayoutVolume:accompMusicId volume:0];[self.audioEffectManager setMusicPublishVolume:accompMusicId volume:0];// 切换至伴奏音乐[self.audioEffectManager setMusicPlayoutVolume:originMusicId volume:0];[self.audioEffectManager setMusicPublishVolume:originMusicId volume:0];[self.audioEffectManager setMusicPlayoutVolume:accompMusicId volume:100];[self.audioEffectManager setMusicPublishVolume:accompMusicId volume:100];
[self.audioEffectManager startPlayMusic:musicParam onStart:^(NSInteger errCode) {// 音乐开始播放} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// 根据最新进度和本地歌词进度误差,判断是否需要 seek// 通过发送 SEI 消息传递歌曲进度NSDictionary *dic = @{@"musicId": @(self.musicId),@"progress": @(progressMs),@"duration": @(durationMs),};JSONModel *json = [[JSONModel alloc] initWithDictionary:dic error:nil];[self.trtcCloud sendSEIMsg:json.toJSONData repeatCount:1];} onComplete:^(NSInteger errCode) {// 音乐播放完成}];
// 切换为观众角色[self.trtcCloud switchRole:TRTCRoleAudience];// 切换角色事件回调- (void)onSwitchRole:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {if (errCode == ERR_NULL) {// 停止播放伴奏音乐[[self.trtcCloud getAudioEffectManager] stopPlayMusic:self.musicId];// 停止本地音频的采集和发布[self.trtcCloud stopLocalAudio];}}// 退出房间[self.trtcCloud exitRoom];// 退出房间事件回调- (void)onExitRoom:(NSInteger)reason {if (reason == 0) {NSLog(@"主动调用 exitRoom 退出房间");} else if (reason == 1) {NSLog(@"被服务器踢出当前房间");} else if (reason == 2) {NSLog(@"当前房间整个被解散");}}
onExitRoom 回调通知到您。enterRoom 或切换到其他音视频 SDK,请等待 onExitRoom 回调到来后再执行相关操作。否则可能会遇到例如摄像头、麦克风设备被强占等各种异常问题。// 进入房间- (void)enterRoomWithRoomId:(NSString *)roomId userID:(NSString *)userId {TRTCParams *params = [[TRTCParams alloc] init];// 以字符串房间号为例params.strRoomId = roomId;params.userId = userId;// 从业务后台获取到的 UserSigparams.userSig = [self generateUserSig:userId];// 替换成您的 SDKAppIDparams.sdkAppId = SDKAppID;// 建议均以观众角色进房params.role = TRTCRoleAudience;// 进房场景须选择 LIVE[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];}// 进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)NSLog(@"Enter room succeed!");} else {// result 代表进房失败的错误码NSLog(@"Enter room failed!");}}
TRTCAppSceneLIVE。- (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {if (available) {[self.trtcCloud startRemoteView:userId view:nil];} else {[self.trtcCloud stopRemoteView:userId];}}- (void)onRecvSEIMsg:(NSString *)userId message:(NSData *)message {JSONModel *json = [[JSONModel alloc] initWithData:message error:nil];NSDictionary *dic = json.toDictionary;int32_t musicId = [dic[@"musicId"] intValue];NSInteger progress = [dic[@"progress"] integerValue];NSInteger duration = [dic[@"duration"] integerValue];// ......// TODO 更新歌词控件逻辑:// 根据接收到的最新进度和本地歌词进度误差,判断是否需要 seek 歌词控件// ......}
// 退出房间[self.trtcCloud exitRoom];// 退出房间事件回调- (void)onExitRoom:(NSInteger)reason {if (reason == 0) {NSLog(@"主动调用 exitRoom 退出房间");} else if (reason == 1) {NSLog(@"被服务器踢出当前房间");} else if (reason == 2) {NSLog(@"当前房间整个被解散");}}
- (void)enterRoomWithRoomId:(NSString *)roomId userID:(NSString *)userId {// 创建 TRTCCloud 主实例(人声实例)TRTCCloud *mainCloud = [TRTCCloud sharedInstance];// 创建 TRTCCloud 子实例(音乐实例)TRTCCloud *subCloud = [mainCloud createSubCloud];// 主实例(人声实例)进房TRTCParams *params = [[TRTCParams alloc] init];params.strRoomId = roomId;params.userId = userId;params.userSig = userSig;params.sdkAppId = SDKAppID;params.role = TRTCRoleAnchor;[mainCloud enterRoom:params appScene:TRTCAppSceneLIVE];// 子实例开启手动订阅模式,默认不订阅远端流[subCloud setDefaultStreamRecvMode:NO video:NO];// 子实例(音乐实例)进房TRTCParams *bgmParams = [[TRTCParams alloc] init];bgmParams.strRoomId = roomId;// 子实例用户名不能与房间内其他用户重复bgmParams.userId = [userId stringByAppendingString:@"_bgm"];bgmParams.userSig = userSig;bgmParams.sdkAppId = SDKAppID;bgmParams.role = TRTCRoleAnchor;[subCloud enterRoom:bgmParams appScene:TRTCAppSceneLIVE];}
// 主实例进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// 主实例取消订阅子实例发布的音乐流[self.trtcCloud muteRemoteAudio:[self.userId stringByAppendingString:@"_bgm"] mute:YES];// 主实例开启补黑帧的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":true}}"];// 主实例开启合唱模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":0}}"];// 主实例开启低延时模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}"];// 主实例启用音量大小回调TRTCAudioVolumeEvaluateParams *aveParams = [[TRTCAudioVolumeEvaluateParams alloc] init];aveParams.interval = 300;[self.trtcCloud enableAudioVolumeEvaluation:YES withParams:aveParams];// 主实例设置全程媒体音量类型[self.trtcCloud setSystemVolumeType:TRTCSystemVolumeTypeMedia];// 主实例采集和发布本地音频,同时设置音质[self.trtcCloud startLocalAudio:TRTCAudioQualityMusic];} else {// result 代表进房失败的错误码NSLog(@"Enter room failed");}}// 子实例进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// 子实例开启合唱模式的实验性接口[self.subCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":1}}"];// 子实例开启低延时模式的实验性接口[self.subCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}"];// 子实例设置全程媒体音量类型[self.subCloud setSystemVolumeType:TRTCSystemVolumeTypeMedia];// 子实例设置音质[self.subCloud setAudioQuality:TRTCAudioQualityMusic];} else {// result 代表进房失败的错误码NSLog(@"Enter room failed");}}
audioSource 参数的不同。- (void)startPublishMediaToRoomWithRoomId:(NSString *)roomId userId:(NSString *)userId {// 创建 TRTCPublishTarget 对象TRTCPublishTarget *target = [[TRTCPublishTarget alloc] init];// 混流后回推到房间target.mode = TRTCPublishMixStreamToRoom;TRTCUser *mixStreamIdentity = [[TRTCUser alloc] init];mixStreamIdentity.strRoomId = roomId;// 混流机器人用户名不能与房间内其他用户重复mixStreamIdentity.userId = [userId stringByAppendingString:@"_robot"];target.mixStreamIdentity = mixStreamIdentity;// 设置转码后的音频流的编码参数(可自定义)TRTCStreamEncoderParam *encoderParam = [[TRTCStreamEncoderParam alloc] init];encoderParam.audioEncodedChannelNum = 2;encoderParam.audioEncodedKbps = 64;encoderParam.audioEncodedCodecType = 2;encoderParam.audioEncodedSampleRate = 48000;// 设置转码后的视频流的编码参数(混入黑帧必填)encoderParam.videoEncodedFPS = 15;encoderParam.videoEncodedGOP = 3;encoderParam.videoEncodedKbps = 30;encoderParam.videoEncodedWidth = 64;encoderParam.videoEncodedHeight = 64;// 设置音频混流参数TRTCStreamMixingConfig *mixingConfig = [[TRTCStreamMixingConfig alloc] init];// 默认情况下填空值即可,代表会混合房间中的所有音频mixingConfig.audioMixUserList = nil;// 配置视频混流模板(混入黑帧必填)TRTCVideoLayout *layout = [[TRTCVideoLayout alloc] init];mixingConfig.videoLayoutList = @[layout];// 开始混流回推[self.trtcCloud startPublishMediaStream:target encoderParam:encoderParam mixingConfig:mixingConfig];}
- (void)updateNetworkTimeExample {[TXLiveBase sharedInstance].delegate = self;[TXLiveBase updateNetworkTime];}- (void)onUpdateNetworkTime:(int)errCode message:(NSString *)errMsg {// errCode 0: 校时成功且偏差在30ms以内;1: 校时成功但偏差可能在30ms以上;-1: 校时失败if (errCode == 0) {// 校时成功,获取 NTP 时间戳NSInteger ntpTime = [TXLiveBase getNetworkTimestamp];} else {NSLog(@"校时失败,可尝试重新校时");}}
- (void)sendChorusSignalExample {__weak typeof(self) weakSelf = self;NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {__strong typeof(weakSelf) strongSelf = weakSelf;NSDictionary *dic = @{@"cmd": @"startChorus",// 约定合唱开始时间: 当前 NTP 时间 + 延迟播放时间(例如3秒)@"startPlayMusicTS": @([TXLiveBase getNetworkTimestamp] + 3000),@"musicId": @(self.musicId),@"musicDuration": @([[strongSelf.subCloud getAudioEffectManager] getMusicDurationInMS:strongSelf.originMusicUri]),};JSONModel *json = [[JSONModel alloc] initWithDictionary:dic error:nil];[strongSelf.trtcCloud sendCustomCmdMsg:1 data:json.toJSONData reliable:NO ordered:NO];}];[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];}
// 获取音频特效管理类TXAudioEffectManager *audioEffectManager = [self.subCloud getAudioEffectManager];// originMusicId: 自定义原唱音乐标识;originMusicUrl: 原唱音乐资源地址TXAudioMusicParam *originMusicParam = [[TXAudioMusicParam alloc] init];originMusicParam.ID = originMusicId;originMusicParam.path = originMusicUrl;// 是否将原唱发布到远端(否则仅本地播放)originMusicParam.publish = YES;// 音乐开始播放的时间点(毫秒)originMusicParam.startTimeMS = 0;// accompMusicId: 自定义伴奏音乐标识;accompMusicUrl: 伴奏音乐资源地址TXAudioMusicParam *accompMusicParam = [[TXAudioMusicParam alloc] init];accompMusicParam.ID = accompMusicId;accompMusicParam.path = accompMusicUrl;// 是否将伴奏发布到远端(否则仅本地播放)accompMusicParam.publish = YES;// 音乐开始播放的时间点(毫秒)accompMusicParam.startTimeMS = 0;// 预加载原唱音乐[audioEffectManager preloadMusic:originMusicParam onProgress:nil onError:nil];// 预加载伴奏音乐[audioEffectManager preloadMusic:accompMusicParam onProgress:nil onError:nil];// 延迟播放时间(例如3秒)后开始播放原唱音乐[self.audioEffectManager startPlayMusic:originMusicParam onStart:^(NSInteger errCode) {// onStart} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// onProgress} onComplete:^(NSInteger errCode) {// onComplete}];// 延迟播放时间(例如3秒)后开始播放伴奏音乐[self.audioEffectManager startPlayMusic:originMusicParam onStart:^(NSInteger errCode) {// onStart} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// onProgress} onComplete:^(NSInteger errCode) {// onComplete}];// 切换至原唱音乐[self.audioEffectManager setMusicPlayoutVolume:originMusicId volume:100];[self.audioEffectManager setMusicPublishVolume:originMusicId volume:100];[self.audioEffectManager setMusicPlayoutVolume:accompMusicId volume:0];[self.audioEffectManager setMusicPublishVolume:accompMusicId volume:0];// 切换至伴奏音乐[self.audioEffectManager setMusicPlayoutVolume:originMusicId volume:0];[self.audioEffectManager setMusicPublishVolume:originMusicId volume:0];[self.audioEffectManager setMusicPlayoutVolume:accompMusicId volume:100];[self.audioEffectManager setMusicPublishVolume:accompMusicId volume:100];
// 约定的合唱开始时间@property (nonatomic, assign) NSInteger startPlayMusicTS;- (void)syncBgmExample {// 当前伴奏音乐的实际播放进度NSInteger currentProgress = [[self.subCloud getAudioEffectManager] getMusicCurrentPosInMS:self.musicId];// 当前伴奏音乐的理想播放进度NSInteger estimatedProgress = [TXLiveBase getNetworkTimestamp] - self.startPlayMusicTS;// 当进度差超过50ms,进行修正if (estimatedProgress >= 0 && labs(currentProgress - estimatedProgress) > 50) {[[self.subCloud getAudioEffectManager] seekMusicToPosInMS:self.musicId pts:estimatedProgress];}}
[[self.subCloud getAudioEffectManager] startPlayMusic:musicParam onStart:^(NSInteger errCode) {// 音乐开始播放} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// 根据最新进度和本地歌词进度误差,判断是否需要 seek// 通过发送 SEI 消息传递歌曲进度NSDictionary *dic = @{@"musicId": @(self.musicId),@"progress": @(progressMs),@"duration": @(durationMs),};JSONModel *json = [[JSONModel alloc] initWithDictionary:dic error:nil];[self.trtcCloud sendSEIMsg:json.toJSONData repeatCount:1];} onComplete:^(NSInteger errCode) {// 音乐播放完成}];
- (void)exitRoomExample {// 子实例关闭合唱模式的实验性接口[self.subCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":1}}"];// 子实例关闭低延时模式的实验性接口[self.subCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}"];// 子实例切换为观众角色[self.subCloud switchRole:TRTCRoleAudience];// 子实例停止播放伴奏音乐[[self.subCloud getAudioEffectManager] stopPlayMusic:self.musicId];// 子实例退出房间[self.subCloud exitRoom];// 主实例关闭补黑帧的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":false}}"];// 主实例关闭合唱模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":0}}"];// 主实例关闭低延时模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}"];// 主实例切换为观众角色[self.trtcCloud switchRole:TRTCRoleAudience];// 主实例停止本地音频采集和发布[self.trtcCloud stopLocalAudio];// 主实例退出房间[self.trtcCloud exitRoom];}
- (void)enterRoomWithRoomId:(NSString *)roomId userID:(NSString *)userId {TRTCParams *params = [[TRTCParams alloc] init];// 以字符串房间号为例params.strRoomId = roomId;params.userId = userId;// 从业务后台获取到的 UserSigparams.userSig = [self generateUserSig:userId];// 替换成您的 SDKAppIDparams.sdkAppId = SDKAppID;// 示例以观众角色进房params.role = TRTCRoleAudience;// 进房场景须选择 LIVE[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];}// 进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)NSLog(@"Enter room succeed!");} else {// result 代表进房失败的错误码NSLog(@"Enter room failed!");}}
// 切换为主播角色[self.trtcCloud switchRole:TRTCRoleAnchor];// 切换角色事件回调- (void)onSwitchRole:(TXLiteAVError)errCode errMsg:(NSString *)errMsg {if (errCode == ERR_NULL) {// 取消订阅主唱子实例发布的音乐流[self.trtcCloud muteRemoteAudio:self.bgmUserId mute:YES];// 开启合唱模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":0}}"];// 开启低延时模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}"];// 设置媒体音量类型[self.trtcCloud setSystemVolumeType:TRTCSystemVolumeTypeMedia];// 上行本地音频流,设置音质[self.trtcCloud startLocalAudio:TRTCAudioQualityMusic];}}
- (void)updateNetworkTimeExample {[TXLiveBase sharedInstance].delegate = self;[TXLiveBase updateNetworkTime];}- (void)onUpdateNetworkTime:(int)errCode message:(NSString *)errMsg {// errCode 0: 校时成功且偏差在30ms以内;1: 校时成功但偏差可能在30ms以上;-1: 校时失败if (errCode == 0) {// 校时成功,获取 NTP 时间戳NSInteger ntpTime = [TXLiveBase getNetworkTimestamp];} else {NSLog(@"校时失败,可尝试重新校时");}}
- (void)onRecvCustomCmdMsgUserId:(NSString *)userId cmdID:(NSInteger)cmdID seq:(UInt32)seq message:(NSData *)message {JSONModel *json = [[JSONModel alloc] initWithData:message error:nil];NSDictionary *dic = json.toDictionary;// 匹配合唱信令if ([dic[@"cmd"] isEqualToString:@"startChorus"]) {self.startPlayMusicTS = [dic[@"startPlayMusicTS"] integerValue];self.musicId = [dic[@"musicId"] intValue];self.musicDuration = [dic[@"musicDuration"] intValue];// 约定合唱时间和当前时间差值self.delayMs = self.startPlayMusicTS - [TXLiveBase getNetworkTimestamp];}}
- (void)playBmgExample {// 合唱未开始if (self.delayMs > 0) {// 开始预加载音乐[self preloadMusicWithStartTimeMS:0];// 延迟 delayMs 后开始播放音乐[self startPlayMusicWithStartTimeMS:0];} else if (labs(self.delayMs) < self.musicDuration) {// 合唱进行中// 开始播放时间: 时间差值绝对值 + 预加载延迟(例如400ms)NSInteger startTimeMS = labs(self.delayMs) + 400;// 开始预加载音乐[self preloadMusicWithStartTimeMS:startTimeMS];// 预加载延迟(例如400ms)后开始播放音乐[self startPlayMusicWithStartTimeMS:startTimeMS];} else {// 合唱已结束// 不允许加入合唱}}// 预加载音乐- (void)preloadMusicWithStartTimeMS:(NSInteger)startTimeMS {// musicId: 从合唱信令获取;musicUrl: 对应的音乐资源地址TXAudioMusicParam *musicParam = [[TXAudioMusicParam alloc] init];musicParam.ID = self.musicId;musicParam.path = self.musicUrl;// 仅本地播放音乐musicParam.publish = NO;musicParam.startTimeMS = startTimeMS;[self.audioEffectManager preloadMusic:musicParam onProgress:nil onError:nil];}// 开始播放音乐- (void)startPlayMusicWithStartTimeMS:(NSInteger)startTimeMS {// musicId: 从合唱信令获取;musicUrl: 对应的音乐资源地址TXAudioMusicParam *musicParam = [[TXAudioMusicParam alloc] init];musicParam.ID = self.musicId;musicParam.path = self.musicUrl;// 仅本地播放音乐musicParam.publish = NO;musicParam.startTimeMS = startTimeMS;[self.audioEffectManager startPlayMusic:musicParam onStart:nil onProgress:nil onComplete:nil];}
delayMs 可判断当前合唱状态,不同状态下的 startPlayMusic 延迟调用需要开发者自行实现。// 约定的合唱开始时间@property (nonatomic, assign) NSInteger startPlayMusicTS;- (void)syncBgmExample {// 当前伴奏音乐的实际播放进度NSInteger currentProgress = [[self.trtcCloud getAudioEffectManager] getMusicCurrentPosInMS:self.musicId];// 当前伴奏音乐的理想播放进度NSInteger estimatedProgress = [TXLiveBase getNetworkTimestamp] - self.startPlayMusicTS;// 当进度差超过50ms,进行修正if (estimatedProgress >= 0 && labs(currentProgress - estimatedProgress) > 50) {[[self.trtcCloud getAudioEffectManager] seekMusicToPosInMS:self.musicId pts:estimatedProgress];}}
[self.audioEffectManager startPlayMusic:musicParam onStart:^(NSInteger errCode) {// 音乐开始播放} onProgress:^(NSInteger progressMs, NSInteger durationMs) {// TODO 更新歌词控件逻辑:// 根据最新进度和本地歌词进度误差,判断是否需要 seek 歌词控件} onComplete:^(NSInteger errCode) {// 音乐播放完成}];
- (void)exitRoomExample {// 关闭合唱模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":0}}"];// 关闭低延时模式的实验性接口[self.trtcCloud callExperimentalAPI:@"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}"];// 切换为观众角色[self.trtcCloud switchRole:TRTCRoleAudience];// 停止播放伴奏音乐[[self.trtcCloud getAudioEffectManager] stopPlayMusic:self.musicId];// 停止本地音频采集和发布[self.trtcCloud stopLocalAudio];// 退出房间[self.trtcCloud exitRoom];}
- (void)enterRoomWithRoomId:(NSString *)roomId userID:(NSString *)userId {TRTCParams *params = [[TRTCParams alloc] init];// 以字符串房间号为例params.strRoomId = roomId;params.userId = userId;// 从业务后台获取到的 UserSigparams.userSig = [self generateUserSig:userId];// 替换成您的 SDKAppIDparams.sdkAppId = SDKAppID;// 建议均以观众角色进房params.role = TRTCRoleAudience;// 进房场景须选择 LIVE[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];}// 进房结果事件回调- (void)onEnterRoom:(NSInteger)result {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)NSLog(@"Enter room succeed!");} else {// result 代表进房失败的错误码NSLog(@"Enter room failed!");}}
TRTCAppSceneLIVE。- (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {if (available) {[self.trtcCloud startRemoteView:userId view:nil];} else {[self.trtcCloud stopRemoteView:userId];}}- (void)onRecvSEIMsg:(NSString *)userId message:(NSData *)message {JSONModel *json = [[JSONModel alloc] initWithData:message error:nil];NSDictionary *dic = json.toDictionary;int32_t musicId = [dic[@"musicId"] intValue];NSInteger progress = [dic[@"progress"] integerValue];NSInteger duration = [dic[@"duration"] integerValue];// ......// TODO 更新歌词控件逻辑:// 根据接收到的最新进度和本地歌词进度误差,判断是否需要 seek 歌词控件// ......}
// 退出房间[self.trtcCloud exitRoom];// 退出房间事件回调- (void)onExitRoom:(NSInteger)reason {if (reason == 0) {NSLog(@"主动调用 exitRoom 退出房间");} else if (reason == 1) {NSLog(@"被服务器踢出当前房间");} else if (reason == 2) {NSLog(@"当前房间整个被解散");}}
{"action": "CreateJob","secretId": "{secretId}","secretKey": "{secretKey}","createJobRequest": {"customId": "{customId}","callback": "{callback}","inputs": [{ "url": "{url}" }],"outputs": [{"contentId": "{contentId}","destination": "{destination}","inputSelectors": [0],"smartContentDescriptor": {"outputPrefix": "{outputPrefix}","vocalScore": {"standardAudio": {"midi": {"url":"{url}"},"standardWav": {"url":"{url}"},"alignWav": {"url":"{url}"}}}}}]}}
{"requestId": "ac004192-110b-46e3-ade8-4e449df84d60","createJobResponse": {"job": {"id": "13f342e4-6866-450e-b44e-3151431c578b","state": 1,"customId": "{customId}","callback": "{callback}","inputs": [ { "url": "{url}" } ],"outputs": [{"contentId": "{contentId}","destination": "{destination}","inputSelectors": [ 0 ],"smartContentDescriptor": {"outputPrefix": "{outputPrefix}","vocalScore": {"standardAudio": {"midi": {"url":"{url}"},"standardWav": {"url":"{url}"},"alignWav": {"url":"{url}"}}}}}],"timing": {"createdAt": "1603432763000","startedAt": "0","completedAt": "0"}}}}
{"action": "GetJob","secretId": "{secretId}","secretKey": "{secretKey}","getJobRequest": {"id": "{id}"}}
{"requestId": "c9845a99-34e3-4b0f-80f5-f0a2a0ee8896","getJobResponse": {"job": {"id": "a95e9d74-6602-4405-a3fc-6408a76bcc98","state": 3,"customId": "{customId}","callback": "{callback}","timing": {"createdAt": "1610513575000","startedAt": "1610513575000","completedAt": "1610513618000"},"inputs": [{ "url": "{url}" }],"outputs": [{"contentId": "{contentId}","destination": "{destination}","inputSelectors": [0],"smartContentDescriptor": {"outputPrefix": "{outputPrefix}","vocalScore": {"standardAudio": {"midi": {"url":"{url}"},"standardWav": {"url":"{url}"},"alignWav": {"url":"{url}"}}}},"smartContentResult": {"vocalScore": "out.json"}}]}}}
- (void)onUserVoiceVolume:(NSArray<TRTCVolumeInfo *> *)userVolumes totalVolume:(NSInteger)totalVolume {if (userVolumes.count) {// 用于保存麦上用户对应的音量值NSMutableDictionary *volumesMap = [NSMutableDictionary dictionary];for (TRTCVolumeInfo *user in userVolumes) {// 可以设置适当的音量阈值if (user.volume > 10) {volumesMap[user.userId] = @(user.volume);}}JSONModel *json = [[JSONModel alloc] initWithDictionary:volumesMap error:nil];// 通过 SEI 消息发送麦上用户音量集合[self.trtcCloud sendSEIMsg:json.toJSONData repeatCount:1];}}- (void)onRecvSEIMsg:(NSString *)userId message:(NSData *)message {JSONModel *json = [[JSONModel alloc] initWithData:message error:nil];NSDictionary *dic = json.toDictionary;for (NSString *userId in dic.allKeys) {// 打印所有麦上用户单流的音量大小NSLog(@"%@: %@", userId, dic[userId]);}}
onNetworkQuality 来实时统计本地及远端用户的网络质量,该回调每隔2秒抛出一次。#pragma mark - TRTCCloudDelegate- (void)onNetworkQuality:(TRTCQualityInfo *)localQuality remoteQuality:(NSArray<TRTCQualityInfo *> *)remoteQuality {// localQuality代表本地用户网络质量评估结果// remoteQuality 代表远端用户网络质量评估结果,其结果受远端和本地共同影响switch(localQuality.quality) {case TRTCQuality_Unknown:NSLog(@"未定义");break;case TRTCQuality_Excellent:NSLog(@"当前网络非常好");break;case TRTCQuality_Good:NSLog(@"当前网络比较好");break;case TRTCQuality_Poor:NSLog(@"当前网络一般");break;case TRTCQuality_Bad:NSLog(@"当前网络较差");break;case TRTCQuality_Vbad:NSLog(@"当前网络很差");break;case TRTCQuality_Down:NSLog(@"当前网络不满足 TRTC 最低要求");break;default:break;}}

TRTCParams 中传入 privateMapKey 参数才可以成功进房。因此如果您线上有使用此 SDKAppID 的用户,请不要轻易开启此功能。TRTCParams *params = [[TRTCParams alloc] init];params.sdkAppId = SDKAppID;params.roomId = self.roomId;params.userId = self.userId;// 从业务后台获取到的 UserSigparams.userSig = [self getUserSig];// 从业务后台获取到的 PrivateMapKeyparams.privateMapKey = [self getPrivateMapKey];params.role = TRTCRoleAudience;[self.trtcCloud enterRoom:params appScene:TRTCAppSceneLIVE];
// 从业务后台获取到最新的 PrivateMapKey 传入切换角色接口[self.trtcCloud switchRole:TRTCRoleAnchor privateMapKey:[self getPrivateMapKey]];
枚举 | 取值 | 描述 |
ERR_TRTC_INVALID_USER_SIG | -3320 | 进房参数 UserSig 不正确,请检查 TRTCParams.userSig 是否为空。 |
ERR_TRTC_USER_SIG_CHECK_FAILED | -100018 | UserSig 校验失败,请检查参数 TRTCParams.userSig 是否填写正确或已经过期。 |
枚举 | 取值 | 描述 |
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.roomId 或 TRTCParams.strRoomId 是否为空,注意 roomId 和 strRoomId 不可混用。 |
ERR_TRTC_INVALID_USER_ID | -3319 | 进房参数 userId 不正确,请检查 TRTCParams.userId 是否为空。 |
ERR_TRTC_ENTER_ROOM_REFUSED | -3340 | 进房请求被拒绝,请检查是否连续调用 enterRoom 进入相同 ID 的房间。 |
枚举 | 取值 | 描述 |
ERR_MIC_START_FAIL | -1302 | 打开麦克风失败,例如在 Windows 或 Mac 设备,麦克风的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。 |
ERR_SPEAKER_START_FAIL | -1321 | 打开扬声器失败,例如在 Windows 或 Mac 设备,扬声器的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。 |
ERR_MIC_OCCUPY | -1319 | 麦克风正在被占用,例如移动设备正在通话时,打开麦克风会失败。 |
// 开启耳返[[self.trtcCloud getAudioEffectManager] enableVoiceEarMonitor:YES];// 设置耳返音量[[self.trtcCloud getAudioEffectManager] setVoiceEarMonitorVolume:volume];
setSystemAudioKitEnabled 开启硬件耳返来改善耳返延迟过高的问题。硬件耳返性能较好,且延迟较低;软件耳返延迟较高,但兼容性较好。目前,对于华为和 VIVO 设备,SDK 默认使用硬件耳返,其他设备默认使用软件耳返。如果硬件耳返存在兼容性问题,可以 联系我们 配置强制使用软件耳返。
[[self.trtcCloud getAudioEffectManager] preloadMusic:musicParam onProgress:nil onError:nil];
// 设置某一首背景音乐的本地播放音量的大小[[self.trtcCloud getAudioEffectManager] setMusicPlayoutVolume:self.musicId volume:volume];// 设置某一首背景音乐的远端播放音量的大小[[self.trtcCloud getAudioEffectManager] setMusicPublishVolume:self.musicId volume:volume];// 设置所有背景音乐的本地音量和远端音量的大小[[self.trtcCloud getAudioEffectManager] setAllMusicVolume:volume];// 设置人声采集音量的大小[[self.trtcCloud getAudioEffectManager] setVoiceVolume:volume];
文档反馈