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

WebRTC(peerjs)通话中途新增媒体流(非替换)实现方法

PeerJS 音视频通话初始无视频轨时中途新增视频流实现方案

问题根因

你之前直接调用remotePeerConnection.addTrack()失效的核心原因有三点:

  • 调用原生addTrack时没有传入关联的MediaStream对象,对端收到新轨道后无法将其归类到已有的通话流中
  • 仅调用addTrack不会自动触发WebRTC的连接重协商流程,新添加的轨道不会被编码传输到对端
  • PeerJS默认的stream事件仅在通话首次建立时触发,后续新增轨道不会自动触发该事件,需要手动监听track事件处理新轨道

正确实现步骤

1. 调整全局变量存储,保留PeerJS call实例引用

之前的逻辑仅存储了原生RTCPeerConnection实例,需要额外存储PeerJS返回的call对象,方便后续监听轨道事件、处理协商流程:

const peer = new Peer()
// 新增存储PeerJS call实例的变量
let currentCall = null
let remotePeerConnection = null
let audioStream;
let videoStream;
let mixedStream = new MediaStream()

// 初始获取音频流的逻辑保持不变,初始仅音频通话时不需要提前获取视频流
try{
    audioStream = await navigator.mediaDevices.getUserMedia({audio:true})
    if(audioStream) mixedStream.addTrack(audioStream.getAudioTracks()[0])
}
catch(err){
    audioStream = null;
}

// 本地预览逻辑保持不变
const myVideo = document.getElementById("my-video")
myVideo.srcObject = mixedStream
myVideo.play()

2. 修改通话绑定逻辑,监听中途新增的轨道事件

不管是主叫发起通话还是被叫接听通话,都需要绑定track事件,用来接收通话过程中对端新增的媒体轨道:

// 主叫发起通话方法
const call = (peerID) => {
    currentCall = peer.call(peerID,mixedStream)
    remotePeerConnection = currentCall.peerConnection
    bindCallEvents(currentCall)
}

// 被叫接听逻辑
peer.on('call', (incomingCall) => {
    currentCall = incomingCall
    remotePeerConnection = currentCall.peerConnection
    // 应答时传入本地初始mixedStream
    currentCall.answer(mixedStream)
    bindCallEvents(currentCall)
})

// 抽离公共的事件绑定逻辑
const bindCallEvents = (callInstance) => {
    // 保留初始流接收逻辑
    callInstance.on("stream",remoteStream=>{
        const remoteVideo = document.getElementById("remote-video")
        remoteVideo.srcObject = remoteStream
        remoteVideo.play()
    })
    // 新增:监听通话过程中新增的轨道,手动追加到已有的远端流中
    callInstance.on("track", (track, remoteStream) => {
        remoteStream.addTrack(track)
    })
}

3. 实现中途新增视频轨逻辑

新增视频轨时需要完成本地流更新、加轨、重协商三个步骤,全程不需要挂断重建通话:

const addVideoTrackMidCall = async () => {
    if(!currentCall || !remotePeerConnection) return

    try{
        // 开摄像头用getUserMedia,共享屏幕替换为getDisplayMedia即可
        videoStream = await navigator.mediaDevices.getUserMedia({video:true})
        // videoStream = await navigator.mediaDevices.getDisplayMedia({video:true})
    }
    catch(err){
        console.error('获取视频流失败:', err)
        return
    }

    const videoTrack = videoStream.getVideoTracks()[0]
    // 将新视频轨加入本地混合流,同步本地预览
    mixedStream.addTrack(videoTrack)
    // 加轨时必须传入关联的mixedStream,否则对端无法正确归类轨道
    remotePeerConnection.addTrack(videoTrack, mixedStream)

    // 手动触发重协商,将新的媒体能力同步给对端,PeerJS会自动转发协商信令
    try {
        const offer = await remotePeerConnection.createOffer()
        await remotePeerConnection.setLocalDescription(offer)
    } catch (err) {
        console.error('重协商失败:', err)
    }

    // 监听轨道结束事件(比如用户停止共享屏幕),自动清理视频轨
    videoTrack.onended = () => {
        removeVideoTrackMidCall()
    }
}

4. 配套视频轨移除逻辑(可选)

如果需要支持中途关闭视频/停止共享,可使用以下逻辑,同样不需要挂断通话:

const removeVideoTrackMidCall = () => {
    if(!currentCall || !remotePeerConnection) return
    const videoSender = remotePeerConnection.getSenders().find(s => s.track?.kind === 'video')
    if(videoSender){
        remotePeerConnection.removeTrack(videoSender)
        // 清理本地视频轨
        const localVideoTrack = mixedStream.getVideoTracks()[0]
        if(localVideoTrack){
            localVideoTrack.stop()
            mixedStream.removeTrack(localVideoTrack)
        }
        // 触发重协商同步状态
        remotePeerConnection.createOffer()
            .then(offer => remotePeerConnection.setLocalDescription(offer))
            .catch(err => console.error('重协商失败:', err))
    }
    if(videoStream){
        videoStream.getTracks().forEach(t => t.stop())
        videoStream = null
    }
}

注意事项

  • 主叫、被叫两端都必须绑定track事件,否则无法接收对方中途新增的轨道
  • addTrack方法的第二个参数必须传入当前通话使用的mixedStream,不能省略
  • 不需要手动编写重协商的信令转发逻辑,PeerJS内部已经封装了offer/answer的自动转发能力,仅需在本地修改轨道后调用setLocalDescription即可触发协商流程

内容的提问来源于stack exchange,提问作者Ahmed Ghazi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:31:15