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

WebRTC单向通信异常:SDP传递后接收端无画面且报错

问题描述

我正在实现WebRTC单向通信,通过将发送方(Offer端)的SDP存入数据库,供接收方(Answer端)获取并建立连接。

Offer端代码如下:

const videoConstraints = {
  'video': true,
  'audio': false
}

var localStream = new MediaStream();

try {
  navigator.mediaDevices.getUserMedia(videoConstraints).then((stream) => {
    localStream = stream;
    video = document.getElementById("video");
    video.srcObject = stream;
  })
} catch (error) {
  console.log(error);
}

const turnConfig = {
  iceServers: [
//servers
  ]
}

const createOfferer = () => {
  var peer = new RTCPeerConnection(turnConfig);

  localStream.getTracks().forEach(track => {
    peer.addTrack(track, localStream);
  });

  peer.createOffer()
    .then((offer) => {
      fetch(window.location.href, {
        method: "POST",
        body: JSON.stringify(offer),
        headers: {
          "Content-Type": "application/json"
        }
      });
    })  
}

接收端获取SDP后调用createAnswererForSeat方法,代码如下:

const setOnTrack = (peer, remoteVideo) => {
  var remoteStream = new MediaStream();

  remoteVideo.srcObject = remoteStream;

  peer.addEventListener('track', async (e) => {
    remoteStream.addTrack(e.track, remoteStream);
  })
}

const createAnswererForSeat = (offer) => {
  var peer = new RTCPeerConnection(turnConfig);

  var remoteVideo = createVideoForSeat(); //this function only creates the video element
  setOnTrack(peer, remoteVideo);

  peer.setRemoteDescription(offer)
    .then(() => {
      return peer.createAnswer();
    })
    .then((answer) => {
      peer.setLocalDescription(answer);
    });
}

目前出现问题:Chrome浏览器中接收端无画面,Firefox浏览器中出现WebRTC错误。我是WebRTC新手,请问问题出在哪里?


问题分析与修复方案

1. Offer端未设置本地描述,发送的SDP不完整

调用peer.createOffer()后,必须先通过peer.setLocalDescription(offer)将Offer设置为本地描述,才能生成包含ICE候选相关信息的完整SDP。原代码直接发送原始Offer,导致SDP缺失关键信息,接收端无法正常处理。

修改后的Offer端createOfferer函数:

const createOfferer = () => {
  var peer = new RTCPeerConnection(turnConfig);

  // 先判断本地媒体流是否准备完成
  if (!localStream.getTracks().length) {
    console.error("本地媒体流未准备好");
    return;
  }

  localStream.getTracks().forEach(track => {
    peer.addTrack(track, localStream);
  });

  peer.createOffer()
    .then((offer) => {
      return peer.setLocalDescription(offer); // 先设置本地描述
    })
    .then(() => {
      // 发送完整的本地描述SDP
      fetch(window.location.href, {
        method: "POST",
        body: JSON.stringify(peer.localDescription),
        headers: {
          "Content-Type": "application/json"
        }
      });
    })
    .catch(err => console.error("创建Offer失败:", err)); // 增加错误捕获
}

2. 缺少ICE候选的交换逻辑

WebRTC需要交换ICE候选才能建立实际的网络连接,两端都需要监听icecandidate事件,收集候选信息并通过数据库完成交换。

  • Offer端添加ICE候选监听:
peer.addEventListener('icecandidate', (e) => {
  if (e.candidate) {
    // 将候选信息存入数据库,供接收端获取
    fetch('/store-ice-candidate', {
      method: "POST",
      body: JSON.stringify({
        candidate: e.candidate,
        type: "offer"
      }),
      headers: { "Content-Type": "application/json" }
    });
  }
});
  • 接收端获取到Offer端的候选后,调用peer.addIceCandidate(candidate)添加;同时接收端也要监听自己的icecandidate事件,将候选发送给Offer端添加。

3. Answer端未将Answer发送回Offer端

Answer端创建Answer并设置本地描述后,必须将Answer的SDP发送给Offer端,让Offer端调用peer.setRemoteDescription(answer),否则连接无法完成握手。

修改后的Answer端createAnswererForSeat函数:

const createAnswererForSeat = (offer) => {
  var peer = new RTCPeerConnection(turnConfig);

  var remoteVideo = createVideoForSeat();
  setOnTrack(peer, remoteVideo);

  peer.setRemoteDescription(offer)
    .then(() => {
      return peer.createAnswer();
    })
    .then((answer) => {
      return peer.setLocalDescription(answer);
    })
    .then(() => {
      // 将Answer发送回Offer端(存入数据库)
      fetch('/store-answer', {
        method: "POST",
        body: JSON.stringify(peer.localDescription),
        headers: { "Content-Type": "application/json" }
      });
    })
    .catch(err => console.error("创建Answer失败:", err)); // 增加错误捕获

  // 监听ICE候选,发送给Offer端
  peer.addEventListener('icecandidate', (e) => {
    if (e.candidate) {
      fetch('/store-ice-candidate', {
        method: "POST",
        body: JSON.stringify({
          candidate: e.candidate,
          type: "answer"
        }),
        headers: { "Content-Type": "application/json" }
      });
    }
  });
}

4. 媒体流准备时机问题

Offer端的createOfferer可能在getUserMedia完成前被调用,导致localStream为空,没有添加任何媒体轨道。建议将createOfferer的调用移到getUserMedia的回调中:

navigator.mediaDevices.getUserMedia(videoConstraints).then((stream) => {
  localStream = stream;
  video = document.getElementById("video");
  video.srcObject = stream;
  // 媒体流准备好后再创建Offer
  createOfferer();
})

5. 缺失错误捕获逻辑

原代码中大量Promise操作未添加catch,导致Firefox的错误无法被捕获排查。所有异步操作都要添加错误处理,方便定位问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 06:43:19