为了保证最佳体验效果,本场景需要集成火山引擎的 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]; } /** * 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 { // 远端用户音量回调 }
状态变化
/** * 演唱者角色变化(上下麦) */ - (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]; } }
主唱
/** * 开始演唱。在歌词/歌曲下载完成,收到开始演唱广播后执行。 * @param filePath 歌曲的文件路径 */ - (void)startStartSinging:(NSString *)filePath { ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; ByteRTCMediaPlayerConfig *config = [[ByteRTCMediaPlayerConfig alloc]init]; config.autoPlay = YES; // 设置歌曲本地播放和推送到远端 config.type = ByteRTCAudioMixingTypePlayoutAndPublish; // 设置歌曲播放一次 config.playCount = 1; // 开始播放歌曲 [mediaPlayer open:filePath config:config]; // 设置歌曲进度回调,100ms 间隔回调一次 [mediaPlayer setProgressInterval: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) { // 歌曲播放结束 } }
副唱
/** * 副唱开启混音 */ - (void)startSuccentorAudioMixing { // 开启PCM混音 ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; ByteRTCMediaPlayerCustomSource *mdsSource = [[ByteRTCMediaPlayerCustomSource alloc] init]; mdsSource.provider = (id<ByteRTCMediaPlayerCustomSourceProvider>)source; mdsSource.mode = ByteRTCMediaPlayerCustomSourceModePull; mdsSource.type = ByteRTCMediaPlayerCustomSourceStreamTypeEncoded; ByteRTCMediaPlayerConfig *config = [[ByteRTCMediaPlayerConfig alloc] init]; config.type = ByteRTCAudioMixingTypePlayoutAndPublish; config.playCount = 1; config.startPos = startPos; config.callbackOnProgressInterval = self.ktvInfo.callbackOnProgressInterval; config.syncProgressToRecordFrame = YES; config.autoPlay = NO; [mediaPlayer openWithCustomSource:self.mdsSource config:config]; // 监听远端用户音频数据 [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混音 ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; [mediaPlayer stop]; } /** * ByteRTCAudioFrameObserver 中的音频数据回调 * @param streamKey 远端流信息。 * @param audioFrame 音频数据。 */ - (void)onRemoteUserAudioFrame:(ByteRTCRemoteStreamKey * _Nonnull)streamKey audioFrame:(ByteRTCAudioFrame * _Nonnull)audioFrame { // 副唱转推房间内主唱的音频 ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; [mediaPlayer pushExternalAudioFrame: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]; }
观众
/** * 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]; }
/** * 原唱/伴奏切换,需要音乐素材支持右声道伴奏,左声道原唱 * @param isAccompaniment 是否为伴奏,YES 为伴奏,NO 为原唱 */ - (void)setIsAccompaniment:(BOOL)isAccompaniment { ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; if (isAccompaniment) { [mediaPlayer setAudioDualMonoMode:ByteRTCAudioMixingDualMonoModeR]; } else { [mediaPlayer setAudioDualMonoMode:ByteRTCAudioMixingDualMonoModeL]; } } /** * 暂停混音播放 */ - (void)pauseSinging { ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; [mediaPlayer pause]; } /** * 恢复混音播放 */ - (void)resumeSinging { ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; [mediaPlayer resume]; } /** * 开启/关闭耳返 * @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 { ByteRTCMediaPlayer *mediaPlayer = [self.rtcEngineKit getMediaPlayer:0]; [mediaPlayer setVolume: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]; }
功能点 | API |
---|---|
创建 RTCVideo 对象 | createRTCVideo:delegate:parameters: |
设置音频场景类型 | setAudioScenario: |
设置音质档位 | setAudioProfile: |
开启本地音频采集 | startAudioCapture |
开启内部视频采集 | startVideoCapture |
创建 RTC 房间 | createRTCRoom: |
设置用户可见性 | setUserVisibility: |
加入RTC 房间 | joinRoom:userInfo:roomConfig: |
开始播放音频文件 | open:config: |
设置混音时音频文件播放进度回调的间隔 | setProgressInterval: |
发送音频流同步信息 | sendStreamSyncInfo:config: |
订阅房间内指定的通过摄像头/麦克风采集的媒体流 | subscribeStream:mediaStreamType: |
取消订阅房间内指定的通过摄像头/麦克风采集的媒体流 | unSubscribeStream:mediaStreamType: |
启动 PCM 音频数据混音 | openWithCustomSource:config: |
注册音频数据回调观察者。 | registerAudioFrameObserver: |
开启音频回调 | enableAudioFrameCallback:format: |
推送 PCM 音频帧数据用于混音 | pushExternalAudioFrame: |
启用音频信息提示。 | enableAudioPropertiesReport: |
设置当前音频文件的声道模式 | setAudioDualMonoMode: |
暂停混音播放 | pause |
恢复混音播放 | resume |
打开/关闭耳返功能 | setEarMonitorMode: |
设置耳返音量 | setEarMonitorVolume: |
调节音频采集音量 | setCaptureVolume:volume: |
设置混响特效类型 | setVoiceReverbType: |
离开 RTC 房间 | leaveRoom |
销毁 RTC 房间引擎 | destroy |
关闭内部音频采集 | stopAudioCapture |
销毁 RTCVideo 对象 | destroyRTCVideo |
功能点 | 回调 |
---|---|
本地用户加入 RTC 回调 | rtcRoom:onRoomStateChanged:uid:state:extraInfo |
音频流同步信息回调 | rtcEngine:onStreamSyncInfoReceived:streamType:data: |
返回远端单个用户的音频数据 | onRemoteUserAudioFrame:audioFrame: |
本地音频的相关信息回调 | rtcEngine:onLocalAudioPropertiesReport: |
订阅的远端用户的音频信息回调 | rtcEngine:onRemoteAudioPropertiesReport:totalRemoteVolume: |
音频混音文件播放状态改变时回调 | onMediaPlayerStateChanged:state:error: |
音频播放路由变化回调 | rtcEngine:onAudioRouteChanged: |
混音音频文件播放进度回调 | onMediaPlayerPlayingProgress:progress: |
远端视频流的状态改变回调 | rtcEngine:onRemoteVideoStateChanged:withVideoState:withVideoStateReason: |