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

如何实现WebRTC单向视频通话?解决权限与SDP异常问题

正确实现WebRTC单向视频通话的方案

核心思路

单向视频的关键是:仅发送端需要获取视频权限并提供轨道,接收端完全不需要调用getUserMedia,这样就不会出现误导性的权限请求。同时利用收发器(Transceiver)明确指定媒体流的方向,避开已废弃的offerToReceive*选项。


方案一:发送端发起Offer(推荐,逻辑更直观)

发送端(仅发送视频)操作:

  1. 初始化PeerConnection,配置统一的STUN服务器:
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
  1. 获取本地视频轨道(这一步才会触发权限请求,合理且明确):
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const videoTrack = stream.getVideoTracks()[0];
  1. 添加视频收发器,指定方向为sendonly(仅发送):
// 方式1:直接用addTrack,自动创建sendonly收发器
pc.addTrack(videoTrack);
// 方式2:显式创建收发器
// pc.addTransceiver(videoTrack, { direction: 'sendonly' });
  1. 创建Offer并设置本地描述,通过信令发送给接收端:
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendSignalingMessage(offer); // 自定义信令发送逻辑

接收端(仅接收视频)操作:

  1. 初始化PeerConnection,配置相同的STUN服务器:
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
  1. 监听track事件,拿到视频轨道后播放:
pc.ontrack = (event) => {
  const videoElement = document.getElementById('remote-video');
  if (!videoElement.srcObject) {
    videoElement.srcObject = new MediaStream([event.track]);
  }
};
  1. 收到发送端的Offer后,设置远程描述,创建应答并发送:
const offer = await receiveSignalingMessage(); // 自定义信令接收逻辑
await pc.setRemoteDescription(offer);

const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendSignalingMessage(answer);

方案二:接收端发起Offer(适配你的尝试场景)

如果必须由接收端发起Offer,需严格保证收发器的顺序和方向一致,避免m-line顺序错误:

接收端操作:

  1. 初始化PeerConnection,添加recvonly的视频收发器(仅接收):
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// 只添加一个视频收发器,方向设为仅接收
pc.addTransceiver('video', { direction: 'recvonly' });
  1. 创建Offer并发送给发送端:
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendSignalingMessage(offer);
  1. 监听track事件准备播放视频(同方案一的接收端步骤2)。

发送端操作:

  1. 初始化PeerConnection,收到接收端的Offer后先设置远程描述:
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
const offer = await receiveSignalingMessage();
await pc.setRemoteDescription(offer);
  1. 获取本地视频轨道,找到对应的视频收发器并替换轨道:
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const videoTrack = stream.getVideoTracks()[0];
// 找到接收端创建的视频收发器
const videoTransceiver = pc.getTransceivers().find(t => t.receiver.track.kind === 'video');
// 替换为本地视频轨道
videoTransceiver.sender.replaceTrack(videoTrack);
// 可选:将收发器方向设置为sendonly,明确仅发送
videoTransceiver.direction = 'sendonly';
  1. 创建应答并发送给接收端:
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendSignalingMessage(answer);

解决你遇到的错误

  1. m-line顺序不匹配:确保在整个协商过程中,收发器的数量和顺序完全一致,不要在后续的Offer/Answer中新增、删除收发器,也不要改变它们的顺序。
  2. STUN地址不兼容:两端必须使用相同的STUN服务器配置,避免使用私有或不兼容的STUN地址,推荐使用公共STUN服务器如stun:stun.l.google.com:19302。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:46:10