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

PeerJS call.on('stream')事件不触发但peer.on('call')正常的问题

问题根源与优雅解决方案

核心问题分析

你遇到的call.on("stream")不触发问题,本质是两个关键错误导致的:

  1. 空本地流无效:最初代码里window.localStream是无轨道的空MediaStream,PeerJS需要有效媒体流(带音频/视频轨道)才能完成完整的媒体协商流程,空流会导致后续stream事件逻辑无法触发。
  2. 异步操作时序混乱:你提前创建了peer.connect()数据连接,又在其open回调里同步发起呼叫,但获取真实本地流的getLocalStream()是异步操作,没等它完成就发起了呼叫;同时冗余的数据连接会干扰PeerJS的媒体信令时序,这也是setTimeout能临时凑效的原因——它给了异步操作和信令流程额外的缓冲时间。

优雅解决方案

1. 封装异步获取本地流的函数

确保获取媒体流的操作返回Promise,等待用户授权并拿到有效流后再执行后续逻辑:

async function getLocalStream() {
  try {
    // 请求麦克风权限,获取有效音频流
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    window.localStream = stream;
    return stream;
  } catch (err) {
    console.error("获取本地麦克风流失败:", err);
    throw err;
  }
}

2. 重构发起呼叫代码

去掉冗余的peer.connect(),直接在拿到本地流后发起媒体呼叫:

if (peerid !== -1 && peer) {
  try {
    // 先确保拿到有效本地流
    const localStream = await getLocalStream();
    // 直接发起媒体呼叫,PeerJS会自动处理连接
    const call = peer.call(peerid, localStream);
    
    console.log("正在连接到:", peerid);

    call.on("stream", (remoteStream) => {
      console.log("获取到对方媒体流");
      window.remoteAudio.srcObject = remoteStream;
      window.remoteAudio.autoplay = true;
      window.peerStream = remoteStream;
      addMessage(11);
    });

    call.on("error", (err) => {
      console.error("呼叫错误:", err);
    });

    // 若需传输额外文本数据,可单独创建数据连接(非必须)
    // const dataConn = peer.connect(peerid);
    // dataConn.on("open", () => dataConn.send("呼叫已发起"));
  } catch (err) {
    console.error("发起呼叫失败:", err);
    addMessage(12);
  }
}

3. 重构接听呼叫代码

确保接听时本地流已准备就绪,避免用空流应答:

peer.on('call', async (incomingCall) => {
  console.log("接听来自", incomingCall.peer, "的呼叫");
  try {
    // 等待本地流获取完成再应答
    const localStream = await getLocalStream();
    incomingCall.answer(localStream);

    incomingCall.on("stream", (remoteStream) => {
      console.log("获取到对方媒体流(接听方)");
      window.remoteAudio.srcObject = remoteStream;
      window.remoteAudio.autoplay = true;
      window.peerStream = remoteStream;
    });

    incomingCall.on("error", (err) => {
      console.error("接听错误:", err);
    });
  } catch (err) {
    console.error("接听失败:", err);
    // 无法获取本地流时,主动关闭呼叫
    incomingCall.close();
  }
});

// 单独处理数据连接(若需)
peer.on('connection', (connection) => {
  window.peerConn = connection;
  connection.on("open", () => {
    console.log("数据连接已建立");
  });
});

为什么setTimeout能临时解决?

你的临时方案里,定时器给了getLocalStream()足够时间完成异步授权和流获取,同时让冗余的数据连接有时间建立,避免了时序冲突,但这是治标不治本的方法——一旦网络延迟或设备响应慢,问题会再次出现。

内容的提问来源于stack exchange,提问作者Ivan Griga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:46:07