WebRTC audio/video call and conferencing server.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

237 lines
6.7 KiB

/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
define(['underscore', 'mediastream/peercall'], function(_, PeerCall) {
//NOTE(longsleep): This id should be changed to something undeterministic.
var conferences = 0;
var PeerConference = function(webrtc, currentcall, id) {
this.webrtc = webrtc;
this.currentcall = currentcall;
this.calls = {};
this.callsIn = {};
this.e = $({});
if (!id) {
this.id = webrtc.api.id + "_" + (++conferences);
} else {
this.id = id;
}
console.log("Created conference", this.id);
};
PeerConference.prototype.createCall = function(id, from, to) {
var currentcall = new PeerCall(this.webrtc, id, from, to);
currentcall.e.on("closed", _.bind(function() {
delete this.calls[id];
if (this.callsIn.hasOwnProperty(id)) {
delete this.callsIn[id];
}
console.log("Cleaned up conference call", id);
if (_.isEmpty(this.calls)) {
console.log("Conference is now empty -> cleaning up.");
this.e.triggerHandler("finished");
}
}, this));
currentcall.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentcall) {
this.onConnectionStateChange(iceConnectionState, currentcall);
}, this));
currentcall.e.on("remoteStreamAdded", _.bind(function(event, stream, currentcall) {
this.webrtc.onRemoteStreamAdded(stream, currentcall);
}, this));
currentcall.e.on("remoteStreamRemoved", _.bind(function(event, stream, currentcall) {
this.webrtc.onRemoteStreamRemoved(stream, currentcall);
}, this));
return currentcall;
};
PeerConference.prototype.doCall = function(id, autocall) {
if (id === this.currentcall.id || this.calls.hasOwnProperty(id)) {
// Ignore calls which we already have.
//console.debug("Already got a call to this id (doCall)", id, this.calls, this.currentcall);
return;
}
var call = this.calls[id] = this.createCall(id, null, id);
call.setInitiate(true);
call.e.on("sessiondescription", _.bind(function(event, sessionDescription) {
console.log("Injected conference id into sessionDescription", this.id);
sessionDescription._conference = this.id;
}, this));
if (!autocall) {
this.webrtc.e.triggerHandler("connecting", [call]);
}
console.log("Creating PeerConnection", call);
call.createPeerConnection(_.bind(function(peerconnection) {
// Success call.
if (this.webrtc.usermedia) {
this.webrtc.usermedia.addToPeerConnection(peerconnection);
}
call.createOffer(_.bind(function(sessionDescription, extracall) {
console.log("Sending offer with sessionDescription", sessionDescription, extracall.id);
this.webrtc.api.sendOffer(extracall.id, sessionDescription);
}, this));
}, this), _.bind(function() {
// Error call.
console.error("Failed to create peer connection for conference call.");
}, this));
};
PeerConference.prototype.handOver = function() {
// Use a new call as currentcall and return this one.
var calls = _.keys(this.callsIn);
if (calls.length) {
var id = calls[0];
var currentcall = this.currentcall = this.calls[id];
delete this.calls[id];
delete this.callsIn[id];
console.log("Handed over conference to", id, currentcall);
if (_.isEmpty(this.calls)) {
console.log("Conference is now empty -> cleaning up.");
this.e.triggerHandler("finished");
}
return currentcall;
}
return null;
};
PeerConference.prototype.autoAnswer = function(from, rtcsdp) {
if (from === this.currentcall.id || this.calls.hasOwnProperty(from)) {
console.warn("Already got a call to this id (autoAnswer)", from, this.calls);
return;
}
var call = this.calls[from] = this.createCall(from, this.webrtc.api.id, from);
console.log("Creating PeerConnection", call);
call.createPeerConnection(_.bind(function(peerconnection) {
// Success call.
call.setRemoteDescription(rtcsdp, _.bind(function() {
if (this.webrtc.usermedia) {
this.webrtc.usermedia.addToPeerConnection(peerconnection);
}
call.createAnswer(_.bind(function(sessionDescription, extracall) {
console.log("Sending answer", sessionDescription, extracall.id);
this.webrtc.api.sendAnswer(extracall.id, sessionDescription);
}, this));
}, this));
}, this), _.bind(function() {
// Error call.
console.error("Failed to create peer connection for auto answer.");
}, this));
};
PeerConference.prototype.getCall = function(id) {
var call = this.calls[id];
if (!call) {
call = null;
}
return call;
};
PeerConference.prototype.close = function() {
this.currentcall = null;
var api = this.webrtc.api;
_.each(this.calls, function(c) {
c.close();
var id = c.id;
if (id) {
api.sendBye(id);
}
});
this.calls = {};
};
PeerConference.prototype.onConnectionStateChange = function(iceConnectionState, currentcall) {
console.log("Conference peer connection state changed", iceConnectionState, currentcall);
switch (iceConnectionState) {
case "completed":
case "connected":
if (!this.callsIn.hasOwnProperty(currentcall.id)) {
this.callsIn[currentcall.id] = true;
this.pushUpdate();
}
break;
case "failed":
console.warn("Conference peer connection state failed", currentcall);
break;
}
this.webrtc.onConnectionStateChange(iceConnectionState, currentcall);
};
PeerConference.prototype.pushUpdate = function() {
var calls = _.keys(this.callsIn);
if (calls) {
if (this.currentcall) {
calls.push(this.currentcall.id);
calls.push(this.webrtc.api.id);
}
}
console.log("Calls in conference: ", calls);
this.webrtc.api.sendConference(this.id, calls);
};
PeerConference.prototype.applyUpdate = function(ids) {
console.log("Applying conference update", this.id, ids);
var myid = this.webrtc.api.id;
_.each(ids, _.bind(function(id) {
var res = myid < id ? -1 : myid > id ? 1 : 0;
console.log("Considering conference peers to call", res, id);
if (res === -1) {
this.doCall(id, true);
}
}, this));
};
PeerConference.prototype.peerIds = function() {
return _.keys(this.calls);
};
return PeerConference;
});