跨房间转发媒体流,指可见用户的媒体流可以同时转发到多个 RTC 房间。转发目标房间的数量在 RTC 侧没有限制。
使用转发媒体流功能并不会产生额外的费用,与其他音视频通话遵循相同的计费原则,详见音视频通话计费 。
本功能适用于以下场景,观众在原来的房间中就能够接收到来自其他房间主播的媒体流。
主播连麦 PK:接受连麦邀请后,主播 A 的音视频流可以转发到主播 B 所在的房间,同时,B 的音视频流也可以转发到 A 房间。观众和主播均无需离开原有房间,即可观赏到两位主播连麦 PK 的音视频内容。
子母直播间:母直播间的音视频为集团总部统一制作,可以同时转发到多个子直播间。子直播间的主播们直接转播母直播间内容,边与用户进行互动,也可以合入自己的个性化内容后进行直播。
你已经集成 RTC SDK,实现了基本的音视频通话。
支持跨房间转发功能的 SDK 详见API 及回调。
转推流:跨房间转发的媒体流
目标房间:转推流到达的房间
以下时序图以 Android SDK 中的 API 名称为例。不同端的 SDK 中 API 或回调名称可能略有不同,以 API 及回调为准。

创建引擎实例并加入房间,本地用户默认对他人可见。
const list = [ { "lang": "typescript", "text": `import VERTC, { ForwardStreamState } from '@volcengine/rtc'; // 创建引擎实例 const engine = VERTC.createEngine('appid'); // 加入房间 await engine.joinRoom('roomId_token', 'roomId', { userId: 'userid' });`, "selected": true, }, { "lang": "java", "text": `// 创建引擎 EngineConfig config = new EngineConfig(); config.appID = appId; config.context = applicationContext; rtcEngine = RTCEngine.createRTCEngine(config, engineEventHandler);`, // 加入房间 private void joinRoom(String roomId) { rtcRoom = rtcEngine.createRTCRoom(roomId); rtcRoom.setRTCRoomEventHandler(roomEventHandler); String token = requestRoomToken(roomId); // 用户信息 UserInfo userInfo = new UserInfo(localUid, ""); // 设置房间配置 boolean isAutoPublishAudio = true; boolean isAutoPublishVideo = true; boolean isAutoSubscribeAudio = true; boolean isAutoSubscribeVideo = true; RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_LIVE, isAutoPublishAudio, isAutoPublishVideo, isAutoSubscribeAudio, isAutoSubscribeVideo); // 加入房间 rtcRoom.joinRoom(token, userInfo, true, roomConfig); }`, }, { "lang": "swift", "text": `//创建引擎 let engineCfg = ByteRTCEngineConfig.init() engineCfg.appID = appId //私有参数,可以填空 engineCfg.parameters = [:] //delegate: 用于接收 ByteRTCEngine 回调的接口实例 self.rtcEngine = ByteRTCEngine.createRTCEngine(engineCfg, delegate: self)`, //创建房间 self.rtcRoom = self.rtcEngine?.createRTCRoom(roomId) self.rtcRoom?.delegate = self //加入房间 let userInfo = ByteRTCUserInfo.init() userInfo.userId = userId let roomCfg = ByteRTCRoomConfig.init() roomCfg.isPublishAudio = true roomCfg.isPublishVideo = true roomCfg.isAutoSubscribeAudio = true roomCfg.isAutoSubscribeVideo = true self.rtcRoom1?.joinRoom(token, userInfo: userInfo, userVisibility: true, roomConfig: roomCfg)`, }, { "lang": "cpp", "text": `//创建引擎 bytertc::EngineConfig conf; conf.app_id = g_appid.c_str(); engine = bytertc::IRTCEngine::createRTCEngine(conf, handler.get()); engine->startAudioCapture(); engine->startVideoCapture(); //创建房间 bytertc::IRTCRoom *room = engine->createRTCRoom(roomid); room->setRTCRoomEventHandler(room_handler); bytertc::UserInfo info; bytertc::RTCRoomConfig config; info.uid = str_uid.c_str(); config.is_publish_audio = true; config.is_publish_video = true; config.is_auto_subscribe_audio = true; config.is_auto_subscribe_video = true; int ret = room->joinRoom(token, userinfo, true, config);`, }, ] return (<PreCodeTabs list={list} />);
参考 构建 RTC 应用 获取详细步骤。
startForwardStreamToRooms 中所传入的 token 是与目标房间相对应的 token,而非当前用户所在房间的 token。stopForwardStreamToRooms 前,仅可调用一次 startForwardStreamToRooms。// 管理目标房间列表 const targetRoomList: string[] = []; // 开启跨房间转发媒体流 const startResult = await engine.startForwardStreamToRooms([ { roomId: 'targetRoomIdA', token: 'targetRoomIdA_token' }, // 可以同时指定多个目标房间 { roomId: 'targetRoomIdB', token: 'targetRoomIdB_token' } ]); // 处理调用结果 startResult.forEach(item => { if (item.state === ForwardStreamState.FORWARD_STREAM_STATE_SUCCESS) { // 保存成功的目标房间 targetRoomList.push(item.roomId); } else { // 根据错误码进行对应处理 console.error(item.error); } });
更新跨房间媒体流转发请求。
如果调用 updateForwardStreamToRooms 时不传入任何参数或者传空值,会停止所有正在转发的目标房间流。此时仍需要调用 stopForwardStreamToRooms 后才可以再次调用 startForwardStreamToRooms 。
// 更新跨房间转发媒体流的目标房间列表 const updateResult = await engine.updateForwardStreamToRooms([ // 列表中不包含 targetRoomIdA,会停止该房间的转发媒体流 // targetRoomIdB 在之前的目标房间列表中,会更新对应的 token。 // 在暂停状态下调用更新无法对 token 进行校验。在调用恢复 resumeForwardStreamToAllRooms 后才会进行 token 校验。 { roomId: 'targetRoomIdB', token: 'targetRoomIdB_new_token' }, // targetRoomIdC 不在之前的目标房间列表中,会开启向该房间转发媒体流 { roomId: 'targetRoomIdC', token: 'targetRoomIdC_token' } ]); // 处理调用结果 // 停止转发的目标房间也会在 updateForwardStreamToRooms 的返回结果中。 updateResult.forEach(item => { if (item.state === ForwardStreamState.FORWARD_STREAM_STATE_SUCCESS) { if (!['targetRoomIdB', 'targetRoomIdC'].includes(item.roomId)) { // 不在请求列表中,但在返回结果中的应当删除 targetRoomList.splice(targetRoomList.indexOf(item.roomId), 1); } else if (!targetRoomList.includes(item.roomId)) { // 不在已有房间中,需要新增 targetRoomList.push(item.roomId); } } else { // 根据错误码进行对应处理 console.error(item.error); } });
暂停与恢复向所有目标房间转发媒体流。
在调用 resumeForwardStreamToAllRooms 恢复媒体流转发前,仅可调用一次 pauseForwardStreamToAllRooms 。
// 暂停跨房间转发媒体流的目标房间列表 await engine.pauseForwardStreamToAllRooms(); // 恢复跨房间转发媒体流的目标房间列表 const result = await engine.resumeForwardStreamToAllRooms(); // 处理调用结果 result.forEach(item => { if (item.state !== ForwardStreamState.FORWARD_STREAM_STATE_SUCCESS) { // 根据错误码进行对应处理 console.error(item.error); } });
停止向所有目标房间转发媒体流。
await engine.stopForwardStreamToRooms();
监听 onForwardStreamStateChanged,感知跨房间转发媒体流状态。
onUserJoin 、 onUserLeave 、 onUserPublishStream / onUserPublishScreen 、 onUserUnpublishStream / onUserUnpublishScreen。注: 方法
onUserPublishStream已被拆分为onUserPublishStreamAudio和onUserPublishStreamVideo,两个新方法都增加了(BOOL)isPublish参数用于控制是 publish 流还是 unpublish 流。方法onUserUnpublishStream和onUserUnpublishScreen已被废弃。在RTCRoom业务场景下,方法onUserPublishScreen已被废弃。
function onForwardError(info) { console.error(info); } engine.on(EngineEventsTypes.onForwardStreamError, onForwardError);
以下客户端 SDK 均支持跨房间转发媒体流功能。你可以根据上文中的描述和实例,使用不同的 SDK,在不同的端上实现这个功能。
说明:表格中的 macOS API 接口为 Objective-C,而示例项目中的 macOS 项目使用的是 Windows SDK 中的 API 接口。
自 Native SDK v3.34 起,本功能在 Native 端可用。
自 Web SDK v4.54 起,本功能在 Web 端可用。