WebRTC项目中ICE Candidates无法生成的问题求助
WebRTC ICE候选生成失败问题修复
自建socket.io信令服务器实现WebRTC视频通话,目前offer与answer可正常创建,但接收方无法生成ICE候选,尝试添加transceiver属性后仅发起呼叫的设备能生成候选,接收方问题依旧。
原代码
const call = document.getElementById("call") const endcall = document.getElementById("endcall") const videosection = document.getElementById('rightvideo') const user1 = document.getElementById('user1') // first video pannel which shows client video const user2 = document.getElementById('user2') //second video pannel which shows remote videos const servers = { iceServers:[ { urls:['stun:stun1.l.google.com:19302','stun:stun2.l.google.com:19302'] } ] } const socket = io(); let localstream; let remotestream; let poffer; let peerconnection; let recieverid = undefined; let recieversidermoteoffer; let recieversidelocalanswer; let sendersideoffer; let sendersideanswer; // sender side socket.on('answer',async (data)=>{ data = JSON.parse(data); console.log('answer recieved sent by ',recieverid,' ',data.ans) sendersideanswer = data.ans; await peerconnection.setLocalDescription(sendersideoffer); if(!peerconnection.currentRemoteDescription) await peerconnection.setRemoteDescription(sendersideanswer); }) //reciever side socket.on('offer',(data)=>{ data = JSON.parse(data); videosection.style.display = 'flex'; recieversidermoteoffer = data.offer; settingsteam(); createanswer(); }) //common socket.on('candidate',async (data)=>{ data = JSON.parse(data); await peerconnection.addIceCandidate(data.icecandidate) }) call.onclick = ()=>{ if(recieverid) { callinit() videosection.style.display = 'flex'; } } endcall.onclick = ()=>{ if(localstream){ localstream.getTracks().forEach((track)=>{ track.stop() })} videosection.style.display = 'none'; } //function area //sender side let callinit = async ()=>{ settingsteam(); createpeerconnection(); // creating offer poffer = await peerconnection.createOffer() sendersideoffer = poffer; socket.emit('offer',JSON.stringify({reciever:recieverid,offer:poffer})) } //reciever side let createanswer = async ()=>{ createpeerconnection(); await peerconnection.setLocalDescription(recieversidelocalanswer) await peerconnection.setRemoteDescription(recieversidermoteoffer); let answer = await peerconnection.createAnswer(); recieversidelocalanswer = answer; socket.emit('answer',JSON.stringify({reciever:recieverid,ans:answer})) } //common let settingsteam =async ()=>{ localstream = await navigator.mediaDevices.getUserMedia({video:true,audio:false}); remotestream = new MediaStream(); user1.srcObject = localstream; user2.srcObject = remotestream; } let createpeerconnection =async ()=>{ peerconnection = new RTCPeerConnection(servers); peerconnection.onicecandidate = (e)=>{ if(e.candidate) { socket.emit('candidate',JSON.stringify({reciever:recieverid,icecandidate:e.candidate})) } } if(!localstream) { localstream = await navigator.mediaDevices.getUserMedia({video:true,audio:false}); user1.srcObject = localstream } localstream.getTracks().forEach((track)=>{ peerconnection.addTrack(track) }) peerconnection.ontrack = (event)=>{ event.streams[0].getTracks = (tracks)=>{ remotestream.addTrack(tracks) } } }
核心问题点
接收方SDP设置顺序完全错误
在createanswer函数中,先执行setLocalDescription(此时recieversidelocalanswer未赋值,为undefined),再设置远程SDP、创建answer,违背WebRTC标准流程:必须先设置远程offer的SDP,再创建answer,最后设置本地answer的SDP。ontrack事件处理逻辑错误
错误重写event.streams[0].getTracks方法,导致远程流轨道无法正确添加到本地流中。异步执行未等待
socket.on('offer')中调用的异步函数settingsteam未等待完成就执行createanswer,可能导致流未准备好就创建PeerConnection。
修复后的关键代码
修复接收方createanswer函数
//reciever side let createanswer = async ()=>{ await settingsteam(); // 等待流准备完成 createpeerconnection(); // 先设置远程offer的SDP await peerconnection.setRemoteDescription(recieversidermoteoffer); // 创建answer let answer = await peerconnection.createAnswer(); recieversidelocalanswer = answer; // 再设置本地answer的SDP await peerconnection.setLocalDescription(recieversidelocalanswer); socket.emit('answer',JSON.stringify({reciever:recieverid,ans:answer})) }
修复ontrack事件处理
peerconnection.ontrack = (event)=>{ // 直接遍历远程轨道添加到remotestream event.streams[0].getTracks().forEach(track => { remotestream.addTrack(track); }); }
修复发起方answer处理逻辑
// sender side socket.on('answer',async (data)=>{ data = JSON.parse(data); console.log('answer recieved sent by ',recieverid,' ',data.ans) sendersideanswer = data.ans; // 本地offer的SDP在createOffer后已设置,只需设置远程answer的SDP if(!peerconnection.currentRemoteDescription) { await peerconnection.setRemoteDescription(sendersideanswer); } })
额外优化点
- 确保
recieverid已正确赋值,否则信令消息无法发送到目标用户 - 在
createpeerconnection中避免重复调用getUserMedia,可通过判断localstream是否存在跳过 - 建议用
try/catch包裹所有异步操作,便于排查隐藏错误
内容的提问来源于stack exchange,提问作者Naman Gupta
相关产品推荐
相关产品推荐

