You need to enable JavaScript to run this app.
导航

场景搭建(iOS)

最近更新时间2024.04.11 14:39:16

首次发布时间2022.11.15 16:06:43

SDK集成

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

  1. RTC SDK总体接入流程,详细步骤请参看 RTC服务开通指南

alt

  1. HIFIVE 音乐开放平台 SDK 接入流程,详细步骤请参看 HIFIVE 集成文档

整体实现流程

整体业务流程图

alt

数据流向图

alt

核心功能实现

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

创建/加入房间

时序图

alt

示例代码

/**
 * 加入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 {
    // 远端用户音量回调
}

双人合唱

时序图

alt

示例代码

状态变化

/** 
 * 演唱者角色变化(上下麦)
 */
- (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];
}

歌曲控制台

alt

alt

示例代码

/**
 * 原唱/伴奏切换,需要音乐素材支持右声道伴奏,左声道原唱
 * @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 与回调

API

功能点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: