为了保证最佳体验效果,本场景需要集成火山引擎的 RTC SDK 以及 HIFIVE 音乐开放平台的SDK,您需要在 RTC、HIFIVE的控制台开通服务,相应开通指南如下:



当前仅提供了你已通过其他渠道获取音乐文件时,实现 KTV 场景的参考实现。RTC 也提供从曲库接入到 KTV 场景的完整能力,如果希望获取参考文档,请咨询技术支持。

/**
* 加入RTC房间并初始化参数
* @param token: RTC Token
* @param roomID: RTC room id
* @param uid: RTC user id
* @param isHost: YES 业务上主播 ; NO 业务上观众
**/
- (void)joinRTCRoomWithToken:(NSString *)token
roomID:(NSString *)roomID
uid:(NSString *)uid
isHost:(BOOL)isHost {
// 初始化 ByteRTCVideo 对象
self.rtcEngineKit = [ByteRTCVideo createRTCVideo:APPID
delegate:self
parameters:@{}];
// 初始化 ByteRTCRoom 对象
self.rtcRoom = [self.rtcEngineKit createRTCRoom:roomID];
self.rtcRoom.delegate = self;
// 设置音频场景类型 Music
[self.rtcEngineKit setAudioScenario:ByteRTCAudioScenarioMusic];
// 设置音频双声道音乐音质
[self.rtcEngineKit setAudioProfile:ByteRTCAudioProfileHD];
// 设置主播为可见,观众为隐身
[self.rtcRoom setUserVisibility:isHost];
// 加入房间时主播需要开启麦克风,观众需要关闭麦克风
if (isHost) {
[self.rtcEngineKit startAudioCapture];
} else {
[self.rtcEngineKit stopAudioCapture];
}
// 设置音频路由模式
[self.rtcEngineKit setDefaultAudioRoute:ByteRTCAudioRouteSpeakerphone];
// 开启发言者音量监听
ByteRTCAudioPropertiesConfig *audioPropertiesConfig = [[ByteRTCAudioPropertiesConfig alloc] init];
audioPropertiesConfig.interval = 300;
[self.rtcEngineKit enableAudioPropertiesReport:audioPropertiesConfig];
// 加入房间,开始连麦,需要申请AppId和Token
ByteRTCUserInfo *userInfo = [[ByteRTCUserInfo alloc] init];
userInfo.userId = uid;
ByteRTCRoomConfig *config = [[ByteRTCRoomConfig alloc] init];
config.profile = ByteRTCRoomProfileKTV;
config.isAutoPublish = YES;
config.isAutoSubscribeAudio = YES;
[self.rtcRoom joinRoom:token userInfo:userInfo roomConfig:config];
}
objectivec/**
* ByteRTCRoomDelegate 中的房间状态改变回调,加入房间、离开房间、发生房间相关的警告或错误时会收到此回调。
* @param roomId 房间 ID。
* @param uid 用户 ID。
* @param state 房间状态码。
* @param extraInfo 额外信息。
*/
- (void)rtcRoom:(ByteRTCRoom *)rtcRoom onRoomStateChanged:(NSString *)roomId
withUid:(NSString *)uid
state:(NSInteger)state
extraInfo:(NSString *)extraInfo {
// 收到 RTC 加入房间结果
}
/**
* ByteRTCVideoDelegate 中的用户音量信息回调
* @param engine ByteRTCVideo 对象
* @param audioPropertiesInfos 本地音频信息
*/
- (void)rtcEngine:(ByteRTCVideo *)engine onLocalAudioPropertiesReport:(NSArray<ByteRTCLocalAudioPropertiesInfo *> *)audioPropertiesInfos {
// 本地用户音量回调
}
/**
* ByteRTCVideoDelegate 中的远端用户音量回调
* @param engine ByteRTCVideo 对象
* @param audioPropertiesInfos 远端音频信息,其中包含音频流属性、房间 ID、用户 ID。
* @param totalRemoteVolume 订阅的所有远端流的总音量,范围是 [0,255]。
*/
- (void)rtcEngine:(ByteRTCVideo *)engine onRemoteAudioPropertiesReport:(NSArray<ByteRTCRemoteAudioPropertiesInfo *> *)audioPropertiesInfos
totalRemoteVolume:(NSInteger)totalRemoteVolume {
// 远端用户音量回调
}
objectivec
状态变化
/**
* 演唱者角色变化(上下麦)
*/
- (void)switchRoleBecomeSinger:(BOOL)isSinger {
// 设置可见性
[self.rtcRoom setUserVisibility:isSinger];
// 开启/关闭 本地音频采集
if (isSinger) {
[self.rtcEngineKit startAudioCapture];
} else {
[self.rtcEngineKit stopAudioCapture];
}
}
/**
* 主唱、副唱和观众更新音频流订阅状态
*/
- (void)updateAudioSubscribe {
if (!isChorusing) {
return;
}
// 在合唱中
// 如果自己是主唱,取消订阅副唱音频
if (role == LeadSinger) {
[self.rtcRoom unsubscribeStream:succentorUid mediaStreamType:ByteRTCMediaStreamTypeAudio];
}
// 如果自己是观众,取消订阅主唱音频
else if (role == Audience) {
[self.rtcRoom unsubscribeStream:leadSingerUid mediaStreamType:ByteRTCMediaStreamTypeAudio];
}
}
objectivec主唱
/**
* 开始演唱。在歌词/歌曲下载完成,收到开始演唱广播后执行。
* @param filePath 歌曲的文件路径
*/
- (void)startStartSinging:(NSString *)filePath {
ByteRTCAudioMixingManager *audioMixingManager = [self.rtcEngineKit getAudioMixingManager];
ByteRTCAudioMixingConfig *config = [[ByteRTCAudioMixingConfig alloc] init];
// 设置歌曲本地播放和推送到远端
config.type = ByteRTCAudioMixingTypePlayoutAndPublish;
// 设置歌曲播放一次
config.playCount = 1;
// 开始播放歌曲
[audioMixingManager startAudioMixing:audioMixingID
filePath:filePath
config:config];
// 设置歌曲进度回调,100ms 间隔回调一次
[audioMixingManager setAudioMixingProgressInterval:audioMixingID
interval:100];
}
/**
* ByteRTCVideoDelegate 中收到本地歌曲播放进度回调
* @param engine ByteRTCVideo 对象
* @param mixId 混音ID
* @param progress 歌曲播放进度,单位为毫秒
*/
- (void)rtcEngine:(ByteRTCVideo *)engine onAudioMixingPlayingProgress:(NSInteger)mixId progress:(int64_t)progress {
// 主唱发送音频流同步信息
NSString *millisecondStr = [NSString stringWithFormat:@"%ld", (long)(progress)];
NSData *data = [millisecondStr dataUsingEncoding:NSUTF8StringEncoding];
ByteRTCStreamSycnInfoConfig *config = [[ByteRTCStreamSycnInfoConfig alloc] init];
[self.rtcEngineKit sendStreamSyncInfo:data config:config];
// 刷新本地歌词进度
[self reloadLocalLyrics:progress];
}
/**
* ByteRTCVideoDelegate 中的音乐文件播放状态改变回调
* @param engine 当前 RTC SDK 对象
* @param mixId 混音 ID
* @param state 混音状态
* @param error 错误码
*/
- (void)rtcEngine:(ByteRTCVideo * _Nonnull)engine onAudioMixingStateChanged:(NSInteger)mixId state:(ByteRTCAudioMixingState)state error:(ByteRTCAudioMixingError)error {
if (state == ByteRTCAudioMixingStateFinished) {
// 歌曲播放结束
}
}
objectivec副唱
/**
* 副唱开启混音
*/
- (void)startSuccentorAudioMixing {
// 开启PCM混音
ByteRTCAudioMixingManager *manager = [self.rtcEngineKit getAudioMixingManager];
[manager enableAudioMixingFrame:audioMixingID type:ByteRTCAudioMixingTypePublish];
// 监听远端用户音频数据
[self.rtcEngineKit registerAudioFrameObserver:self];
ByteRTCAudioFormat *format = [[ByteRTCAudioFormat alloc] init];
format.sampleRate = ByteRTCAudioSampleRateAuto;
format.channel = ByteRTCAudioChannelAuto;
[self.rtcEngineKit enableAudioFrameCallback:ByteRTCAudioFrameCallbackRemoteUser format:format];
}
/**
* 副唱停止合唱
*/
- (void)stopSuccentorAudioMixing {
// 关闭监听远端用户音频数据
[self.rtcEngineKit disableAudioFrameCallback:ByteRTCAudioFrameCallbackRemoteUser];
// 关闭PCM混音
ByteRTCAudioMixingManager *manager = [self.rtcEngineKit getAudioMixingManager];
[manager disableAudioMixingFrame:audioMixingID];
}
/**
* ByteRTCAudioFrameObserver 中的音频数据回调
* @param streamKey 远端流信息。
* @param audioFrame 音频数据。
*/
- (void)onRemoteUserAudioFrame:(ByteRTCRemoteStreamKey * _Nonnull)streamKey audioFrame:(ByteRTCAudioFrame * _Nonnull)audioFrame {
// 副唱转推房间内主唱的音频
ByteRTCAudioMixingManager *manager = [self.rtcEngineKit getAudioMixingManager];
[manager pushAudioMixingFrame:audioMixingID audioFrame:audioFrame];
}
/**
* ByteRTCVideoDelegate 中的音频流同步信息回调
* @param engine 当前 ByteRTCVideo 实例。
* @param remoteStreamKey 远端流信息。
* @param streamType 媒体流类型。
* @param data 消息内容。
*/
- (void)rtcEngine:(ByteRTCVideo *)engine onStreamSyncInfoReceived:(ByteRTCRemoteStreamKey *)remoteStreamKey streamType:(ByteRTCSyncInfoStreamType)streamType data:(NSData *)data {
// 转发主唱的音频流同步信息
ByteRTCStreamSycnInfoConfig *config = [[ByteRTCStreamSycnInfoConfig alloc] init];
[self.rtcEngineKit sendStreamSyncInfo:data config:config];
// 刷新本地歌词进度
[self reloadLocalLyrics:data];
}
objectivec观众
/**
* ByteRTCVideoDelegate 中的音频流同步信息回调
* @param engine 当前 ByteRTCVideo 实例。
* @param remoteStreamKey 远端流信息。
* @param streamType 媒体流类型。
* @param data 消息内容。
*/
- (void)rtcEngine:(ByteRTCVideo *)engine onStreamSyncInfoReceived:(ByteRTCRemoteStreamKey *)remoteStreamKey streamType:(ByteRTCSyncInfoStreamType)streamType data:(NSData *)data {
// 刷新本地歌词进度
[self reloadLocalLyrics:data];
}
objectivec

/**
* 原唱/伴奏切换,需要音乐素材支持右声道伴奏,左声道原唱
* @param isAccompaniment 是否为伴奏,YES 为伴奏,NO 为原唱
*/
- (void)setIsAccompaniment:(BOOL)isAccompaniment {
ByteRTCAudioMixingManager *audioMixingManager = [self.rtcEngineKit getAudioMixingManager];
if (isAccompaniment) {
[audioMixingManager setAudioMixingDualMonoMode:audioMixingID
mode:ByteRTCAudioMixingDualMonoModeR];
} else {
[audioMixingManager setAudioMixingDualMonoMode:audioMixingID
mode:ByteRTCAudioMixingDualMonoModeL];
}
}
/**
* 暂停混音播放
*/
- (void)pauseSinging {
ByteRTCAudioMixingManager *audioMixingManager = [self.rtcEngineKit getAudioMixingManager];
[audioMixingManager pauseAudioMixing:audioMixingID];
}
/**
* 恢复混音播放
*/
- (void)resumeSinging {
ByteRTCAudioMixingManager *audioMixingManager = [self.rtcEngineKit getAudioMixingManager];
[audioMixingManager resumeAudioMixing:audioMixingID];
}
/**
* 开启/关闭耳返
* @param isEnable YES 为开启耳返,NO 为关闭耳返
*/
- (void)enableEarMonitor:(BOOL)isEnable {
[self.rtcEngineKit setEarMonitorMode:isEnable ? ByteRTCEarMonitorModeOn : ByteRTCEarMonitorModeOff];
}
/**
* 设置耳返的音量
* @param volume 耳返音量
*/
- (void)setEarMonitorVolume:(NSInteger)volume {
[self.rtcEngineKit setEarMonitorVolume:volume];
}
/**
* 调节本地播放和远端混音的音量大小
* @param volume 混音音量
*/
- (void)setMusicVolume:(NSInteger)volume {
ByteRTCAudioMixingManager *audioMixingManager = [self.rtcEngineKit getAudioMixingManager];
[audioMixingManager setAudioMixingVolume:audioMixingID volume:(int)volume type:ByteRTCAudioMixingTypePlayoutAndPublish];
}
/**
* 调节麦克风采集音量
* @param volume 麦克风采集音量
*/
- (void)setRecordingVolume:(NSInteger)volume {
[self.rtcEngineKit setCaptureVolume:ByteRTCStreamIndexMain volume:(int)volume];
}
/**
* 设置混响特效类型
* @param reverbType 特效类型
*/
- (void)setVoiceReverbType:(ByteRTCVoiceReverbType)reverbType {
[self.rtcEngineKit setVoiceReverbType:reverbType];
}
objectivec| 功能点 | 回调 |
|---|---|
| 本地用户加入 RTC 回调 | rtcRoom:onRoomStateChanged:uid:state:extraInfo |
| 音频流同步信息回调 | rtcEngine:onStreamSyncInfoReceived:streamType:data: |
| 返回远端单个用户的音频数据 | onRemoteUserAudioFrame:audioFrame: |
| 本地音频的相关信息回调 | rtcEngine:onLocalAudioPropertiesReport: |
| 订阅的远端用户的音频信息回调 | rtcEngine:onRemoteAudioPropertiesReport:totalRemoteVolume: |
| 音频混音文件播放状态改变时回调 | rtcEngine:onAudioMixingStateChanged:state:error: |
| 音频播放路由变化回调 | rtcEngine:onAudioRouteChanged: |
| 混音音频文件播放进度回调 | rtcEngine:onAudioMixingPlayingProgress:progress: |
| 远端视频流的状态改变回调 | rtcEngine:onRemoteVideoStateChanged:withVideoState:withVideoStateReason: |