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

PeerJS流重复触发问题:屏幕共享合并流致监听器多次执行求助

解决MediaStream合并后stream监听器重复触发的问题

问题原因

你遇到的核心问题是:合并屏幕捕获流与麦克风流后,call.on('stream')监听器被多次触发,导致addAudioStream重复执行。主要有两个诱因:

  1. 每次启动屏幕共享时,调用callAllConnectedPeers对所有已连接Peer重复发起呼叫,每个新呼叫都会触发一次stream事件,叠加导致重复执行。
  2. 旧版PeerJS存在多轨道流触发多次stream事件的bug,当合并后的流包含多个轨道(比如1个视频+2个音频)时,监听器会为每个轨道触发一次回调。

解决方案

1. 避免重复发起屏幕共享呼叫

维护一个已发起屏幕共享呼叫的Peer列表,确保每个Peer只被呼叫一次:

// 新增私有变量记录已发起屏幕共享的Peer ID
private screenShareCalledPeers: string[] = [];

callAllConnectedPeers() {
  this.connectedPeers.forEach(peerId => {
    // 仅对未发起过屏幕共享呼叫的Peer发起新请求
    if (!this.screenShareCalledPeers.includes(peerId)) {
      this.callUser(peerId, this.currentUser?.username || '');
      this.screenShareCalledPeers.push(peerId);
    }
  });
}

// 在停止屏幕共享时清空列表,以便下次重新发起
stopScreenShare() {
  this.screenShareCalledPeers = [];
  // 其他停止逻辑(比如关闭轨道、更新状态等)...
}

2. 对stream事件做去重处理

在处理 incoming stream 时,检查该Peer的流是否已经被处理过,避免重复调用addAudioStream:

// 新增私有Set存储已处理过的Peer ID
private processedPeerStreams = new Set<string>();

// 在initMedia的call监听回调中修改stream事件处理
this.peer.on('call', (call) => {
  console.log('📞 来电:', call);
  if (!this.connectedPeers.includes(call.peer)) {
    this.connectedPeers.push(call.peer);
  }

  const callerUsername = call.metadata?.currentUser || 'Unknown User'; 
  call.answer(this.myStream);

  call.on('stream', (userStream) => {
    console.log('Incoming stream tracks:', userStream.getTracks().length);
    // 检查该Peer是否已处理过流,未处理则执行添加逻辑
    if (!this.processedPeerStreams.has(call.peer)) {
      this.addAudioStream(userStream, call.peer, callerUsername);
      this.processedPeerStreams.add(call.peer);
    }
  });

  call.on('close', () => {
    console.warn('📴 Call closed');
    // 呼叫关闭时移除记录,释放资源
    this.processedPeerStreams.delete(call.peer);
  });

  call.on('error', (err) => {
    console.error('❌ Call error:', err);
  });
});

3. 优化流合并逻辑

如果不需要同时传输系统音频(屏幕共享的音频)和麦克风音频,可简化流合并,减少轨道数量:

async startScreenShare(): Promise<MediaStream> {
  const screenStream = await navigator.mediaDevices.getDisplayMedia({ 
    video: true, 
    audio: false // 关闭屏幕共享的音频,只保留视频
  });
  const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });

  const combinedStream = new MediaStream([
    ...screenStream.getVideoTracks(),
    ...audioStream.getAudioTracks(),
  ]);

  // 后续逻辑不变...
}

4. 升级PeerJS版本

确认你使用的是最新版PeerJS,旧版本存在多轨道流触发多次stream事件的已知问题,升级到最新版本可直接修复此类bug。


内容的提问来源于stack exchange,提问作者Alper Uluses

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:13:18