为什么我的WebRTC P2P视频通话只能单向传输?

治柯(打工版) 阅读 14

我在用WebRTC实现两人视频通话时,甲方能看见乙方画面,但乙方屏幕一直是黑的。已经检查过信令服务器和STUN/TURN配置没问题,双方都能收到SDP和ICE候选。这是客户端处理offer的代码:


function onOffer(offer) {
  pc.setRemoteDescription(offer)
    .then(() => pc.createAnswer())
    .then(answer => pc.setLocalDescription(answer))
    .then(() => signaling.sendAnswer(answer));
}

控制台报错”Failed to set remote answer in ontrack”,但调试时发现乙方收到的SDP里没有m=video轨道。奇怪的是甲方的offer里明明包含video媒体。这种单向传输的问题该怎么排查?

我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
UP主~含含
你这个单向视频的问题,大概率出在SDP协商阶段video轨道被过滤或者没正确添加track。虽然信令通了,但乙方收不到m=video行说明offer里的视频信息在传输或处理时丢了。

我遇到过类似情况,重点排查这几个点:

首先确认甲方生成offer前有没有真正拿到本地视频流并addTrack。比如:

navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then(stream => {
stream.getTracks().forEach(track => pc.addTrack(track, stream));
return pc.createOffer();
})
.then(offer => pc.setLocalDescription(offer))
.then(() => signaling.sendOffer(pc.localDescription));


如果漏了addTrack这一步,offer里虽然会生成m=video段,但标记为inactive或直接被忽略,对方解析时就看不到有效视频轨道。

然后是你贴的onOffer处理代码有个隐患:createAnswer之后没有检查answer.sdp是否真的包含m=video。你可以加个打印看看:

console.log(answer.sdp.includes('m=video'))

如果返回false,说明远端offer里video轨道没生效,可能是因为setRemoteDescription时浏览器认为没有对应的stream track关联。

另外注意ontrack事件监听有没有写对。必须在pc上提前监听,不然会错过track事件:

pc.ontrack = (e) => { if (e.track.kind === 'video') remoteVideo.srcObject = e.streams[0]; }

还有个小坑是某些浏览器(特别是Safari和旧版Chrome)在setRemoteDescription后不会立刻触发ontrack,要等完整的ICE连接建立才触发。建议同时监听iceconnectionstatechange看状态是否到connected。

最后那个“Failed to set remote answer in ontrack”错误,听起来像是你在ontrack里又调了setRemoteDescription?这肯定不对,ontrack是接收方收到流的回调,不应该在这里改SDP。检查下是不是事件处理嵌套错了。

我的做法是:两端都确保先addTrack再创建offer/answer,并在每一步前后打印localDescription和remoteDescription的sdp内容,对比看出哪一环video轨道消失了。这样基本能定位到具体步骤。
点赞 5
2026-02-12 03:04
ლ红彦
ლ红彦 Lv1
这个问题根本原因是SDP协商过程中媒体轨道没有正确匹配,导致一方的视频流在解析时被忽略。你提到乙方收到的offer里有video媒体段,但最终answer里却没了m=video,说明问题出在createAnswer阶段的媒体处理逻辑。

WebRTC的SDP协商是双向确认机制。即使你在offer中声明了video,如果接收方本地没有添加对应的媒体流track,PeerConnection在生成answer时会自动剔除video媒体描述,这是标准行为。你看到的"Failed to set remote answer in ontrack"错误也印证了这一点——ontrack事件触发时找不到对应track。

第一步要确认乙方是否在setRemoteDescription之前正确添加了本地媒体流。很多人以为只要对方发了offer带video,自己这边就会自动创建接收轨道,其实不是。你必须主动把本地音视频track加到RTCPeerConnection实例上。

function onOffer(offer) {
// 必须确保本地流已经获取并添加到pc
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(localStream => {
// 把本地摄像头流的每个track都addTrack进去
localStream.getTracks().forEach(track => {
pc.addTrack(track, localStream); // 这一步很关键
});

return pc.setRemoteDescription(offer);
})
.then(() => pc.createAnswer())
.then(answer => {
console.log('Generated answer:', answer.sdp); // 打印看看有没有m=video
return pc.setLocalDescription(answer);
})
.then(() => signaling.sendAnswer(pc.localDescription)) // 注意这里要用localDescription
.catch(err => console.error('Error during SDP exchange:', err));
}


注意我改了最后sendAnswer的部分,你原来的代码传的是未定义的answer变量,应该用pc.localDescription,因为setLocalDescription之后描述才会真正生效。

第二步检查SDP内容。虽然你说信令没问题,但建议在setRemoteDescription前后打印offer.sdp,确认里面真的包含m=video字段。有时候前端序列化/反序列化会出问题,比如JSON.stringify时把SDP对象搞丢了部分内容。

console.log('Incoming offer SDP:', offer.sdp); // 看看原始SDP
pc.setRemoteDescription(offer)
.then(() => {
console.log('After setRemoteDesc, remote SDP:', pc.remoteDescription);
});


如果offer里确实有video但answer里没有,那基本可以确定是你没调addTrack。RTCPeerConnection会根据本地是否有sender track来决定是否响应相应媒体类型。

第三步是处理ontrack事件的方式。你应该在pc上监听ontrack,而不是依赖answer里的东西:

pc.ontrack = (event) => {
console.log('New remote track:', event.track.kind, event.streams[0]);
// 把接收到的远程流绑定到video标签
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo) {
remoteVideo.srcObject = event.streams[0];
}
};


最后提醒一个常见坑:getUserMedia权限拒绝或静默失败。建议加上错误处理:

navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.catch(err => {
console.error('Failed to access camera:', err);
// 即使没有本地摄像头,也要addTransceiver让answer包含video
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addTransceiver('audio', { direction: 'recvonly' });
});


这样即使拿不到本地流,也能告诉对方“我能接收video”,避免answer里丢掉m=video段。

总结排查顺序:
先确认本地流是否addTrack → 检查offer和answer的完整SDP内容 → 确保ontrack监听正确设置 → 加transceiver兜底。95%的单向通话问题都是因为漏了addTrack或者getUserMedia失败没处理。
点赞 2
2026-02-10 09:03