WebRTC RTCPeerConnection ontrack事件监听器不触发问题
问题核心原因
- 你在初始SDP(offer/answer)协商完成之后才调用
addTrack添加视频轨道。WebRTC只会在协商过程中交换当前已存在的媒体轨道信息,协商完成后新增轨道不会自动同步媒体信息到对端,对端自然不会触发track事件。 - 原有代码使用已废弃的回调式
createOffer/createAnswer写法,存在时序漏洞:很可能SDP还没完成本地设置就被发送到对端,导致协商信息缺失。 - RTCPeerConnection初始化时未配置ICE服务器,跨网络环境下会出现ICE连通失败,本地localhost测试虽然暂时能通信,但稳定性极差。
- 答端接收到数据通道后未绑定消息、错误事件监听,会出现一端能发消息另一端收不到的问题。
修复方案
- 添加自动重协商逻辑
在initialize函数中创建RTCPeerConnection实例后,添加onnegotiationneeded监听,只要媒体轨道发生变动就自动重新发起SDP协商,同时加协商锁避免两端同时发起offer导致的冲突:let isNegotiating = false; peerConnection.onnegotiationneeded = async () => { if (isNegotiating) return; isNegotiating = true; try { const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); send({ event: "offer", data: peerConnection.localDescription }); } catch (err) { console.error("协商失败:", err); } } - 替换废弃的回调写法,修复SDP时序问题
将原有createOffer、handleOffer、handleAnswer三个方法全部替换为async/await写法,保证SDP设置完成后再发送:async function createOffer() { try { const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); send({ event: "offer", data: offer }); } catch (error) { alert("创建offer失败"); console.error(error); } } async function handleOffer(offer) { try { await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); const answer = await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); send({ event: "answer", data: answer }); isNegotiating = false; } catch (error) { alert("处理offer失败"); console.error(error); } } async function handleAnswer(answer) { try { await peerConnection.setRemoteDescription(new RTCSessionDescription(answer)); console.log("连接建立成功"); isNegotiating = false; } catch (error) { console.error("处理answer失败:", error); } } - 补全ICE服务器配置
把initialize函数里的configuration从null改成带公共STUN服务器的配置,保证ICE连通性:var configuration = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] }; - 补全数据通道事件绑定
修改peerConnection.ondatachannel回调,给答端接收到的数据通道绑定和发起端一致的事件监听:peerConnection.ondatachannel = function (event) { dataChannel = event.channel; dataChannel.onerror = function(error) { console.log("数据通道错误:", error); }; dataChannel.onmessage = function(event) { console.log("收到消息:", event.data); }; dataChannel.onclose = function() { console.log("数据通道已关闭"); }; };
验证说明
修改完成后,打开两个独立标签页建立连接,点击发送流按钮时,发送端会自动触发重协商,SDP交换完成后接收端就会触发track事件,正常渲染远端视频流。
测试注意:两个标签页不要通过浏览器的“复制标签页”功能打开,手动开两个空白页分别输入地址访问,避免标签页实例、WebSocket会话冲突。
内容的提问来源于stack exchange,提问作者Nonek
相关产品推荐
相关产品推荐

