Browse Source

Merge pull request #293 from fancycode/conference_refactoring

Major refactoring of call / conference handling.
pull/295/head
Joachim Bauch 9 years ago committed by GitHub
parent
commit
c6354b8a14
  1. 10
      static/js/controllers/statusmessagecontroller.js
  2. 30
      static/js/controllers/uicontroller.js
  3. 7
      static/js/directives/buddylist.js
  4. 33
      static/js/mediastream/peercall.js
  5. 250
      static/js/mediastream/peerconference.js
  6. 6
      static/js/mediastream/peerconnection.js
  7. 712
      static/js/mediastream/webrtc.js

10
static/js/controllers/statusmessagecontroller.js

@ -25,8 +25,8 @@ define([], function() { @@ -25,8 +25,8 @@ define([], function() {
// StatusmessageController
return ["$scope", "mediaStream", function($scope, mediaStream) {
$scope.doHangup = function() {
mediaStream.webrtc.doHangup();
$scope.doHangup = function(reason, id) {
mediaStream.webrtc.doHangup(reason, id);
}
$scope.doAbort = function() {
mediaStream.webrtc.doHangup("abort", $scope.dialing);
@ -35,10 +35,10 @@ define([], function() { @@ -35,10 +35,10 @@ define([], function() {
mediaStream.connector.reconnect();
}
$scope.doAccept = function() {
mediaStream.webrtc.doAccept();
mediaStream.webrtc.doAccept($scope.incoming);
}
$scope.doReject = function() {
mediaStream.webrtc.doHangup('reject');
$scope.doReject = function(id) {
mediaStream.webrtc.doHangup('reject', id, $scope.incoming);
}
}];

30
static/js/controllers/uicontroller.js

@ -206,10 +206,11 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -206,10 +206,11 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
$scope.updatePeerFromConference = function() {
if (!$scope.conferenceObject) {
$scope.conferencePeers.length = 0;
return;
}
var peerIds = $scope.conferenceObject.peerIds();
var peerIds = $scope.conferenceObject.getCallIds();
if ($scope.peer && peerIds.indexOf($scope.peer) === -1) {
$scope.peer = null;
}
@ -229,7 +230,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -229,7 +230,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
return;
}
if ($scope.conference) {
if ($scope.conference || $scope.isConferenceRoom()) {
$scope.setStatus("conference");
} else {
$scope.setStatus("connected");
@ -473,7 +474,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -473,7 +474,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
$timeout.cancel(pickupTimeout);
pickupTimeout = null;
// Kill ringer.
if (peercall && peercall.from === null) {
if (peercall && peercall.isOutgoing()) {
dialerEnabled = true;
} else {
dialerEnabled = false;
@ -485,7 +486,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -485,7 +486,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
}
// Apply peer call to scope.
safeApply($scope, function(scope) {
// NOTE: the internal call will have a "id" of "null".
scope.peer = peercall ? peercall.id : null;
scope.setConnectedStatus();
});
@ -497,20 +497,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -497,20 +497,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
scope.conferenceObject = peerconference ? peerconference : null;
scope.updatePeerFromConference();
scope.setConnectedStatus();
if (!peerconference) {
scope.peer = null;
if (scope.usermedia) {
$timeout(function() {
scope.usermedia = null;
mediaStream.webrtc.stop();
if (mediaStream.webrtc.isConferenceRoom()) {
mediaStream.webrtc.doUserMediaWithInternalCall();
}
$scope.layout.buddylist = true;
$scope.layout.buddylistAutoHide = false;
}, 0);
}
}
});
});
@ -520,7 +506,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -520,7 +506,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
});
if ($scope.updateAutoAccept(null, from)) {
// Auto accept support.
mediaStream.webrtc.doAccept();
mediaStream.webrtc.doAccept(from);
return;
}
// Start to ring.
@ -533,7 +519,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -533,7 +519,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
// Start accept timeout.
pickupTimeout = $timeout(function() {
console.log("Pickup timeout reached.");
mediaStream.webrtc.doHangup("pickuptimeout");
mediaStream.webrtc.doHangup("pickuptimeout", from);
$scope.$emit("notification", "incomingpickuptimeout", {
reason: 'pickuptimeout',
from: from
@ -631,10 +617,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -631,10 +617,6 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
mediaStream.webrtc.e.on("waitforusermedia connecting", function(event, currentcall) {
var t = event.type;
if (currentcall && currentcall.isinternal && t === "connecting") {
// Don't show "Calling Someone" for the internal call.
return;
}
safeApply($scope, function(scope) {
scope.dialing = currentcall ? currentcall.id : null;
scope.setStatus(t);

7
static/js/directives/buddylist.js

@ -56,6 +56,13 @@ define(['underscore', 'text!partials/buddylist.html'], function(_, template) { @@ -56,6 +56,13 @@ define(['underscore', 'text!partials/buddylist.html'], function(_, template) {
$scope.$apply(updateBuddyListVisibility);
});
$scope.$watch("peer", function() {
if ($scope.peer) {
// Also reset the buddylist if the peer is cleared after the "done" event.
updateBuddyListVisibility();
}
});
$scope.$on("room.joined", function(ev) {
inRoom = true;
updateBuddyListVisibility();

33
static/js/mediastream/peercall.js

@ -38,6 +38,7 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -38,6 +38,7 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
this.offerOptions = $.extend(true, {}, this.webrtc.settings.offerOptions);
this.peerconnection = null;
this.pendingCandidates = [];
this.datachannels = {};
this.streams = {};
@ -47,6 +48,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -47,6 +48,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
};
PeerCall.prototype.isOutgoing = function() {
return !!this.from;
};
PeerCall.prototype.setInitiate = function(initiate) {
this.initiate = !! initiate;
//console.log("Set initiate", this.initiate, this);
@ -74,6 +79,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -74,6 +79,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
// TODO(longsleep): Check if this can happen?
error_cb(peerconnection);
}
while (this.pendingCandidates.length > 0) {
var candidate = this.pendingCandidates.shift();
this.addIceCandidate(candidate);
}
return peerconnection;
};
@ -96,6 +105,12 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -96,6 +105,12 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
PeerCall.prototype.onCreateAnswerOffer = function(cb, sessionDescription) {
if (sessionDescription.type === "answer") {
// We processed the incoming Offer by creating an answer, so it's safe
// to create own Offers to perform renegotiation.
this.peerconnection.setReadyForRenegotiation(true);
}
this.setLocalSdp(sessionDescription);
// Convert to object to allow custom property injection.
@ -130,6 +145,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -130,6 +145,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
console.error("Failed to create answer/offer", event);
// Even though the Offer/Answer could not be created, we now allow
// to create own Offers to perform renegotiation again.
this.peerconnection.setReadyForRenegotiation(true);
};
PeerCall.prototype.setRemoteDescription = function(sessionDescription, cb) {
@ -142,6 +161,12 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -142,6 +161,12 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
this.setRemoteSdp(sessionDescription);
if (sessionDescription.type === "offer") {
// Prevent creation of Offer messages to renegotiate streams while the
// remote Offer is being processed.
peerconnection.setReadyForRenegotiation(false);
}
peerconnection.setRemoteDescription(sessionDescription, _.bind(function() {
console.log("Set remote session description.", sessionDescription, this);
if (cb) {
@ -255,6 +280,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -255,6 +280,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
};
PeerCall.prototype.onNegotiationNeeded = function() {
if (!this.peerconnection.readyForRenegotiation) {
console.log("PeerConnection is not ready for renegotiation yet", this);
return;
}
if (!this.negotiationNeeded) {
this.negotiationNeeded = true;
@ -270,6 +299,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection @@ -270,6 +299,10 @@ define(['jquery', 'underscore', 'mediastream/utils', 'mediastream/peerconnection
// Avoid errors when still receiving candidates but closed.
return;
}
if (!this.peerconnection) {
this.pendingCandidates.push(candidate);
return;
}
this.peerconnection.addIceCandidate(candidate, function() {
//console.log("Remote candidate added successfully.", candidate);
}, function(error) {

250
static/js/mediastream/peerconference.js

@ -22,180 +22,111 @@ @@ -22,180 +22,111 @@
"use strict";
define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall) {
//NOTE(longsleep): This id should be changed to something undeterministic.
var conferences = 0;
var PeerConference = function(webrtc, currentcall, id) {
var STATE_ACTIVE = "active";
var STATE_INCOMING = "incoming";
var STATE_OUTGOING = "outgoing";
var PeerConference = function(webrtc) {
this.webrtc = webrtc;
this.currentcall = currentcall;
this.calls = {};
this.callsIn = {};
this.callsCount = 0;
this.callStates = {};
this.connectedCalls = {};
this.conferenceMode = false;
this.e = $({});
this.id = null;
if (!id) {
this.id = webrtc.api.id + "_" + (++conferences);
} else {
this.id = id;
}
if (!webrtc.usermedia) {
// Conference was started without getUM being called before. This
// happens for server-manager conference rooms. Create internal
// dummy call to trigger getUM, so actual conference calls can
// be established.
webrtc.doUserMediaWithInternalCall();
}
this.usermedia = webrtc.usermedia;
webrtc.e.on("usermedia", _.bind(function(event, um) {
console.log("Conference user media changed", um);
this.usermedia = um;
// Send conference updates to the other peers once we get a new connection.
webrtc.e.on("statechange", _.bind(function(event, iceConnectionState, currentcall) {
this.onConnectionStateChange(iceConnectionState, currentcall);
}, this));
};
console.log("Created conference", this.id);
// Creates a new unique random id to be used as conference id.
PeerConference.prototype._createConferenceId = function() {
return this.webrtc.api.id + "_" + (++conferences) + "_" + Math.round(Math.random() * 1e16);
};
PeerConference.prototype.checkEmpty = function() {
if (!_.isEmpty(this.calls) || (this.currentcall && this.currentcall.id)) {
return false;
PeerConference.prototype.getOrCreateId = function() {
if (!this.id) {
this.id = this._createConferenceId();
console.log("Created new conference id", this.id);
}
console.log("Conference is now empty -> cleaning up.");
this.e.triggerHandler("finished");
return true;
return 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);
this.checkEmpty();
}, 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.hasCalls = function() {
return this.callsCount > 0;
};
PeerConference.prototype.doCall = function(id, autocall) {
if ((this.currentcall && 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));
// Return number of currently active and pending calls.
PeerConference.prototype.getCallsCount = function() {
return this.callsCount;
};
if (!autocall) {
this.webrtc.e.triggerHandler("connecting", [call]);
PeerConference.prototype._addCallWithState = function(id, call, state) {
if (this.calls.hasOwnProperty(id)) {
console.warn("Already has a call for", id);
return false;
}
console.log("Creating PeerConnection", call);
call.createPeerConnection(_.bind(function(peerconnection) {
// Success call.
if (this.usermedia) {
this.usermedia.addToPeerConnection(peerconnection);
}
call.e.on("negotiationNeeded", _.bind(function(event, extracall) {
this.webrtc.sendOfferWhenNegotiationNeeded(extracall);
}, this));
}, this), _.bind(function() {
// Error call.
console.error("Failed to create peer connection for conference call.");
}, this));
this.calls[id] = call;
this.callStates[id] = state;
this.callsCount += 1;
return true;
};
PeerConference.prototype.addIncoming = function(from, call) {
return this._addCallWithState(from, call, STATE_INCOMING);
};
PeerConference.prototype.callClosed = function(call) {
if (_.isEmpty(this.callsIn)) {
// No more calls in the conference
return null;
}
PeerConference.prototype.addOutgoing = function(to, call) {
return this._addCallWithState(to, call, STATE_OUTGOING);
};
if (call !== this.currentcall) {
// An arbitrary call of the conference hung up.
delete this.calls[call.id];
delete this.callsIn[call.id];
console.log("Conference call closed", call);
} else {
// The "initiator" call of the conference hung up, promote another
// call to "initator" and return it.
var calls = _.keys(this.callsIn);
var id = calls[0];
this.currentcall = this.calls[id];
delete this.calls[id];
delete this.callsIn[id];
console.log("Handed over conference to", id, this.currentcall);
PeerConference.prototype._setCallState = function(id, state) {
if (this.callStates.hasOwnProperty(id)) {
this.callStates[id] = state;
console.log("Call state changed", id, state);
}
return this.currentcall;
};
PeerConference.prototype.autoAnswer = function(from, rtcsdp) {
if ((this.currentcall && from === this.currentcall.id) || this.calls.hasOwnProperty(from)) {
console.warn("Already got a call to this id (autoAnswer)", from, this.calls);
return;
}
PeerConference.prototype.setCallActive = function(id) {
this._setCallState(id, STATE_ACTIVE);
};
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.usermedia) {
this.usermedia.addToPeerConnection(peerconnection);
}
call.e.on("negotiationNeeded", _.bind(function(event, extracall) {
this.webrtc.sendOfferWhenNegotiationNeeded(extracall);
}, this));
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) {
return this.calls[id] || null;
};
PeerConference.prototype.getCalls = function() {
return _.values(this.calls);
};
PeerConference.prototype.getCall = function(id) {
PeerConference.prototype.getCallIds = function() {
return _.keys(this.calls);
};
var call = this.calls[id];
if (!call) {
call = null;
PeerConference.prototype.removeCall = function(id) {
if (!this.calls.hasOwnProperty(id)) {
return null;
}
var call = this.calls[id];
delete this.calls[id];
delete this.callStates[id];
delete this.connectedCalls[id];
this.callsCount -= 1;
return call;
};
PeerConference.prototype.close = function() {
this.currentcall = null;
var api = this.webrtc.api;
_.each(this.calls, function(c) {
c.close();
@ -205,6 +136,10 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall @@ -205,6 +136,10 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall
}
});
this.calls = {};
this.callStates = {};
this.connectedCalls = {};
this.callsCount = 0;
this.id = null;
};
@ -214,8 +149,8 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall @@ -214,8 +149,8 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall
switch (iceConnectionState) {
case "completed":
case "connected":
if (!this.callsIn.hasOwnProperty(currentcall.id)) {
this.callsIn[currentcall.id] = true;
if (!this.connectedCalls.hasOwnProperty(currentcall.id)) {
this.connectedCalls[currentcall.id] = true;
this.pushUpdate();
}
break;
@ -223,7 +158,6 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall @@ -223,7 +158,6 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall
console.warn("Conference peer connection state failed", currentcall);
break;
}
this.webrtc.onConnectionStateChange(iceConnectionState, currentcall);
};
@ -233,44 +167,16 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall @@ -233,44 +167,16 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall
return;
}
var calls = _.keys(this.callsIn);
if (calls) {
if (this.currentcall) {
calls.push(this.currentcall.id);
calls.push(this.webrtc.api.id);
}
var ids = _.keys(this.connectedCalls);
if (ids.length > 1) {
ids.push(this.webrtc.api.id);
console.log("Calls in conference:", ids);
this.webrtc.api.sendConference(this.getOrCreateId(), ids);
}
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() {
var result = _.keys(this.calls);
// "peerIds" returns the session ids of all participants in the
// conference, so we need to add the id of the peer the user called
// manually before migrating to a conference (but only if it has an id,
// i.e. is not an internal call object).
if (this.currentcall && this.currentcall.id && result.indexOf(this.currentcall.id) === -1) {
result.push(this.currentcall.id);
}
return result;
return this.getCallIds();
};
return PeerConference;

6
static/js/mediastream/peerconnection.js

@ -33,6 +33,7 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) { @@ -33,6 +33,7 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) {
this.pc = null;
this.datachannel = null;
this.datachannelReady = false;
this.readyForRenegotiation = true;
if (currentcall) {
this.createPeerConnection(currentcall);
@ -40,6 +41,10 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) { @@ -40,6 +41,10 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) {
};
PeerConnection.prototype.setReadyForRenegotiation = function(ready) {
this.readyForRenegotiation = !!ready;
};
PeerConnection.prototype.createPeerConnection = function(currentcall) {
// XXX(longsleep): This function is a mess.
@ -318,7 +323,6 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) { @@ -318,7 +323,6 @@ define(['jquery', 'underscore', 'webrtc.adapter'], function($, _) {
};
PeerConnection.prototype.createAnswer = function() {
return this.pc.createAnswer.apply(this.pc, arguments);
};

712
static/js/mediastream/webrtc.js

@ -56,6 +56,9 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -56,6 +56,9 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
InternalPC.prototype.addStream = function() {
};
InternalPC.prototype.removeStream = function() {
};
InternalPC.prototype.negotiationNeeded = function() {
};
@ -87,13 +90,11 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -87,13 +90,11 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
this.e = $({});
this.currentcall = null;
this.currentconference = null;
this.conference = new PeerConference(this);
this.currentroom = null;
this.msgQueue = [];
this.started = false;
this.initiator = null;
this.msgQueues = {};
this.usermediaReady = false;
this.pendingMediaCalls = [];
this.usermedia = null;
this.audioMute = false;
@ -160,22 +161,39 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -160,22 +161,39 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
};
WebRTC.prototype.receivedRoom = function(event, room) {
this.currentroom = room;
if (this.isConferenceRoom()) {
if (!this.usermedia) {
this.doUserMediaWithInternalCall();
}
} else {
if (this.currentcall && this.currentcall.isinternal) {
this.stop();
}
// Switching from a conference room closes all current connections.
_.defer(_.bind(function() {
this.doHangup();
}, this));
}
console.log("Joined room", room, this.api.id);
this.currentroom = room;
_.defer(_.bind(function() {
this.maybeStartLocalVideo();
}, this), 100);
};
WebRTC.prototype.isConferenceRoom = function() {
return this.currentroom && this.currentroom.Type === roomTypeConference;
};
WebRTC.prototype.maybeStartLocalVideo = function() {
if (!this.isConferenceRoom()) {
return;
}
console.log("Start local video");
var call = new InternalCall(this);
this._doCallUserMedia(call);
};
WebRTC.prototype.stopLocalVideo = function() {
if (this.usermedia) {
this.usermedia.stop();
}
};
WebRTC.prototype.processReceived = function(event, to, data, type, to2, from) {
//console.log(">>>>>>>>>>>>", type, from, data, to, to2);
@ -191,227 +209,210 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -191,227 +209,210 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
return;
}
if (!this.initiator && !this.started) {
switch (type) {
case "Offer":
if (this.currentcall && !this.currentcall.isinternal) {
console.warn("Received Offer while not started and with current call -> busy.", from);
this.api.sendBye(from, "busy");
this.e.triggerHandler("busy", [from, to2, to]);
return;
}
this.msgQueue.unshift([to, data, type, to2, from]);
if (this.currentcall && this.currentcall.isinternal) {
// Internal getUM is currently in progress, defer
// evaluation of "Offer" until that is completed.
return;
}
// Create call.
this.currentcall = this.createCall(from, from, from);
// Delegate next steps to UI.
this.e.triggerHandler("offer", [from, to2, to]);
break;
case "Bye":
if (!this.currentcall) {
console.warn("Received Bye while without currentcall -> ignore.", from);
return;
}
if (this.currentcall.from !== from) {
console.warn("Received Bye from another id -> ignore.", from);
return;
}
console.log("Bye process (started false)");
this.doHangup("receivedbye", from);
// Delegate bye to UI.
this.e.triggerHandler("bye", [data.Reason, from, to, to2]);
break;
case "Conference":
// No existing call yet, only supported for server-managed
// conference.
if (!this.isConferenceRoom()) {
console.warn("Received Conference outside call for invalid room type.");
return;
}
this.processReceivedMessage(to, data, type, to2, from);
break;
default:
this.msgQueue.push([to, data, type, to2, from]);
break;
}
} else {
this.processReceivedMessage(to, data, type, to2, from);
}
this.processReceivedMessage(to, data, type, to2, from);
};
WebRTC.prototype.findTargetCall = function(id) {
return this.conference.getCall(id);
};
var targetcall = null;
if (this.currentcall) {
do {
if (this.initiator && this.currentcall.to === id) {
targetcall = this.currentcall;
break;
}
if (!this.initiator && this.currentcall.from === id) {
targetcall = this.currentcall;
break;
}
if (this.currentcall.id === id) {
targetcall = this.currentcall;
break;
}
if (this.currentconference) {
targetcall = this.currentconference.getCall(id)
}
} while (false);
WebRTC.prototype.callForEachCall = function(fn) {
var calls = this.conference.getCalls();
if (!calls.length) {
return 0;
}
return targetcall;
_.map(calls, fn);
return calls.length;
};
WebRTC.prototype._getMessageQueue = function(id, create) {
var queue = this.msgQueues[id] || null;
if (queue === null && create) {
queue = this.msgQueues[id] = [];
}
return queue;
};
WebRTC.prototype.callForEachCall = function(fn) {
WebRTC.prototype.pushBackMessage = function(id, message) {
this._getMessageQueue(id, true).push(message);
};
var count = 0;
if (this.currentcall) {
fn(this.currentcall, count);
count++;
if (this.currentconference) {
_.each(this.currentconference.calls, function(v, count) {
fn(v);
count++;
});
WebRTC.prototype.pushFrontMessage = function(id, message) {
this._getMessageQueue(id, true).unshift(message);
};
WebRTC.prototype.popFrontMessage = function(id) {
var queue = this._getMessageQueue(id);
if (!queue) {
return null;
}
var message = queue.shift();
if (!queue.length) {
delete this.msgQueues[id];
}
return message;
};
WebRTC.prototype._processOffer = function(to, data, type, to2, from) {
console.log("Offer process.");
var call = this.conference.getCall(from);
if (call) {
// Remote peer is trying to renegotiate media.
if (!this.settings.renegotiation && call.peerconnection && call.peerconnection.hasRemoteDescription()) {
// Call replace support without renegotiation.
this.doHangup("unsupported", from);
console.error("Processing new offers is not implemented without renegotiation.");
return;
}
call.setRemoteDescription(new window.RTCSessionDescription(data), _.bind(function(sessionDescription, currentcall) {
this.e.triggerHandler("peercall", [currentcall]);
currentcall.createAnswer(_.bind(function(sessionDescription, currentcall) {
console.log("Sending answer", sessionDescription, currentcall.id);
this.api.sendAnswer(currentcall.id, sessionDescription);
}, this));
}, this));
return;
}
var autoaccept = false;
if (data._conference) {
if (this.conference.id !== data._conference) {
console.warn("Received Offer for unknown conference -> busy.", from);
this.api.sendBye(from, "busy");
this.e.triggerHandler("busy", [from, to2, to]);
return;
}
console.log("Received conference Offer -> auto.", from, data._conference);
// Clean own internal data before feeding into browser.
delete data._conference;
autoaccept = true;
} else if (this.conference.hasCalls()) {
// TODO(fancycode): support joining callers to currently active conference.
console.warn("Received Offer while already in a call -> busy.", from);
this.api.sendBye(from, "busy");
this.e.triggerHandler("busy", [from, to2, to]);
return;
}
call = this.createCall(from, this.api.id, from);
if (!this.conference.addIncoming(from, call)) {
console.warn("Already got a call, not processing Offer", from, autoaccept);
return;
}
this.pushFrontMessage(from, [to, data, type, to2, from]);
if (autoaccept) {
if (!this.doAccept(call, true)) {
this.popFrontMessage(from);
}
return;
}
return count;
// Delegate next steps to UI.
this.e.triggerHandler("offer", [from, to2, to]);
};
WebRTC.prototype.processReceivedMessage = function(to, data, type, to2, from) {
WebRTC.prototype._processCandidate = function(to, data, type, to2, from) {
var call = this.conference.getCall(from);
if (!call) {
console.warn("Received Candidate for unknown id -> ignore.", from);
return;
}
var candidate = new window.RTCIceCandidate({
sdpMLineIndex: data.sdpMLineIndex,
sdpMid: data.sdpMid,
candidate: data.candidate
});
call.addIceCandidate(candidate);
//console.log("Got candidate", data.sdpMid, data.sdpMLineIndex, data.candidate);
};
if (!this.started && type !== "Conference") {
console.log('PeerConnection has not been created yet!');
WebRTC.prototype._processAnswer = function(to, data, type, to2, from) {
var call = this.conference.getCall(from);
if (!call) {
console.warn("Received Answer from unknown id -> ignore", from);
return;
}
var targetcall;
console.log("Answer process.");
this.conference.setCallActive(call.id);
// TODO(longsleep): In case of negotiation this could switch offer and answer
// and result in a offer sdp sent as answer data. We need to handle this.
call.setRemoteDescription(new window.RTCSessionDescription(data), function() {
// Received remote description as answer.
console.log("Received answer after we sent offer", data);
});
};
WebRTC.prototype._processBye = function(to, data, type, to2, from) {
console.log("Bye process.");
this.doHangup("receivedbye", from);
// Delegate bye to UI.
this.e.triggerHandler("bye", [data.Reason, from, to, to2]);
};
WebRTC.prototype._processConference = function(to, data, type, to2, from) {
var ids = this.conference.getCallIds();
if (!ids.length && !this.isConferenceRoom()) {
console.warn("Received Conference for unknown call -> ignore.", to, data);
return;
} else if (ids.length == 1) {
// Peer-to-peer call will be upgraded to conference.
if (data.indexOf(ids[0]) === -1) {
console.warn("Received Conference for unknown call -> ignore.", to, data);
return;
}
this.conference.id = to;
} else if (this.conference.id && this.conference.id !== to) {
console.warn("Received Conference for wrong id -> ignore.", to, this.conference);
return;
}
if (!this.conference.id) {
if (!this.isConferenceRoom()) {
console.warn("Received initial Conference for non-conference room -> ignore.", to, this.conference);
return;
}
this.conference.id = to;
console.log("Setting received conference id", to);
}
console.log("Applying conference update", data);
var myid = this.api.id;
_.each(data, _.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));
this.e.triggerHandler("peerconference", [this.conference]);
};
WebRTC.prototype.processReceivedMessage = function(to, data, type, to2, from) {
switch (type) {
case "Offer":
console.log("Offer process.");
targetcall = this.findTargetCall(from);
if (targetcall) {
if (!this.settings.renegotiation && targetcall.peerconnection && targetcall.peerconnection.hasRemoteDescription()) {
// Call replace support without renegotiation.
this.doHangup("unsupported", from);
console.error("Processing new offers is not implemented without renegotiation.");
return;
}
// Hey we know this call.
targetcall.setRemoteDescription(new window.RTCSessionDescription(data), _.bind(function(sessionDescription, currentcall) {
if (currentcall === this.currentcall) {
// Main call.
this.e.triggerHandler("peercall", [this.currentcall]);
}
currentcall.createAnswer(_.bind(function(sessionDescription, currentcall) {
console.log("Sending answer", sessionDescription, currentcall.id);
this.api.sendAnswer(currentcall.id, sessionDescription);
}, this));
}, this));
} else {
// No target call. Check conference auto answer support.
if (this.currentconference && this.currentconference.id === data._conference) {
console.log("Received conference Offer -> auto.", from, data._conference);
// Clean own internal data before feeding into browser.
delete data._conference;
this.currentconference.autoAnswer(from, new window.RTCSessionDescription(data));
break;
}
// Cannot do anything with this offer, reply with busy.
console.log("Received Offer from unknown id -> busy.", from);
this.api.sendBye(from, "busy");
this.e.triggerHandler("busy", [from, to2, to]);
}
this._processOffer(to, data, type, to2, from);
break;
case "Candidate":
targetcall = this.findTargetCall(from);
if (!targetcall) {
console.warn("Received Candidate for unknown id -> ignore.", from);
return;
}
var candidate = new window.RTCIceCandidate({
sdpMLineIndex: data.sdpMLineIndex,
sdpMid: data.sdpMid,
candidate: data.candidate
});
targetcall.addIceCandidate(candidate);
//console.log("Got candidate", data.sdpMid, data.sdpMLineIndex, data.candidate);
this._processCandidate(to, data, type, to2, from);
break;
case "Answer":
targetcall = this.findTargetCall(from);
if (!targetcall) {
console.warn("Received Answer from unknown id -> ignore", from);
return;
}
console.log("Answer process.");
// TODO(longsleep): In case of negotiation this could switch offer and answer
// and result in a offer sdp sent as answer data. We need to handle this.
targetcall.setRemoteDescription(new window.RTCSessionDescription(data), function() {
// Received remote description as answer.
console.log("Received answer after we sent offer", data);
});
this._processAnswer(to, data, type, to2, from);
break;
case "Bye":
targetcall = this.findTargetCall(from);
if (!targetcall) {
console.warn("Received Bye from unknown id -> ignore.", from);
return;
}
console.log("Bye process.");
if (this.currentconference) {
// Hand over current call to next conference call.
var newcurrentcall = this.currentconference.callClosed(targetcall);
targetcall.close()
if (newcurrentcall && newcurrentcall != this.currentcall) {
this.currentcall = newcurrentcall;
this.e.triggerHandler("peercall", [newcurrentcall]);
} else if (!newcurrentcall) {
this.doHangup("receivedbye", targetcall.id);
}
if (this.currentconference && !this.currentconference.checkEmpty()) {
this.e.triggerHandler("peerconference", [this.currentconference]);
}
} else {
this.doHangup("receivedbye", targetcall.id);
}
this.e.triggerHandler("bye", [data.Reason, from, to, to2]);
this._processBye(to, data, type, to2, from);
break;
case "Conference":
if ((!this.currentcall || data.indexOf(this.currentcall.id) === -1) && !this.isConferenceRoom()) {
console.warn("Received Conference for unknown call -> ignore.", to, data);
return;
} else {
var currentconference = this.currentconference;
if (!currentconference) {
currentconference = this.currentconference = new PeerConference(this, this.currentcall, to);
currentconference.e.one("finished", _.bind(function() {
this.currentconference = null;
this.e.triggerHandler("peerconference", [null]);
}, this));
} else {
if (currentconference.id !== to) {
console.warn("Received Conference for wrong id -> ignore.", to, currentconference);
return;
}
}
currentconference.applyUpdate(data);
this.e.triggerHandler("peerconference", [currentconference]);
}
this._processConference(to, data, type, to2, from);
break;
default:
console.log("Unhandled message type", type, data);
break;
}
};
WebRTC.prototype.testMediaAccess = function(cb) {
@ -419,50 +420,46 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -419,50 +420,46 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
var success = function(stream) {
console.info("testMediaAccess success");
cb(true);
}
};
var failed = function() {
console.info("testMediaAccess failed");
cb(false);
}
};
UserMedia.testGetUserMedia(success, failed);
};
WebRTC.prototype.createCall = function(id, from, to) {
var currentcall = new PeerCall(this, id, from, to);
currentcall.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentcall) {
var call = new PeerCall(this, id, from, to);
call.e.on("connectionStateChange", _.bind(function(event, iceConnectionState, currentcall) {
this.onConnectionStateChange(iceConnectionState, currentcall);
}, this));
currentcall.e.on("remoteStreamAdded", _.bind(function(event, stream, currentcall) {
call.e.on("remoteStreamAdded", _.bind(function(event, stream, currentcall) {
this.onRemoteStreamAdded(stream, currentcall);
}, this));
currentcall.e.on("remoteStreamRemoved", _.bind(function(event, stream, currentcall) {
call.e.on("remoteStreamRemoved", _.bind(function(event, stream, currentcall) {
this.onRemoteStreamRemoved(stream, currentcall);
}, this));
currentcall.e.on("error", _.bind(function(event, id, message) {
if (!id) {
id = "failed_peerconnection";
call.e.on("error", _.bind(function(event, error_id, message) {
if (!error_id) {
error_id = "failed_peerconnection";
}
this.e.triggerHandler("error", [message, id]);
_.defer(_.bind(this.doHangup, this), "error", currentcall.id); // Hangup on error is good yes??
this.e.triggerHandler("error", [message, error_id]);
_.defer(_.bind(this.doHangup, this), "error", id); // Hangup on error is good yes??
}, this));
return currentcall;
call.e.on("closed", _.bind(function() {
this.conference.removeCall(id);
}, this));
return call;
};
WebRTC.prototype.doUserMediaWithInternalCall = function() {
if (this.currentcall && !this.currentcall.isinternal) {
console.warn("Already have a current call, not doing internal getUM", this.currentcall);
return;
}
var currentcall = this.currentcall = new InternalCall(this);
this.e.triggerHandler("peercall", [currentcall]);
this.doUserMedia(currentcall);
};
WebRTC.prototype.doUserMedia = function(call) {
WebRTC.prototype.doUserMedia = function(currentcall) {
if (this.usermedia) {
// We should not create a new UserMedia object while the current one
// is still being used.
console.error("UserMedia already created, check caller");
}
// Create default media (audio/video).
var usermedia = new UserMedia({
@ -472,8 +469,12 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -472,8 +469,12 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
});
usermedia.e.on("mediasuccess mediaerror", _.bind(function(event, um) {
this.e.triggerHandler("usermedia", [um]);
this.usermediaReady = true;
// Start always, no matter what.
this.maybeStart(um);
while (this.pendingMediaCalls.length > 0) {
var c = this.pendingMediaCalls.shift();
this.maybeStart(um, c);
}
}, this));
usermedia.e.on("mediachanged", _.bind(function(event, um) {
// Propagate media change events.
@ -482,7 +483,9 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -482,7 +483,9 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
usermedia.e.on("stopped", _.bind(function(event, um) {
if (um === this.usermedia) {
this.e.triggerHandler("usermedia", [null]);
this.usermediaReady = false;
this.usermedia = null;
this.maybeStartLocalVideo();
}
}, this));
this.e.one("stop", function() {
@ -490,55 +493,99 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -490,55 +493,99 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
});
this.usermedia = usermedia;
this.e.triggerHandler("usermedia", [usermedia]);
this.pendingMediaCalls.push(call);
return usermedia.doGetUserMedia(currentcall);
return usermedia.doGetUserMedia(call);
};
WebRTC.prototype.doCall = function(id) {
if (this.currentcall) {
// Conference mode.
var currentconference = this.currentconference;
if (!currentconference) {
currentconference = this.currentconference = new PeerConference(this, this.currentcall);
currentconference.e.one("finished", _.bind(function() {
this.currentconference = null;
this.e.triggerHandler("peerconference", [null]);
}, this));
}
currentconference.doCall(id);
this.e.triggerHandler("peerconference", [currentconference]);
} else {
var currentcall = this.currentcall = this.createCall(id, null, id);
this.e.triggerHandler("peercall", [currentcall]);
var ok = this.doUserMedia(currentcall);
if (ok) {
this.e.triggerHandler("waitforusermedia", [currentcall]);
WebRTC.prototype._doCallUserMedia = function(call) {
if (this.usermedia) {
if (!this.usermediaReady) {
this.pendingMediaCalls.push(call);
} else {
this.e.triggerHandler("error", ["Failed to access camera/microphone.", "failed_getusermedia"]);
return this.doHangup();
this.maybeStart(this.usermedia, call);
}
this.initiator = true;
return true;
}
var ok = this.doUserMedia(call);
if (ok) {
this.e.triggerHandler("waitforusermedia", [call]);
return true;
}
this.e.triggerHandler("error", ["Failed to access camera/microphone.", "failed_getusermedia"]);
if (call.id) {
this.doHangup("usermedia", call.id);
}
return false;
};
WebRTC.prototype.doAccept = function() {
WebRTC.prototype._doAutoStartCall = function(call) {
if (!this.usermedia) {
return false;
}
//NOTE(longsleep): currentcall was created as early as possible to be able to process incoming candidates.
var currentcall = this.currentcall;
if (!currentcall) {
console.warn("Trying to accept without a call.", currentcall);
if (!this.usermediaReady) {
// getUserMedia is still pending, defer starting of call.
this.pendingMediaCalls.push(call);
} else {
this.maybeStart(this.usermedia, call, true);
}
return true;
};
WebRTC.prototype.doCall = function(id, autocall) {
var call = this.createCall(id, null, id);
call.setInitiate(true);
var count = this.conference.getCallsCount();
if (!this.conference.addOutgoing(id, call)) {
console.log("Already has a call with", id);
return;
}
var ok = this.doUserMedia(currentcall);
if (ok) {
this.e.triggerHandler("waitforusermedia", [currentcall]);
} else {
this.e.triggerHandler("error", ["Failed to access camera/microphone.", "failed_getusermedia"]);
return this.doHangup();
this.e.triggerHandler("peercall", [call]);
if (!autocall) {
this.e.triggerHandler("connecting", [call]);
}
if ((autocall && count > 0) || this.isConferenceRoom()) {
call.e.on("sessiondescription", _.bind(function(event, sessionDescription) {
var cid = this.conference.getOrCreateId();
console.log("Injected conference id into sessionDescription", cid);
sessionDescription._conference = cid;
}, this));
}
if (count > 0) {
if (count === 1) {
// Notify UI that it's a conference now.
this.e.triggerHandler("peerconference", [this.conference]);
}
if (this._doAutoStartCall(call)) {
return;
}
}
if (!this._doCallUserMedia(call)) {
return;
}
};
WebRTC.prototype.doAccept = function(call, autoanswer) {
if (typeof call === "string") {
var id = call;
call = this.conference.getCall(id);
if (!call) {
console.warn("Trying to accept unknown call.", id);
return false;
}
}
this.conference.setCallActive(call.id);
if (autoanswer && this._doAutoStartCall(call)) {
return true;
}
return this._doCallUserMedia(call);
};
WebRTC.prototype.doXfer = function(id, token, options) {
@ -670,99 +717,94 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u @@ -670,99 +717,94 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u
WebRTC.prototype.stop = function() {
if (this.currentconference) {
this.currentconference.close();
this.currentconference = null;
}
if (this.currentcall) {
this.currentcall.close();
this.currentcall = null;
}
this.conference.close();
this.e.triggerHandler("peerconference", [null]);
this.e.triggerHandler("peercall", [null]);
this.e.triggerHandler("stop");
this.msgQueue.length = 0;
this.initiator = null;
this.started = false;
this.msgQueues = {};
}
WebRTC.prototype.doHangup = function(reason, id) {
if (!id) {
console.log("Closing all calls")
_.each(this.conference.getCallIds(), _.bind(function(callid) {
this.doHangup(reason, callid);
}, this));
this.stop();
return true;
}
console.log("Hanging up.", id);
if (id) {
var currentcall = this.findTargetCall(id);
if (!currentcall) {
console.warn("Tried to hangup unknown call.", reason, id);
return;
}
if (currentcall !== this.currentcall) {
currentcall.close();
if (reason !== "receivedbye") {
this.api.sendBye(id, reason);
}
_.defer(_.bind(function() {
if (this.currentcall && currentcall) {
this.e.triggerHandler("statechange", ["connected", this.currentcall]);
} else {
this.e.triggerHandler("done", [reason]);
}
}, this));
return;
}
var call = this.conference.removeCall(id);
if (!call) {
console.warn("Tried to hangup unknown call.", reason, id);
return false;
}
call.close();
if (reason !== "receivedbye") {
this.api.sendBye(id, reason);
}
if (this.currentcall) {
id = this.currentcall.id;
var calls = this.conference.getCalls();
if (!calls.length) {
// Last peer disconnected, perform cleanup.
this.e.triggerHandler("peercall", [null]);
_.defer(_.bind(function() {
this.e.triggerHandler("done", [reason]);
}, this));
this.stop();
} else if (calls.length === 1) {
this.e.triggerHandler("peerconference", [null]);
this.e.triggerHandler("peercall", [calls[0]]);
}
this.stop();
if (id) {
if (reason !== "receivedbye") {
this.api.sendBye(id, reason);
}
}
return true;
}
WebRTC.prototype.maybeStart = function(usermedia) {
//console.log("maybeStart", this.started);
if (!this.started) {
var currentcall = this.currentcall;
currentcall.setInitiate(this.initiator);
this.e.triggerHandler("connecting", [currentcall]);
console.log('Creating PeerConnection.', currentcall);
currentcall.createPeerConnection(_.bind(function(peerconnection) {
// Success call.
usermedia.addToPeerConnection(peerconnection);
this.started = true;
if (!this.initiator) {
this.calleeStart();
}
currentcall.e.on("negotiationNeeded", _.bind(function(event, currentcall) {
this.sendOfferWhenNegotiationNeeded(currentcall);
}, this));
}, this), _.bind(function() {
// Error call.
this.e.triggerHandler("error", ["Failed to create peer connection. See log for details."]);
this.doHangup();
}, this));
WebRTC.prototype.maybeStart = function(usermedia, call, autocall) {
//console.log("maybeStart", call);
if (call.peerconnection) {
console.log("Already started", call);
return;
}
};
WebRTC.prototype.calleeStart = function() {
var args;
while (this.msgQueue.length > 0) {
args = this.msgQueue.shift();
this.processReceivedMessage.apply(this, args);
if (!autocall) {
if (!call.isinternal) {
this.e.triggerHandler("connecting", [call]);
} else if (!this.conference.hasCalls()) {
// Signal UI that media access has been granted.
this.e.triggerHandler("done");
}
}
console.log('Creating PeerConnection.', call);
call.createPeerConnection(_.bind(function(peerconnection) {
// Success call.
usermedia.addToPeerConnection(peerconnection);
if (!call.initiate) {
this.processPendingMessages(call.id);
}
call.e.on("negotiationNeeded", _.bind(function(event, call) {
this.sendOfferWhenNegotiationNeeded(call);
}, this));
}, this), _.bind(function() {
// Error call.
this.e.triggerHandler("error", ["Failed to create peer connection. See log for details."]);
if (call.id) {
this.doHangup("failed", call.id);
}
}, this));
};
WebRTC.prototype.processPendingMessages = function(id) {
do {
var message = this.popFrontMessage(id);
if (!message) {
break;
}
this.processReceivedMessage.apply(this, message);
} while (true);
};
WebRTC.prototype.sendOfferWhenNegotiationNeeded = function(currentcall, to) {

Loading…
Cancel
Save