如何实现WebRTC单向视频通话?解决权限与SDP异常问题
正确实现WebRTC单向视频通话的方案
核心思路
单向视频的关键是:仅发送端需要获取视频权限并提供轨道,接收端完全不需要调用getUserMedia,这样就不会出现误导性的权限请求。同时利用收发器(Transceiver)明确指定媒体流的方向,避开已废弃的offerToReceive*选项。
方案一:发送端发起Offer(推荐,逻辑更直观)
发送端(仅发送视频)操作:
- 初始化PeerConnection,配置统一的STUN服务器:
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
- 获取本地视频轨道(这一步才会触发权限请求,合理且明确):
const stream = await navigator.mediaDevices.getUserMedia({ video: true }); const videoTrack = stream.getVideoTracks()[0];
- 添加视频收发器,指定方向为
sendonly(仅发送):
// 方式1:直接用addTrack,自动创建sendonly收发器 pc.addTrack(videoTrack); // 方式2:显式创建收发器 // pc.addTransceiver(videoTrack, { direction: 'sendonly' });
- 创建Offer并设置本地描述,通过信令发送给接收端:
const offer = await pc.createOffer(); await pc.setLocalDescription(offer); sendSignalingMessage(offer); // 自定义信令发送逻辑
接收端(仅接收视频)操作:
- 初始化PeerConnection,配置相同的STUN服务器:
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
- 监听
track事件,拿到视频轨道后播放:
pc.ontrack = (event) => { const videoElement = document.getElementById('remote-video'); if (!videoElement.srcObject) { videoElement.srcObject = new MediaStream([event.track]); } };
- 收到发送端的Offer后,设置远程描述,创建应答并发送:
const offer = await receiveSignalingMessage(); // 自定义信令接收逻辑 await pc.setRemoteDescription(offer); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); sendSignalingMessage(answer);
方案二:接收端发起Offer(适配你的尝试场景)
如果必须由接收端发起Offer,需严格保证收发器的顺序和方向一致,避免m-line顺序错误:
接收端操作:
- 初始化PeerConnection,添加
recvonly的视频收发器(仅接收):
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); // 只添加一个视频收发器,方向设为仅接收 pc.addTransceiver('video', { direction: 'recvonly' });
- 创建Offer并发送给发送端:
const offer = await pc.createOffer(); await pc.setLocalDescription(offer); sendSignalingMessage(offer);
- 监听
track事件准备播放视频(同方案一的接收端步骤2)。
发送端操作:
- 初始化PeerConnection,收到接收端的Offer后先设置远程描述:
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); const offer = await receiveSignalingMessage(); await pc.setRemoteDescription(offer);
- 获取本地视频轨道,找到对应的视频收发器并替换轨道:
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';
- 创建应答并发送给接收端:
const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); sendSignalingMessage(answer);
解决你遇到的错误
- m-line顺序不匹配:确保在整个协商过程中,收发器的数量和顺序完全一致,不要在后续的Offer/Answer中新增、删除收发器,也不要改变它们的顺序。
- STUN地址不兼容:两端必须使用相同的STUN服务器配置,避免使用私有或不兼容的STUN地址,推荐使用公共STUN服务器如
stun:stun.l.google.com:19302。
内容的提问来源于stack exchange,提问作者Anton Zhdanov
相关产品推荐
相关产品推荐

