This topic describes how to integrate the SDK for iOS to implement solo singing in a karaoke room.
The streamer creates a room
/* Implement AliRtcEngineDelegate to listen to related events */
_engine = [AliRtcEngine sharedInstance:self extras:extras];
// Set the channel profile
[self.engine setChannelProfile:AliRtcInteractivelive];
// Set the user role
[self.engine setClientRole:AliRtcClientRoleInteractive];
/* Set the audio profile to the high audio quality mode and set the scenario to KTV */
[self.engine setAudioProfile:AliRtcEngineHighQualityMode audio_scene:AliRtcSceneKtvMode];
// Configure stream pulling settings
[self.engine setDefaultSubscribeAllRemoteAudioStreams:YES];
[self.engine subscribeAllRemoteAudioStreams:YES];
[self.engine publishLocalAudioStream:YES];
[self.engine setAudioOnlyMode:YES];
/* Specify whether to use speaker for playback */
[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; // The user successfully joins the room.
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addTableView]; // Render the remote view.
});
}
}];
Audience join the room
Call the same API as creating a room and the audience role to the user.
/* Implement AliRtcEngineDelegate to listen to related events */
_engine = [AliRtcEngine sharedInstance:self extras:extras];
// Set the channel profile
[self.engine setChannelProfile:AliRtcInteractivelive];
// Set the user role
[self.engine setClientRole:AliRtcClientRoleInteractive];
/* Set the audio profile to the high audio quality mode and set the scenario to KTV */
[self.engine setAudioProfile:AliRtcEngineHighQualityMode audio_scene:AliRtcSceneKtvMode];
// Configure stream pulling settings
[self.engine setDefaultSubscribeAllRemoteAudioStreams:YES];
[self.engine subscribeAllRemoteAudioStreams:YES];
[self.engine publishLocalAudioStream:YES];
[self.engine setAudioOnlyMode:YES];
/* Specify whether to use speaker for playback */
[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; // The user successfully joins the room.
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addTableView]; // Render the remote view.
});
}
}];
The singer starts singing
AliRtcExternalAudioStreamConfig *config = [AliRtcExternalAudioStreamConfig new];
config.channels = _pcmLocalChannels;
config.sampleRate = _pcmLocalSampleRate;
/* Set the publish volume and playback volume */
config.publishVolume = 60;
config.playoutVolume = 100;
/* Save the stream ID for future operations, such as adding Pulse Code Modulation (PCM) data to the stream and deleting the stream. */
_externalPlayoutStreamId = [self.engine addExternalAudioStream:config];
The singer sends the song progress and plays the accompaniment
/* Input PCM data */
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];
/* Send the song progress information through the data channel */
AliRtcDataChannelMsg* msg = [[AliRtcDataChannelMsg alloc] init];
msg.type = AliRtcDataMsgCustom;
msg.networkTime = [self.engine getNetworkTime];
msg.data = [NSData dataWithBytes:&progress length:8];
[self.engine sendDataChannelMessage:msg];
The audience receives song progress
- (void)onDataChannelMessage:(NSString *_Nonnull)uid controlMsg:(AliRtcDataChannelMsg*_Nonnull)controlMsg {
long long progress = 0;
[controlMsg.data getBytes:&progress length:sizeof(time)];
}
The streamer stops song playback
[self.engine removeExternalAudioStream:_externalPlayoutStreamId];
/* Stop decoding the song and notify other users in the room by using the IM service */