You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 14:54:19