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

如何通过单个RTCPeerConnection传输多路WebRTC视频流?

单个RTCPeerConnection传输多路视频流的实现方案

要在单个RTCPeerConnection里传输多路视频流,核心要解决两个问题:放弃Plan-B,切换到Unified Plan SDP语义,以及正确处理多路Track/Stream的接收逻辑。结合你的现有代码,修改步骤如下:

1. 切换到Unified Plan

Plan-B是旧的SDP格式,对多路媒体流的支持有限,Unified Plan是WebRTC当前的标准,原生支持多路流传输。修改RTCPeerConnection的初始化配置:

var webrtc_con = new RTCPeerConnection({
    iceServers: [{
        urls: ["stun:stun.l.google.com:19302"]
    }],
    sdpSemantics: 'unified-plan' // 替换为标准的Unified Plan
})

2. 配置多路视频Transceiver

如果提前知晓需要接收的视频路数,可以主动添加对应数量的视频Transceiver;如果是服务器动态推送多路流,也可以省略主动添加,让WebRTC自动协商。这里以主动添加2路为例:

// 添加两个视频transceiver,方向设为recvonly(仅接收,无需发送本地流)
webrtc_con.addTransceiver('video', {direction: 'recvonly'})
webrtc_con.addTransceiver('video', {direction: 'recvonly'})

3. 修改ontrack事件处理逻辑

原来的代码直接将第一个流赋值给单个video元素,多路流场景下需要为每路流分配独立的播放载体。你可以提前在页面准备多个video标签,或者动态创建:

// 用Map存储流ID与对应video元素的映射
const streamVideoMap = new Map();

webrtc_con.ontrack = function (event) {
    const stream = event.streams[0];
    let videoEl;
    
    // 检查该流是否已有对应的video元素
    if (!streamVideoMap.has(stream.id)) {
        // 动态创建video元素
        videoEl = document.createElement('video');
        videoEl.style.width = '320px';
        videoEl.style.height = '240px';
        videoEl.autoplay = true;
        videoEl.muted = true; // 规避浏览器自动播放限制
        document.body.appendChild(videoEl);
        streamVideoMap.set(stream.id, videoEl);
    } else {
        videoEl = streamVideoMap.get(stream.id);
    }
    
    // 将流绑定到对应的video元素
    videoEl.srcObject = stream;
}

4. 确保服务器端适配

你的DeepEch rtsotowebrtc服务器需要配置为支持Unified Plan,并且允许在单个PeerConnection会话中发送多路视频流。如果服务器默认使用Plan-B,需要调整配置切换到Unified Plan。

修改后的完整代码示例

// 初始化PeerConnection,使用Unified Plan语义
var webrtc_con = new RTCPeerConnection({
    iceServers: [{
        urls: ["stun:stun.l.google.com:19302"]
    }],
    sdpSemantics: 'unified-plan'
})

// 存储流与视频元素的映射关系
const streamVideoMap = new Map();

webrtc_con.ontrack = function (event) {
    const stream = event.streams[0];
    let videoEl;
    
    if (!streamVideoMap.has(stream.id)) {
        videoEl = document.createElement('video');
        videoEl.style.width = '320px';
        videoEl.style.height = '240px';
        videoEl.autoplay = true;
        videoEl.muted = true;
        document.body.appendChild(videoEl);
        streamVideoMap.set(stream.id, videoEl);
    } else {
        videoEl = streamVideoMap.get(stream.id);
    }
    
    videoEl.srcObject = stream;
}

// 添加两个视频接收transceiver
webrtc_con.addTransceiver('video', {direction: 'recvonly'})
webrtc_con.addTransceiver('video', {direction: 'recvonly'})

webrtc_con.onnegotiationneeded = async function handleNegotiationNeeded() {
    try {
        const offer = await webrtc_con.createOffer()
        await webrtc_con.setLocalDescription(offer)

        const response = await fetch(url, {
            method: 'POST',
            body: new URLSearchParams({data: btoa(webrtc_con.localDescription.sdp)})
        })
        const data = await response.text()
        
        await webrtc_con.setRemoteDescription(
            new RTCSessionDescription({type: 'answer', sdp: atob(data)})
        )
    } catch (e) {
        console.warn('协商失败:', e)
        if (e.message.includes('NotFound')) {
            console.warn(`Stream ${url} Not Found!`)
        } else {
            console.warn('Server maybe offline?')
        }
    }
}

内容的提问来源于stack exchange,提问作者Milan Bültmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 04:32:47