本文介绍K歌房多人合唱模式iOS SDK集成的代码示例及集成说明。
功能时序图
使用自研播放器播放音乐
使用RTC解码和播放音乐
如果您需要使用实时音视频RTC解码和播放音乐,请提交工单咨询。关于如何提交工单,请参见联系我们。
主播创建房间
/* 实现AliRtcEngineDelegate 监听回调 */
_engine = [AliRtcEngine sharedInstance:self extras:extras];
//设置频道模式
[self.engine setChannelProfile:AliRtcInteractivelive];
//设置用户角色
[self.engine setClientRole:AliRtcClientRoleInteractive];
/* 使用高音质和ktv场景 */
[self.engine setAudioProfile:AliRtcEngineHighQualityMode audio_scene:AliRtcSceneKtvMode];
//拉流设置
[self.engine setDefaultSubscribeAllRemoteAudioStreams:YES];
[self.engine subscribeAllRemoteAudioStreams:YES];
[self.engine publishLocalAudioStream:YES];
[self.engine setAudioOnlyMode:YES];
/* 是否听筒播放 */
[self.engine enableSpeakerphone:ctrl.useSpeaker];
NSMutableDictionary *raw_token = [[NSMutableDictionary alloc] init];
[raw_token setValue:info.appId forKey:@"appid"];
[raw_token setValue:info.channelId forKey:@"channelid"];
[raw_token setValue:info.userId forKey:@"userid"];
[raw_token setValue:info.nonce forKey:@"nonce"];
[raw_token setValue:@(info.timestamp) forKey:@"timestamp"];
[raw_token setValue:info.gslb forKey:@"gslb"];
[raw_token setValue:info.token forKey:@"token"];
NSData *token_data = [NSJSONSerialization dataWithJSONObject:raw_token options:NSJSONWritingPrettyPrinted error:nil];
NSString *token_str = [token_data base64EncodedStringWithOptions:0];
[self.engine joinChannel:token_str channelId:nil userId:nil name:nil onResultWithUserId:^(NSInteger errCode, NSString * _Nonnull channel, NSString * _Nonnull userId, NSInteger elapsed) {
NSString *sting = [NSString stringWithFormat:@"joinRst: %d", (int)errCode];
[weakSelf log:sting];
if(errCode != 0 && ![weakSelf.engine isInCall]){
weakSelf.callState = YES; // restore gui
}else{
weakSelf.callState = NO; //入会成功
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addTableView]; //渲染远端视图
});
}
}];
观众进入房间
/* 实现AliRtcEngineDelegate 监听回调 */
_engine = [AliRtcEngine sharedInstance:self extras:extras];
//设置频道模式
[self.engine setChannelProfile:AliRtcInteractivelive];
//设置用户角色
[self.engine setClientRole:AliRtcClientRolelive];
/* 使用高音质和ktv场景 */
[self.engine setAudioProfile:AliRtcEngineHighQualityMode audio_scene:AliRtcSceneKtvMode];
//拉流设置
[self.engine setDefaultSubscribeAllRemoteAudioStreams:YES];
[self.engine subscribeAllRemoteAudioStreams:YES];
[self.engine publishLocalAudioStream:YES];
[self.engine setAudioOnlyMode:YES];
/* 是否听筒播放 */
[self.engine enableSpeakerphone:ctrl.useSpeaker];
NSMutableDictionary *raw_token = [[NSMutableDictionary alloc] init];
[raw_token setValue:info.appId forKey:@"appid"];
[raw_token setValue:info.channelId forKey:@"channelid"];
[raw_token setValue:info.userId forKey:@"userid"];
[raw_token setValue:info.nonce forKey:@"nonce"];
[raw_token setValue:@(info.timestamp) forKey:@"timestamp"];
[raw_token setValue:info.gslb forKey:@"gslb"];
[raw_token setValue:info.token forKey:@"token"];
NSData *token_data = [NSJSONSerialization dataWithJSONObject:raw_token options:NSJSONWritingPrettyPrinted error:nil];
NSString *token_str = [token_data base64EncodedStringWithOptions:0];
[self.engine joinChannel:token_str channelId:nil userId:nil name:nil onResultWithUserId:^(NSInteger errCode, NSString * _Nonnull channel, NSString * _Nonnull userId, NSInteger elapsed) {
NSString *sting = [NSString stringWithFormat:@"joinRst: %d", (int)errCode];
[weakSelf log:sting];
if(errCode != 0 && ![weakSelf.engine isInCall]){
weakSelf.callState = YES; // restore gui
}else{
weakSelf.callState = NO; //入会成功
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addTableView]; //渲染远端视图
});
}
}];
主播点歌
添加伴奏流并创建pcm输入。
[self.engine publishLocalDualAudioStream:true]; AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new]; config.channels = _pcmLocalChannels; config.sampleRate = _pcmLocalSampleRate; /* 如果需要外放出来声音,设置外放音量 */ config.publishVolume = 60; config.playoutVolume = 100; /* 通过伴奏流发送音频伴奏 */ config.publishStream = 1; /* 该stream id需要存下来,用于添加pcm数据和删除流 */ _externalPlayoutStreamId = [self.engine addExternalAudioStream:config]; [self.engine subscribeAllRemoteDualAudioStreams:true];
准备好物料文件之后,持续解码输入pcm数据。
/* ... 持续送入pcm数据... */ AliRtcAudioFrame *sample = [AliRtcAudioFrame new]; sample.dataPtr = _pcmLocalData; sample.samplesPerSec = _pcmLocalSampleRate; sample.bytesPerSample = sizeof(int16_t); sample.numOfChannels = _pcmLocalChannels; sample.numOfSamples = numOfSamples; int rc = [self.engine pushExternalAudioStream:_externalPlayoutStreamId rawData:sample];
输入pcm数据的同时,更新进度。
/* 发送datachannel 进度信息 */ AliRtcDataChannelMsg* msg = [[AliRtcDataChannelMsg alloc] init]; msg.type = AliRtcDataMsgCustom; msg.networkTime = [self.engine getNetworkTime]; msg.progress = progress; [self.engine sendDataChannelMessage:msg];
观众获取进度更新歌词
- (void)onDataChannelMessage:(NSString *_Nonnull)uid controlMsg:(AliRtcDataChannelMsg*_Nonnull)controlMsg {
/* 更新歌词进度到 controlMsg.progress */
}
观众上麦成为参唱者
/* 参与演唱不需要订阅伴奏流,本地播放 */
[self.engine subscribeAllRemoteDualAudioStreams:false];
[self.engine setClientRole:AliRtcClientRoleInteractive];
参唱者接受进度更新播放位置
- (void)onDataChannelMessage:(NSString *_Nonnull)uid controlMsg:(AliRtcDataChannelMsg*_Nonnull)controlMsg {
long long div = [self.engine getNetworkTime] - controlMsg.networkTime;
long long progress = controlMsg.progress + div;
/* 更新歌曲播放进度到progress */
}
参唱者下麦成为观众
/* 观众需要订阅远端的流来播放伴奏 */
[self.engine subscribeAllRemoteDualAudioStreams:true];
[self.engine setClientRole:AliRtcClientRolelive];
主播切歌
[self.engine removeExternalAudioStream:_externalAudioStreamId];
/* 停止伴奏流 */
[self.engine publishLocalDualAudioStream:false];
/* 停止歌曲解码 、IM通知其他人 */
文档内容是否对您有帮助?