Reputation: 2669
I do not know what I am doing wrong with implementing a serverless WebRTC.
I do not know what I am doing wrong with implementing a serverless WebRTC.
Here is what I have so far:
var localStream, localPeerConnection, remotePeerConnection;
var servers = {"iceServers":[{"url":"stun:23.21.150.121"}]};
var sdpConstraints = {
optional: [],
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
var localVideo = document.getElementById("alice");
var remoteVideo = document.getElementById("bob");
var startButton = document.getElementById("startButton");
var callButton = document.getElementById("callButton");
var hangupButton = document.getElementById("hangupButton");
var joinButton = document.getElementById("joinButton");
var localOffer = document.getElementById("localOffer");
var remoteOffer = document.getElementById("remoteOffer");
var localAnswer = document.getElementById("localAnswer");
var remoteAnswer = document.getElementById("remoteAnswer");
var showLocalOffer = document.getElementById("showLocalOffer");
var getRemoteOffer = document.getElementById("getRemoteOffer");
var showLocalAnswer = document.getElementById("showLocalAnswer");
var getRemoteAnswer = document.getElementById("getRemoteAnswer");
var sentLocalOfferButton = document.getElementById("sentLocalOfferButton");
var pasteRemoteOfferButton = document.getElementById("pasteRemoteOfferButton");
var sentLocalAnswerButton = document.getElementById("sentLocalAnswerButton");
var pasteRemoteAnswerButton = document.getElementById("pasteRemoteAnswerButton");
startButton.disabled = false;
callButton.disabled = true;
hangupButton.disabled = true;
joinButton.disabled = true;
showLocalOffer.style.display = 'none';
getRemoteOffer.style.display = 'none';
showLocalAnswer.style.display = 'none';
getRemoteAnswer.style.display = 'none';
startButton.onclick = start;
callButton.onclick = call;
joinButton.onclick = join;
hangupButton.onclick = hangup;
sentLocalOfferButton.onclick = showRemote;
pasteRemoteOfferButton.onclick = answerCreate;
pasteRemoteAnswerButton.onclick = answerRemote;
sentLocalAnswerButton.onclick = hideRemoteAnswer;
function trace(text) {
console.log((performance.now() / 1000).toFixed(3) + ": " + text);
}
function start() {
trace("Requesting local stream");
startButton.disabled = true;
getUserMedia({audio:true, video:true}, gotStream,
function(error) {
trace("getUserMedia error: ", error);
});
}
function gotStream(stream){
trace("Received local stream");
localVideo.src = URL.createObjectURL(stream);
localStream = stream;
callButton.disabled = false;
joinButton.disabled = false;
}
// ALICE
function call() {
showLocalOffer.style.display = 'block';
callButton.disabled = true;
joinButton.disabled = true;
hangupButton.disabled = false;
trace("Starting call");
if (localStream.getVideoTracks().length > 0) {
trace('Using video device: ' + localStream.getVideoTracks()[0].label);
}
if (localStream.getAudioTracks().length > 0) {
trace('Using audio device: ' + localStream.getAudioTracks()[0].label);
}
localPeerConnection = new RTCPeerConnection(servers);
trace("Created local peer connection object localPeerConnection");
localPeerConnection.addStream(localStream);
trace("Added localStream to localPeerConnection");
localPeerConnection.createOffer(gotLocalDescription,handleError,sdpConstraints);
}
function gotLocalDescription(description){
localPeerConnection.setLocalDescription(description);
trace("Offer from localPeerConnection SDP: \n" + description.sdp);
trace("Offer from localPeerConnection TYPE: \n" + description.type);
localOffer.value = JSON.stringify(description);
}
function showRemote() {
showLocalOffer.style.display = 'none';
getRemoteAnswer.style.display = 'block';
}
function answerRemote() {
getRemoteAnswer.style.display = 'none';
var remoteSesssionDescription = new RTCSessionDescription(JSON.parse(remoteAnswer.value));
localPeerConnection.setRemoteDescription(remoteSesssionDescription);
localPeerConnection.onaddstream = gotRemoteStream;
localPeerConnection.onicecandidate = gotRemoteIceCandidate;
}
function gotRemoteIceCandidate(evt) {
if (event.candidate) {
localPeerConnection.addIceCandidate(new RTCIceCandidate(evt.candidate));
trace("Remote ICE candidate: \n " + evt.candidate.candidate);
}
}
// BOB
function join() {
trace("Joining call");
getRemoteOffer.style.display = 'block';
callButton.disabled = true;
hangupButton.disabled = false;
joinButton.disabled = true;
remotePeerConnection = new RTCPeerConnection(servers);
trace("Created remote peer connection object remotePeerConnection");
}
function answerCreate() {
getRemoteOffer.style.display = 'none';
showLocalAnswer.style.display = 'block';
var sessionDescription = new RTCSessionDescription(JSON.parse(remoteOffer.value));
remotePeerConnection.setRemoteDescription(sessionDescription);
remotePeerConnection.createAnswer(gotRemoteDescription,handleError,sdpConstraints);
}
function gotRemoteDescription(answerSdp) {
remotePeerConnection.setLocalDescription(answerSdp);
trace("Answer from remotePeerConnection SDP: \n" + answerSdp.sdp);
trace("Answer from remotePeerConnection TYPE: \n" + answerSdp.type);
localAnswer.value = JSON.stringify(answerSdp);
remotePeerConnection.onaddstream = gotRemoteStream;
remotePeerConnection.onicecandidate = gotLocalIceCandidate;
}
function hideRemoteAnswer() {
showLocalAnswer.style.display = 'none';
}
function gotRemoteStream(evt) {
console.log('evt: ', evt);
remoteVideo.src = URL.createObjectURL(evt.stream);
trace("Received remote stream");
}
function gotLocalIceCandidate(evt){
if (event.candidate) {
remotePeerConnection.addIceCandidate(new RTCIceCandidate(evt.candidate));
trace("Local ICE candidate: \n" + evt.candidate.candidate);
}
}
// GENERAL USE
function hangup() {
trace("Ending call");
localPeerConnection = null;
remotePeerConnection = null;
hangupButton.disabled = true;
callButton.disabled = false;
joinButton.disabled = false;
showLocalOffer.style.display = 'none';
getRemoteOffer.style.display = 'none';
showLocalAnswer.style.display = 'none';
getRemoteAnswer.style.display = 'none';
}
function handleError(){
trace("Cannot Create Offer");
}
Upvotes: 1
Views: 1642
Reputation: 42430
I've written one of these, which you can try in Firefox in this other answer. Yes, typically with a server, you'd send ICE candidates over your signaling channel as soon as they become available.
But here that's impractical. I think you want one message to send to your friend. To do that, wait until the browser has had time to discover all ICE candidates. They will then be included in the offer/answer for you (Note: this may take up to 15 seconds on Windows in my experience).
One way to do this is to listen to onnegotationneeded
until it returns a null candidate, signifying the end of ice gathering:
pc.onicecandidate = function(e) {
if (e.candidate) return;
var offerSdp = pc.localDescription.sdp;
// send offerSdp string to friend
};
Hope that helps.
Upvotes: 1