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

WebRTC Transceiver异常:recvonly Transceiver意外创建

WebRTC Transceiver 重复创建问题分析与解决

问题现象

尝试通过Transceiver在两个WebRTC对等端间建立音频流,预期两端各存在一个sendrecv方向的Transceiver,但实际remoteConnection中出现两个Transceiver:

  • 一个mid为null的sendrecv Transceiver
  • 协商过程中自动生成的recvonly Transceiver

问题原因

1. 冗余协商操作打乱流程

代码中提前执行了remoteConnection.createOffer()并设置本地描述,完全违背WebRTC标准协商流程。正常流程应由一端发起offer,另一端回应answer,提前在remote端创建offer会让其处于异常会话状态,后续处理local端发来的offer时,无法正确匹配已手动创建的Transceiver,导致WebRTC自动生成新的recvonly Transceiver适配远端媒体流。

2. 错误的Track关联逻辑

remote端的Transceiver调用replaceTrack时传入了localStream的音频轨道,逻辑上属于交叉绑定错误(remote端应使用自身媒体轨道),虽不是直接导致多Transceiver的原因,但会干扰会话状态的正常同步。

解决方案

1. 修复协商流程

移除remote端提前创建offer的冗余代码,严格遵循local发起offer → remote接收并生成answer → local接收answer的标准流程。

2. 正确配置Transceiver与Track

确保两端的Transceiver绑定自身的媒体轨道,避免交叉绑定错误。

修正后的完整代码

<!DOCTYPE html>
<html>
  <body>
    <h2>WebRTC with Transceivers on Both Peers</h2>
    <button onclick="startConnection()">Start Connection</button>

    <script>
      let localConnection;
      let remoteConnection;
      let localStream;
      let remoteStream;

      async function startConnection() {
        // 获取本地音频流
        localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
        // 为remote端模拟本地流(测试用,实际场景从remote端获取)
        const remoteMockStream = await navigator.mediaDevices.getUserMedia({ audio: true });

        // 创建对等连接
        localConnection = new RTCPeerConnection();
        remoteConnection = new RTCPeerConnection();

        // ICE候选处理
        localConnection.onicecandidate = (e) => {
          if (e.candidate) remoteConnection.addIceCandidate(e.candidate);
        };
        remoteConnection.onicecandidate = (e) => {
          if (e.candidate) localConnection.addIceCandidate(e.candidate);
        };

        // 配置local端Transceiver
        const localTransceiver = localConnection.addTransceiver('audio', {
          direction: 'sendrecv',
        });
        localTransceiver.sender.replaceTrack(localStream.getAudioTracks()[0]);

        // 配置remote端Transceiver
        const remoteTransceiver = remoteConnection.addTransceiver('audio', { direction: 'sendrecv' });
        remoteTransceiver.sender.replaceTrack(remoteMockStream.getAudioTracks()[0]);

        // 处理remote端收到的轨道
        remoteStream = new MediaStream();
        remoteConnection.ontrack = (event) => {
          remoteStream.addTrack(event.track);
          console.log('Received remote audio track');
          const remoteAudio = document.createElement('audio');
          remoteAudio.srcObject = remoteStream;
          remoteAudio.autoplay = true;
          document.body.appendChild(remoteAudio);
        };

        // 标准协商流程
        const offer = await localConnection.createOffer();
        await localConnection.setLocalDescription(offer);
        await remoteConnection.setRemoteDescription(offer);

        const answer = await remoteConnection.createAnswer();
        await remoteConnection.setLocalDescription(answer);
        await localConnection.setRemoteDescription(answer);
         
        console.log('local Transceivers:', localConnection.getTransceivers())
        console.log('remote Transceivers:', remoteConnection.getTransceivers())

        console.log('WebRTC connection established with transceivers on both sides.');
      }
    </script>
  </body>
</html>

验证结果

修正后,两端的getTransceivers()都会返回一个sendrecv方向的Transceiver,mid正常赋值,不会出现额外的自动创建Transceiver。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:45:11