Reputation: 401
I have run into a strange problem with the abstraction of the offer generation process of WebRTC. It seems as if the incoming ice candidates never reach the null candidate. I have successfully generated offers before using almost the same code but for whatever reason my abstracted version only reaches 12 candidates compared to the regular 20 of my original. This is so strange, it seems the code is pretty much the same but the abstracted one does not work even on the same browser.
Original working code:
var localConn = new webkitRTCPeerConnection({'iceServers':[{...}]});
var remoteConn = new webkitRTCPeerConnection({'iceServers':[{...}]});
function initMedia(localView, callback){
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var constraints = {audio:true,video:true};
navigator.getUserMedia(constraints, successStream, errorStream);
//onSuccess and Error functions
function successStream(stream){
window.stream = stream;
if(window.URL){
$('#'+localView).attr('src',window.URL.createObjectURL(stream));
} else {
$('#'+localView).attr('src',stream);
}
localConn.addStream(stream);
remoteConn.addStream(stream);
console.log('local Stream: '+ stream.id);
callback(); //-> goes on to create new offer
}
function errorStream(error){
console.log('navigator.getUserMedia error: ', error);
}
}
//function that generates offer and sends it out to a callback
function newOffer(callback){
console.log('creating new offer');
localConn.createOffer(function (sessionDescription){
localConn.setLocalDescription(sessionDescription);
}, function(error){
console.log('Error setting local description: '+error);
});
createOffer();
//gather ICE with a callback to handle/send generated offer
function createOffer(){
localConn.onicecandidate = function(iceEvent){
console.log('gathering local ice'); //ice events fired (20 total)
//upon gathering all local ice
if(iceEvent.candidate === null){
console.log('local ice gathered'); //success
var offer = {'type': localConn.localDescription.type,
'sdp': localConn.localDescription.sdp};
offer = JSON.stringify(offer);
console.log('offer created');
callback(offer);
}
}
}
}
New version with functions for abstraction (not getting null ice candidate)
//general rtc vars
var localConn = new webkitRTCPeerConnection({'iceServers':[{'url':'stun:stun.1.google.com:19302'}]});
var remoteConn = new webkitRTCPeerConnection({'iceServers':[{'url':'stun:stun.1.google.com:19302'}]});
//var mediaStream;
var channel;
//creates a stream from webcam
//@params function streamHandle(stream)
function initStream(streamHandle){
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var constraints = {audio:true,video:true};
navigator.getUserMedia(constraints, successStream, errorStream);
//onSuccess and Error functions
function successStream(stream){
window.stream = stream;
console.log('TreRTC: Local Stream-'+ stream.id);
//mediaStream = stream;
localConn.addStream(stream); //not sure if these need to be added before
remoteConn.addStream(stream); //or if they can be added in the creatOffer(stream) function
streamHandle(stream); //gets inserted into createOffer function
}
function errorStream(error){
console.log('navigator.getUserMedia error: ', error);
}
}
//creates an offer to be sent
//@params Stream stream (from getusermedia)
//@return string offer
function createOffer(stream){
console.log('TreRTC: Creating Offer');
//localConn.addStream(stream); //tried both ways from top and from internal
//remoteConn.addStream(stream);
localConn.createOffer(function (sessionDescription){
localConn.setLocalDescription(sessionDescription);
}, function(error){
console.log('Error setting local description: '+error);
});
localConn.onicecandidate = function(iceEvt){
console.log('TreRTC: ICE in'); //ice events firing (12 total)
if(iceEvt.candidate === null){
console.log('TreRTC: ICE gathered'); //never reaches to this point...
var offer = {'type': localConn.localDescription.type,
'sdp': localConn.localDescription.sdp};
offer = JSON.stringify(offer);
console.log('TreRTC: Offer initialized');
return offer; //returns offer as a string
} //could also specify a callback
}
}
Upvotes: 0
Views: 242
Reputation: 42500
streamHandle(stream); //gets inserted into createOffer function
You probably have a race between your createOffer
function and your initStream
function completing, as the two are effectively asynchronous when you count everything they set in motion (hard to tell for sure since you're not showing that code).
If you want to abstract WebRTC, you should look into getting off the old legacy API and instead use RTCPeerConnection's modern promise-methods. Promises are superior abstractions to callbacks when it comes to dealing with races exactly like this.
Also consider using the onnegotiationneeded
callback to trigger negotiation, to solves this (but beware of bug in Chrome).
Here's a local connection example (Use https fiddle in Chrome):
var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
navigator.mediaDevices.getUserMedia({video: true, audio: true})
.then(stream => pc1.addStream(video1.srcObject = stream))
.catch(e => console.log(e));
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc2.ontrack = e => video2.srcObject = e.streams[0];
pc1.oniceconnectionstatechange = e => console.log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
pc1.createOffer().then(d => pc1.setLocalDescription(d))
.then(() => pc2.setRemoteDescription(pc1.localDescription))
.then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
.then(() => pc1.setRemoteDescription(pc2.localDescription))
.catch(e => console.log(e));
<video id="video1" width="160" height="120" autoplay muted></video>
<video id="video2" width="160" height="120" autoplay></video>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
Upvotes: 2