为了保证最佳体验效果,本场景需要集成火山引擎的 RTC SDK 以及 HIFIVE 音乐开放平台的SDK,您需要在 RTC、HIFIVE的控制台开通服务,相应开通指南如下:
当前仅提供了你已通过其他渠道获取音乐文件时,实现 KTV 场景的参考实现。RTC 也提供从曲库接入到 KTV 场景的完整能力,如果希望获取参考文档,请咨询技术支持。
/** * 加入RTC房间并初始化参数 * @param token 加入 RTC 房间的 token * @param roomId 加入 RTC 房间的 id * @param userId 加入 RTC 房间的 用户id * @param isHost true 是业务上主播角色;false 是业务上观众角色 */ public void joinRTCRoom(String token, String roomId, String userId, boolean isHost) { // 初始化 RTCVideo 对象 mRTCVideo = RTCVideo.createRTCVideo(Utilities.getApplicationContext(), appId, mIRTCVideoEventHandler, null, null); // 初始化 RTCRoom 对象 mRTCRoom = mRTCVideo.createRTCRoom(roomId); mRTCRoom.setRTCRoomEventHandler(mIRTCRoomEventHandler); // 设置音频场景类型 Music mRTCVideo.setAudioScenario(AudioScenarioType.AUDIO_SCENARIO_MUSIC); // 设置音频双声道音乐音质 mRTCVideo.setAudioProfile(AudioProfileType.AUDIO_PROFILE_HD); // 设置主播为可见,观众为隐身 mRTCRoom.setUserVisibility(isHost); // 加入房间时主播需要开启麦克风,观众需要关闭麦克风 if (isHost) { mRTCVideo.startAudioCapture(); } else { mRTCVideo.stopAudioCapture(); } // 设置音频路由模式 mRTCVideo.setDefaultAudioRoute(AudioRoute.AUDIO_ROUTE_SPEAKERPHONE); // 开启发言者音量监听 AudioPropertiesConfig audioPropertiesConfig = new AudioPropertiesConfig(300); mRTCVideo.enableAudioPropertiesReport(audioPropertiesConfig); // 加入房间,开始连麦,需要申请AppId和Token UserInfo userInfo = new UserInfo(userId, null); RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_KTV, true, true, true); mRTCRoom.joinRoom(token, userInfo, roomConfig); }
private final IRTCVideoEventHandler mIRTCVideoEventHandler = new IRTCVideoEventHandler() { /** * 本地用户音量回调 */ @Override public void onLocalAudioPropertiesReport(LocalAudioPropertiesInfo[] audioPropertiesInfos) { super.onLocalAudioPropertiesReport(audioPropertiesInfos); } /** * 远端用户音量回调 */ @Override public void onRemoteAudioPropertiesReport(RemoteAudioPropertiesInfo[] audioPropertiesInfos, int totalRemoteVolume) { super.onRemoteAudioPropertiesReport(audioPropertiesInfos, totalRemoteVolume); } }; private final IRTCRoomEventHandler mIRTCRoomEventHandler = new IRTCRoomEventHandler() { /** * 收到 RTC 加入房间结果 */ @Override public void onRoomStateChanged(String roomId, String uid, int state, String extraInfo) { } }
/** * 演唱者角色变化(上下麦) * @param isSinger: 是否是演唱者 */ private void switchRoleBecomeSinger(boolean isSinger) { // 设置用户可见性 mRTCRoom.setUserVisibility(isSinger); // 开启/关闭 本地音频采集 if (isSinger) { mRTCVideo.startAudioCapture(); } else { mRTCVideo.stopAudioCapture(); } } /** * 主唱、副唱和观众更新音频流订阅状态 */ private void updateAudioSubscribe() { if (!mIsChorusing) { return; } // 在合唱中 // 如果自己是主唱,取消订阅副唱音频 if (mRole == LEAD_SINGER) { mRTCRoom.unsubscribeStream(succentorUid, MediaStreamType.RTC_MEDIA_STREAM_TYPE_AUDIO); } // 如果自己是观众,取消订阅主唱音频 else if (mRole == AUDIENCE) { mRTCRoom.unsubscribeStream(leadSingerUid, MediaStreamType.RTC_MEDIA_STREAM_TYPE_AUDIO); } }
/** * 开始演唱。在歌词/歌曲下载完成,收到开始演唱广播后执行。 * @param filePath 歌曲的文件路径 */ public void startStartSing(String filePath) { //根据播放器ID创建播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); //播放器配置 MediaPlayerConfig playerConfig = new MediaPlayerConfig(); // 开始播放歌曲 mediaPlayer.open(filePath, playerConfig); mediaPlayer.setProgressInterval(500); } /** * 收到本地歌曲播放进度回调 * @param mixId 混音ID * @param progress 歌曲播放进度,单位为毫秒 */ @Override public void onMediaPlayerPlayingProgress(int playerId, long progress); // 发送音频流同步信息 String progressStr = String.valueOf(progress); StreamSycnInfoConfig streamSycnInfoConfig = new StreamSycnInfoConfig( StreamIndex.STREAM_INDEX_MAIN, 3, StreamSycnInfoConfig.SyncInfoStreamType.SYNC_INFO_STREAM_TYPE_AUDIO ); mRTCVideo.sendStreamSyncInfo(progressStr.getBytes(StandardCharsets.UTF_8), streamSycnInfoConfig); // 刷新本地歌词进度 syncLocalLyricsProgress(progress); } /** * 音乐文件播放状态改变回调 */ @Override public void onMediaPlayerStateChanged(int playerId, PlayerState state, PlayerError error) if (state == PlayerState.FINISHED) { // 歌曲播放结束 } }
/** * 音频数据回调观察者:因为RTC引擎底层使用弱引用持有观察者需要应用层自己管理观察者的生命周期 */ private IAudioFrameObserver mAudioFrameObserver; /** * 副唱转发主唱音频 */ public void startSuccentorAudioMixing() { // 开启PCM混音 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); MediaPlayerCustomSource source = new MediaPlayerCustomSource(null, MediaPlayerCustomSourceMode.PUSH, MediaPlayerCustomSourceStreamType.RAW); mediaPlayer.openWithCustomSource(source, new MediaPlayerConfig(AudioMixingType.AUDIO_MIXING_TYPE_PLAYOUT_AND_PUBLISH, 1)); // 监听远端用户音频数据 mAudioFrameObserver = new IAudioFrameObserver() { @Override public void onRemoteUserAudioFrame(RemoteStreamKey streamInfo, IAudioFrame audioFrame) { // 副唱转推房间内主唱的音频 if (audioFrame.data_size() > 0) { AudioFrame frame = new AudioFrame(); int size = audioFrame.data_size(); frame.buffer = new byte[size]; ByteBuffer originalBuffer = audioFrame.getDataBuffer(); originalBuffer.get(frame.buffer); frame.channel = audioFrame.channel(); frame.sampleRate = audioFrame.sample_rate(); frame.samples = size / frame.channel.value() / 2; mediaPlayer.pushExternalAudioFrame(frame); } } }; mRTCVideo.enableAudioFrameCallback(AUDIO_FRAME_CALLBACK_REMOTE_USER, new AudioFormat(AudioSampleRate.AUDIO_SAMPLE_RATE_AUTO, AudioChannel.AUDIO_CHANNEL_AUTO)); mRTCVideo.registerAudioFrameObserver(mAudioFrameObserver); } /** * 副唱停止合唱 */ public void stopSuccentorAudioMixing() { // 关闭监听远端用户音频数据 mRTCVideo.disableAudioFrameCallback(AUDIO_FRAME_CALLBACK_REMOTE_USER); } /** * 收到音频同步信息 * @param streamKey 远端流信息 * @param streamType 媒体流类型 * @param data 消息内容 */ @Override public void onStreamSyncInfoReceived(RemoteStreamKey streamKey, StreamSycnInfoConfig.SyncInfoStreamType streamType, ByteBuffer data) { super.onStreamSyncInfoReceived(streamKey, streamType, data); // 转发主唱的音频流同步信息 Charset charset = StandardCharsets.UTF_8; CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = null; try { charBuffer = decoder.decode(data); } catch (CharacterCodingException e) { } String progressStr = charBuffer != null ? charBuffer.toString() : null; if (progressStr != null) { StreamSycnInfoConfig config = new StreamSycnInfoConfig(StreamIndex.STREAM_INDEX_MAIN, 0, StreamSycnInfoConfig.SyncInfoStreamType.SYNC_INFO_STREAM_TYPE_AUDIO); mRTCEngine.sendStreamSyncInfo(progressStr.getBytes(StandardCharsets.UTF_8), config); } // 刷新本地歌词进度 syncLocalLyricsProgress(progressStr); }
/** * 收到音频同步信息 * @param streamKey 远端流信息 * @param streamType 媒体流类型 * @param data 消息内容 */ @Override public void onStreamSyncInfoReceived(RemoteStreamKey streamKey, StreamSycnInfoConfig.SyncInfoStreamType streamType, ByteBuffer data) { super.onStreamSyncInfoReceived(streamKey, streamType, data); Charset charset = StandardCharsets.UTF_8; CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = null; try { charBuffer = decoder.decode(data); } catch (CharacterCodingException e) { } String progressStr = charBuffer != null ? charBuffer.toString() : null; // 刷新本地歌词进度 syncLocalLyricsProgress(progressStr); }
/** * 原唱/伴奏切换,需要音乐素材支持右声道伴奏,左声道原唱 * @param isAccompaniment 是否为伴奏,YES 为伴奏,NO 为原唱 */ public void switchAccompaniment(boolean isAccompaniment) { //根据播放器ID获取前面创建的播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); AudioMixingDualMonoMode audioMixingDualMonoMode = isAccompaniment ? AUDIO_MIXING_DUAL_MONO_MODE_R : AUDIO_MIXING_DUAL_MONO_MODE_L; //通过声道选择进行原唱伴奏切换 mediaPlayer.setAudioDualMonoMode(audioMixingDualMonoMode); } /** * 原唱/伴奏切换,需要音乐素材支持。通过切换音轨来实现切换原唱/伴奏 * @param audioTrackIndex 指定的播放音轨。 */ public void setIsAccompaniment(int audioTrackIndex) { //根据播放器ID获取前面创建的播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); //通过音轨选择进行原唱伴奏切换 mediaPlayer.selectAudioTrack(audioTrackIndex); } /** * 暂停混音播放 */ public void pauseSinging() { //根据播放器ID获取前面创建的播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); //暂停播放 mediaPlayer.pause(); } /** * 恢复混音播放 */ public void resumeSinging() { //根据播放器ID获取前面创建的播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); //暂停播放 mediaPlayer.resume(); } /** * 开启/关闭耳返 * @param isEnable YES 为开启耳返,NO 为关闭耳返 */ public void enableEarMonitor(boolean isEnable) { mRTCVideo.setEarMonitorMode(isEnable ? EarMonitorMode.EAR_MONITOR_MODE_ON : EarMonitorMode.EAR_MONITOR_MODE_OFF); } /** * 设置耳返的音量 * @param volume 耳返音量 */ public void setEarMonitorVolume(int volume) { mRTCVideo.setEarMonitorVolume(volume); } /** * 调节本地播放音量 * @param volume 音量 */ public void setMusicVolume(int volume) { //根据播放器ID获取前面创建的播放器 IMediaPlayer mediaPlayer = mRTCVideo.getMediaPlayer(0); mediaPlayer.setVolume(volume,AudioMixingType.AUDIO_MIXING_TYPE_PLAYOUT_AND_PUBLISH); } /** * 调节麦克风采集音量 * @param volume 麦克风采集音量 */ public void setRecordingVolume(int volume) { mRTCVideo.setCaptureVolume(StreamIndex.STREAM_INDEX_MAIN, volume); } /** * 设置混响特效类型 * @param reverbType 特效类型 */ public void setVoiceReverbType(VoiceReverbType reverbType) { mRTCVideo.setVoiceReverbType(reverbType); }
功能点 | API |
---|---|
创建 ByteRTCVideo 实例 | createRTCVideo |
设置音频场景类型 | setAudioScenario: |
设置音质档位 | setAudioProfile: |
开启本地音频采集 | startAudioCapture |
开启内部视频采集 | startVideoCapture |
创建 RTC 房间 | createRTCRoom |
设置用户可见性 | setUserVisibility |
加入RTC 房间 | joinRoom |
开始播放音频文件 | open |
设置混音时音频文件播放进度回调的间隔 | setProgressInterval |
发送音频流同步信息 | sendStreamSyncInfo |
订阅房间内指定的通过摄像头/麦克风采集的媒体流 | subscribeStream |
取消订阅房间内指定的通过摄像头/麦克风采集的媒体流 | unSubscribeStream |
启动 PCM 音频数据混音 | openWithCustomSource |
注册音频数据回调观察者。 | registerAudioFrameObserver |
开启音频回调 | enableAudioFrameCallback |
推送 PCM 音频帧数据用于混音 | pushExternalAudioFrame |
启用音频信息提示。 | enableAudioPropertiesReport |
设置当前音频文件的声道模式 | setAudioDualMonoMode |
暂停混音播放 | pause |
恢复混音播放 | resume |
打开/关闭耳返功能 | setEarMonitorMode |
设置耳返音量 | setEarMonitorVolume |
调节混音的音量大小,包括音频文件混音和 PCM 混音 | setVolume |
调节音频采集音量 | setCaptureVolume |
设置混响特效类型 | setVoiceReverbType |
离开 RTC 房间 | leaveRoom |
销毁 RTC 房间引擎 | destroy |
关闭内部音频采集 | stopAudioCapture |
销毁 RTCVideo 对象 | destroyRTCVideo |
功能点 | 回调 |
---|---|
本地用户加入 RTC 回调 | onRoomStateChanged |
音频流同步信息回调 | onStreamSyncInfoReceived |
返回远端单个用户的音频数据 | onRemoteUserAudioFrame |
本地音频的相关信息回调 | onLocalAudioPropertiesReport |
订阅的远端用户的音频信息回调 | onRemoteAudioPropertiesReport |
音频混音文件播放状态改变时回调 | onAudioMixingStateChanged |
音频播放路由变化回调 | onAudioRouteChanged |
混音音频文件播放进度回调 | onMediaPlayerPlayingProgress |
远端视频流的状态改变回调 | onRemoteVideoStateChanged |