iOS SDK集成

本文介绍K歌房多人合唱模式iOS SDK集成的代码示例及集成说明。

功能时序图

使用自研播放器播放音乐

image.png

使用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]; //渲染远端视图
        });
    }
}];

主播点歌

  1. 添加伴奏流并创建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];
  2. 准备好物料文件之后,持续解码输入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];
  3. 输入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通知其他人 */