WebRTC是否支持同时调用多摄像头向不同用户分别推流
WebRTC双摄像头定向推流实现方案
结论:WebRTC原生完全支持该需求,无需依赖第三方服务即可实现。
核心实现逻辑
- 双路流采集:通过两次调用
getUserMedia,分别传入前置、后置摄像头的设备ID,获取两路独立的媒体流,音频轨只保留前置流的一路即可,避免双音频回声。 - 定向推流:现有为每个参会用户单独创建
RTCPeerConnection的架构天然适配该需求,针对不同连接对象添加不同的媒体轨即可:- 面向普通学生的连接,仅添加前置摄像头的音视频轨,这类用户只能收到讲师人脸画面
- 面向指定学生的连接,添加前置音频轨+后置摄像头视频轨,若需要该学生同时看到两路画面,可同时添加前置视频轨
- 多流渲染适配:原代码默认仅读取
e.streams[0],无法处理单连接多路流的场景,需要给每个流绑定唯一标识,避免重复渲染、错绑画面。 - 动态调整支持:如果后续需要中途更换接收后置流的用户,不需要重建连接,直接调用
RTCPeerConnection实例的addTrack/removeTrack接口增删对应轨道即可,新的媒体轨会自动通过协商同步到对端。
改造后核心代码
首先是双摄像头初始化逻辑:
// 存储两路摄像头采集流 let frontStream = null; let backStream = null; // 配置需要接收后置摄像头流的目标学生ID const TARGET_STUDENT_ID = "替换为实际指定学生的userId"; // 初始化双摄像头采集 async function initDualCamera() { // 枚举设备获取前后置摄像头ID const allDevices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = allDevices.filter(item => item.kind === "videoinput"); // 常规设备排序中索引0为前置、1为后置,可根据设备label做精准匹配 const frontCam = videoDevices[0]; const backCam = videoDevices[1]; // 采集前置流:带音频+视频 frontStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: { deviceId: frontCam.deviceId, width: 1280, height: 720 } }); // 采集后置流:仅采集视频,音频复用前置流音轨避免回声 backStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { deviceId: backCam.deviceId, width: 1920, height: 1080 } }); }
改造原有Peer连接创建逻辑:
var all_videotab = []; function createPeer(userIdToCall) { const peer = new RTCPeerConnection({ iceServers: [ { urls: [ "******", ], username: "****", credential: "*****" }, { urls: [ "******" ] } ] }); peer.onnegotiationneeded = () => userIdToCall ? handleNegotiationNeededEvent(peer, userIdToCall) : null; peer.onicecandidate = handleICECandidateEvent; // 根据对端用户身份添加对应媒体轨 if (userIdToCall === TARGET_STUDENT_ID) { // 目标学生:推送前置音频+后置视频 frontStream.getAudioTracks().forEach(track => peer.addTrack(track, frontStream)); backStream.getVideoTracks().forEach(track => peer.addTrack(track, backStream)); // 如需该学生同时看到前置画面,放开下一行注释即可 // frontStream.getVideoTracks().forEach(track => peer.addTrack(track, frontStream)); } else { // 普通学生:仅推送前置音视频 frontStream.getTracks().forEach(track => peer.addTrack(track, frontStream)); } // 改造远端流渲染逻辑,支持多路流识别 peer.ontrack = (e) => { const streamUniqKey = `${userIdToCall}-${e.streams[0].id}`; if(!all_videotab.includes(streamUniqKey)) { const container = document.createElement('div'); container.classList.add('remote-video-container'); const video = document.createElement('video'); video.srcObject = e.streams[0]; video.autoplay = true; video.playsInline = true; video.classList.add("remote-video"); // 标记流类型,方便后续布局调整 video.dataset.camType = e.streams[0].id === frontStream.id ? 'front' : 'back'; container.appendChild(video); if (isAdmin) { const button = document.createElement("button"); button.innerHTML = `Hide user's cam`; button.classList.add('button'); button.setAttribute('user-id', userIdToCall); container.appendChild(button); } all_videotab.push(streamUniqKey); container.id = streamUniqKey; remoteVideoContainer.appendChild(container); } } return peer; }
备选方案
如果不想自行处理设备兼容、权限管控、流协商等底层逻辑,也可以使用成熟的商用实时音视频SDK,这类SDK通常内置多流发布、按角色订阅/推送的能力,可减少开发适配成本。
内容的提问来源于stack exchange,提问作者B.Nadesh kumar
相关产品推荐
相关产品推荐

