同一个用户可以加入多个房间,分别订阅和接收这些房间中的音视频流,并在其中一个房间中发布音视频。也可以发送和接收实时消息。
大班小组课:主讲老师在大班房间内讲课,学生在该房间内听讲,同时可以在小组房间交流提问,并由助教老师答疑。
游戏场景:玩家可以在已加入的小队房间内和其他小队成员进行语音互动,同时能收听到世界房间内的广播音频。
会议场景:云端会议过程中,主持人可以发起分组讨论。参会者无需离开原先的会议,即可在各自小组中进行音视频互动,不同讨论组互不干扰。
你已经集成 RTC SDK v3.43 或更新的版本,实现了基本的音视频通话。
支持多房间功能的 SDK 详见API 及回调。
在集成 RTC SDK,并完成业务逻辑代码时,你已发现,音视频引擎类和房间类两个常用的主调类有明显的功能区分:
通过使用音视频引擎类的方法,你可以启动音视频采集,并进行相关设置(如切换摄像头等);
通过房间类的方法,你可以在房间内发布/订阅流,并进行相关设置(如用户自身在房间内的可见性等)。
要让一个用户加入多个房间,可以通过一个音视频引擎实例,创建多个房间实例,并为这些实例设置不同的房间 ID。同一个用户在多个房间中,可以订阅在这些房间中发布的音视频流。
本文以加入两个房间为例,你可以按照相同流程,让用户加入更多房间。
以下时序图以 Android SDK 中的 API 名称为例。不同端的 SDK 中 API 或回调名称可能略有不同,以 API 及回调为准。

创建和初始化一个音视频引擎类。
参考 构建 RTC 应用 获取详细步骤。
const list = [ { "lang": "java", "text": `// APP_ID: 已经在控制台申请的app_id // engineEventHandler: 用于接收 RTCEngine 回调的接口实例 EngineConfig config = new EngineConfig(); config.appID = appId; config.context = applicationContext; rtcEngine = RTCEngine.createRTCEngine(config, engineEventHandler);`, "selected": true, }, { "lang": "swift", "text": `// kAppID: 已经在控制台申请的app_id let engineCfg = ByteRTCEngineConfig.init() engineCfg.appID = appId //私有参数,可以填空 engineCfg.parameters = [:] //delegate: 用于接收 ByteRTCEngine 回调的接口实例 self.rtcEngine = ByteRTCEngine.createRTCEngine(engineCfg, delegate: self)`, }, { "lang": "cpp", "text": `// app_id: 已经在控制台申请的app_id // handler: 用于接收 RTCEngine 回调的接口实例 // parameters: 私有参数,可以填 nullptr bytertc::EngineConfig conf; conf.app_id = g_appid.c_str(); engine = bytertc::IRTCEngine::createRTCEngine(conf, handler.get()); }, ] return (<PreCodeTabs list={list} />);
创建音视频引擎类后,你可以启动音视频采集,并设置渲染视图。
// 开启音视频采集 rtcEngine.startAudioCapture(); rtcEngine.startVideoCapture(); // 设置本地渲染视图,支持 TextureView 和 SurfaceView private void setLocalRenderView() { VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = localView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; rtcEngine.setLocalVideoCanvas(videoCanvas); }
创建房间实例后,你可以加入房间发布和订阅音视频流。建议设置房间回调接口,以便监听房间和音视频流的状态回调。
继续调用 createRTCRoom 并传入不同房间 ID,以创建多个房间实例,并分别加入这些房间。
// 创建房间1 rtcRoom1 = rtcEngine.createRTCRoom(roomID); rtcRoom1.setRTCRoomEventHandler(firstRoomEventHandler); // 创建房间2 rtcRoom2 = rtcEngine.createRTCRoom(roomID); rtcRoom2.setRTCRoomEventHandler(secondRoomEventHandler);

// 用户信息 UserInfo userInfo = new UserInfo(localUid, ""); // 房间1配置 RTCRoomConfig room1Config = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_LIVE, true, true, true, true); // 加入房间 rtcRoom1.joinRoom(room1token, userInfo, true, room1Config); // 房间2配置 // 一个用户只能在一个房间中发布视频流。 RTCRoomConfig room2Config = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_LIVE, false, false, true, true); // 加入房间 rtcRoom2.joinRoom(room2token, userInfo, true, room2Config);
public void onRoomStateChanged(String roomId, String uid, int state, String extraInfo) { if (state == 0) { //进房成功 } else if (state == -1000) { //token错误 } else {// .... 其他错误 } }
在收到远端用户的 onUserPublishVideoStream 或 onFirstRemoteVideoFrameDecoded 回调后,你需要调用 setRemoteVideoCanvas 设置远端视图以在通话中查看远端视频。建议您在 onFirstRemoteVideoFrameDecoded 回调中设置视频渲染,确保视频渲染在视频解码完成后进行,以避免卡顿。
const list = [ { "lang": "java", "text": ` @Override public void onFirstRemoteVideoFrameDecoded(String streamId, StreamInfo streamInfo, VideoFrameInfo frameInfo) { runOnUiThread(() -> { // 设置远端视频渲染视图 VideoCanvas videoCanvas = new VideoCanvas(); videoCanvas.renderView = firstRoomRemoteView; videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN; rtcEngine.setRemoteVideoCanvas(streamId, videoCanvas); }); } @Override public void onUserPublishStreamVideo(String streamId, StreamInfo streamInfo, boolean isPublish) { if (!isPublish) { runOnUiThread(() -> { // 解除远端视频渲染视图绑定 rtcEngine.setRemoteVideoCanvas(streamId, null); }); } }`, "selected": true, }, { "lang": "swift", "text": `// 远端用户发布流 func rtcEngine(_ rtcEngine: ByteRTCEngine, onFirstRemoteVideoFrameDecoded streamId: String, info: ByteRTCStreamInfo, withFrameInfo: ByteRTCVideoFrameInfo) { // 渲染远端用户 DispatchQueue.main.async { // 设置远端用户视频渲染视图 let canvas = ByteRTCVideoCanvas.init() canvas.view = view.videoView canvas.renderMode = .hidden self.rtcEngine?.setRemoteVideoCanvas(streamId, withCanvas: canvas); } } func rtcRoom(_ rtcRoom: ByteRTCRoom, onUserPublishStreamVideo streamId: String, info: ByteRTCStreamInfo, isPublish: Bool) { if !isPublish { //移除 UI 显示 DispatchQueue.main.async { let canvas = ByteRTCVideoCanvas.init() canvas.view = nil // 置为空 canvas.renderMode = .hidden self.rtcEngine?.setRemoteVideoCanvas(streamId, withCanvas: canvas) } } }`, }, { "lang": "cpp", "text": `//远端用户发布流 void EngineHandler::onFirstRemoteVideoFrameDecoded(std::string streamId, bytertc::StreamInfo info, bytertc::VideoFrameInfo frameInfo) { bytertc::VideoCanvas canvas; canvas.view = (void*)ui->widget->winId(); engine->setRemoteVideoCanvas(streamId.c_str(), canvas); } void RoomHandler::onUserPublishStreamVideo(std::string streamId, bytertc::StreamInfo info, bool publish) if (!publish) { //远端用户取消发布流 bytertc::VideoCanvas cas; cas.view = nullptr; engine->setRemoteVideoCanvas(streamId.c_str(), cas); } } `, ] return (<PreCodeTabs list={list} />);
用户结束通话时,退出各个房间,并销毁本地房间实例。
rtcRoom.leaveRoom(); rtcRoom.destroy(); rtcRoom = null;
在销毁所有房间实例后可销毁音视频引擎。
RTCEngine.destroyRTCEngine();
参考以下项目获取完整代码。在示例项目中,本端用户加入了两个不同的 RTC 房间。你可以按照相同流程,让用户加入更多房间。
你可以根据上文的描述和示例,使用以下客户端 SDK,在不同的端上实现同一用户加入多房间功能。
说明:表格中的 macOS API 接口为 Objective-C,而示例项目中的 macOS 项目使用的是 Windows SDK 中的 API 接口。
A:对于一个音视频引擎实例,即使加入了多个房间,也仅能同时在其中的一个房间中发布音视频流。如果需要在多个房间中同时发布音视频流,参看 跨房间转发媒体流 。
A:确保每个房间的 token 使用了对应的 roomId 生成。更多 Token 相关问题,参见 Token 使用常见问题