I'm testing the WebRTC video calling API.
For signaling Firestore is being used.
Offer is being created in caller web browser A and sent to callee B, which then returns the answer to A. Then icecandidates are being shared where I'm facing the problem described below -
- Chrome not showing any error in console. But when I head over to chrome://webrtc-internals/, I see multiple
icecandidateerrorerror logs -
// replaced real IP with x.x.x.x
// following errors from
icecandidateerror{
url: turn:numb.viagenie.ca:3478?transport=udp
address: x.x.x.x
port: 55791
host_candidate: x.x.x.x:55791
error_text: TURN allocate request timed out.
error_code: 701
}
icecandidateerror{
url: stun:stun2.l.google.com:19302
address: x.x.x.x
port: 55791
host_candidate: x.x.x.x:55791
error_text: STUN allocate request timed out.
error_code: 701
}
.....total of 8 such errors
// following logs are from webrtc-internals_dump.txt
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: turn:numb.viagenie.ca:3478?transport=udp\naddress: x.x.x.x\nport: 55787\nhost_candidate: x.x.x.x:55787\nerror_text: TURN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: turn:numb.viagenie.ca:3478?transport=udp\naddress: x.x.x.x\nport: 55791\nhost_candidate: x.x.x.x:55791\nerror_text: TURN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:numb.viagenie.ca:3478\naddress: x.x.x.x\nport: 55787\nhost_candidate: x.x.x.x:55787\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:numb.viagenie.ca:3478\naddress: x.x.x.x\nport: 55791\nhost_candidate: x.x.x.x:55791\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:stun1.l.google.com:19302\naddress: x.x.x.x\nport: 55787\nhost_candidate: x.x.x.x:55787\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:stun1.l.google.com:19302\naddress: x.x.x.x\nport: 55791\nhost_candidate: x.x.x.x:55791\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:stun2.l.google.com:19302\naddress: x.x.x.x\nport: 55787\nhost_candidate: x.x.x.x:55787\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
{
"time": "7/28/2020, 9:14:57 PM",
"type": "icecandidateerror",
"value": "url: stun:stun2.l.google.com:19302\naddress: x.x.x.x\nport: 55791\nhost_candidate: x.x.x.x:55791\nerror_text: STUN allocate request timed out.\nerror_code: 701"
},
Here one thing to notice is that the IP address x.x.x.x is IP address of my ethernet but I'm connected to internet via wifi. Is this be the actual problem?
Firefox console error -
WebRTC: ICE failed, see about:webrtc for more details
Code Sample:
// TODO: Replace the following with your app's Firebase project configuration
var firebaseConfig = {
apiKey: "xx-xx",
authDomain: "xx",
databaseURL: "https://txx.firebaseio.com",
projectId: "xx-xx-xx",
storageBucket: "xx",
messagingSenderId: "xx",
appId: "xx",
measurementId: "xx"
};
const servers = {
iceServers: [
{
urls: [
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302'
],
},
{ 'urls': 'turn:numb.viagenie.ca','credential': 'xzx','username': 'xx'}
],
iceCandidatePoolSize: 10,
};
// Initialize Firebase
var app = firebase.initializeApp(firebaseConfig);
var localStream;
var remoteStream;
var peerConnection;
var localVideo = document.querySelector('#localVideo')
var remoteVideo = document.querySelector('#remoteVideo')
const db = firebase.firestore();
const userID = Math.round(Math.random()*1000)
const collection = db.collection('rooms')
var roomRef = null
var roomID = null
var pc = new RTCPeerConnection(servers)
pc.onicecandidate = event => {
console.log('ice candidate')
if(event.candidate) {
roomRef.update({ iceCandidates: firebase.firestore.FieldValue.arrayUnion({ ice: event.candidate.toJSON(), id: userID}) })
}
else console.log('Sent all ice candidates!')
}
// this is deprecated
// pc.onaddstream = (event => remoteVideo.srcObject = event.stream);
async function createRoom(){
await getLocalStream()
pc.createOffer()
.then(async (offer) => {
roomRef = await collection.add({ id: userID, offer: { sdp: offer.sdp, type: 'offer' }})
console.log(`New room created with ID ${roomRef.id}`)
document.querySelector('#room-id').value = roomRef.id
return offer
})
.then(offer => pc.setLocalDescription(offer))
.then(() => attachRoomUpdateHandler())
}
async function joinRoom(){
roomID = window.prompt('Enter room id', null)
if(roomID){
await getLocalStream()
console.log(roomID)
roomRef = collection.doc(roomID)
const roomSnapshot = await roomRef.get()
console.log(roomSnapshot,roomSnapshot.exits)
var roomData = roomSnapshot.data()
console.log(roomData.offer)
pc.setRemoteDescription(roomData.offer)
pc.createAnswer()
.then(answer => pc.setLocalDescription(answer))
.then(() => roomRef.update({ answers: firebase.firestore.FieldValue.arrayUnion({ sdp: pc.localDescription.sdp, id: userID}) }))
.then(() => attachRoomUpdateHandler())
}
}
async function attachRoomUpdateHandler(){
roomRef.onSnapshot(async snapshot => {
var updatedRoomData = snapshot.data()
if(updatedRoomData){
if(updatedRoomData.iceCandidates){
var remoteIceCandidates = updatedRoomData.iceCandidates.filter( ic => ic.id!=userID)
if(remoteIceCandidates.length>0) {
remoteIceCandidates.forEach(({ice}) => pc.addIceCandidate = new RTCIceCandidate(ice))
} else {
console.log('No remote ice candidates')
}
} else if(updatedRoomData.id!=userID){
console.log('callee remote set')
pc.setRemoteDescription(updatedRoomData.offer)
} else if(updatedRoomData.id==userID){
if(updatedRoomData.answers && updatedRoomData.answers.length){
pc.setRemoteDescription({ type: 'answer', sdp: updatedRoomData.answers[0].sdp})
console.log('answer remote set')
}
}
}
})
}
var getLocalStream = async function(){
return navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then(stream => { localStream = stream; localVideo.srcObject = stream ; return stream })
.then(stream => stream.getTracks().forEach(track => pc.addTrack(track, localStream)))
.then(() => {
pc.ontrack = (event => remoteVideo.srcObject = event.streams[0])
})
}
pc.addEventListener("connectionstatechange", ev => {
console.log(pc.connectionState, ev)
});
video{ max-width: 100%; }
<h1>Realtime communication with WebRTC</h1>
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<input type="text" id="room-id">
<button id="createBtn" onclick="createRoom()">Create Room</button>
<button id="joinBtn" onclick="joinRoom()">Join Room</button>
<p id="currentRoom"></p>
</div>
<script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-firestore.js"></script>
This much I could debug currently, but could not confirm whether the problem is in my code or STUN/TURN server.
Browser versions:
- Chrome version: Version 84.0.4147.89 (Official Build) (64-bit)
- Firefox version: 79.0 (64-bit)
