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

WebRTC RTCPeerConnection ontrack事件监听器不触发问题

问题核心原因
  • 你在初始SDP(offer/answer)协商完成之后才调用addTrack添加视频轨道。WebRTC只会在协商过程中交换当前已存在的媒体轨道信息,协商完成后新增轨道不会自动同步媒体信息到对端,对端自然不会触发track事件。
  • 原有代码使用已废弃的回调式createOffer/createAnswer写法,存在时序漏洞:很可能SDP还没完成本地设置就被发送到对端,导致协商信息缺失。
  • RTCPeerConnection初始化时未配置ICE服务器,跨网络环境下会出现ICE连通失败,本地localhost测试虽然暂时能通信,但稳定性极差。
  • 答端接收到数据通道后未绑定消息、错误事件监听,会出现一端能发消息另一端收不到的问题。
修复方案
  1. 添加自动重协商逻辑
    在initialize函数中创建RTCPeerConnection实例后,添加onnegotiationneeded监听,只要媒体轨道发生变动就自动重新发起SDP协商,同时加协商锁避免两端同时发起offer导致的冲突:
    let isNegotiating = false;
    peerConnection.onnegotiationneeded = async () => {
      if (isNegotiating) return;
      isNegotiating = true;
      try {
        const offer = await peerConnection.createOffer();
        await peerConnection.setLocalDescription(offer);
        send({
          event: "offer",
          data: peerConnection.localDescription
        });
      } catch (err) {
        console.error("协商失败:", err);
      }
    }
    
  2. 替换废弃的回调写法,修复SDP时序问题
    将原有createOffer、handleOffer、handleAnswer三个方法全部替换为async/await写法,保证SDP设置完成后再发送:
    async function createOffer() {
      try {
        const offer = await peerConnection.createOffer();
        await peerConnection.setLocalDescription(offer);
        send({
          event: "offer",
          data: offer
        });
      } catch (error) {
        alert("创建offer失败");
        console.error(error);
      }
    }
    
    async function handleOffer(offer) {
      try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
        const answer = await peerConnection.createAnswer();
        await peerConnection.setLocalDescription(answer);
        send({
          event: "answer",
          data: answer
        });
        isNegotiating = false;
      } catch (error) {
        alert("处理offer失败");
        console.error(error);
      }
    }
    
    async function handleAnswer(answer) {
      try {
        await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
        console.log("连接建立成功");
        isNegotiating = false;
      } catch (error) {
        console.error("处理answer失败:", error);
      }
    }
    
  3. 补全ICE服务器配置
    把initialize函数里的configuration从null改成带公共STUN服务器的配置,保证ICE连通性:
    var configuration = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' }
      ]
    };
    
  4. 补全数据通道事件绑定
    修改peerConnection.ondatachannel回调,给答端接收到的数据通道绑定和发起端一致的事件监听:
    peerConnection.ondatachannel = function (event) {
      dataChannel = event.channel;
      dataChannel.onerror = function(error) {
        console.log("数据通道错误:", error);
      };
      dataChannel.onmessage = function(event) {
        console.log("收到消息:", event.data);
      };
      dataChannel.onclose = function() {
        console.log("数据通道已关闭");
      };
    };
    
验证说明

修改完成后,打开两个独立标签页建立连接,点击发送流按钮时,发送端会自动触发重协商,SDP交换完成后接收端就会触发track事件,正常渲染远端视频流。

测试注意:两个标签页不要通过浏览器的“复制标签页”功能打开,手动开两个空白页分别输入地址访问,避免标签页实例、WebSocket会话冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 00:47:06