From 2e5188031579321a75845f98e19f47f352501993 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 20 Jun 2016 09:19:30 +0200 Subject: [PATCH 1/4] Don't show "failed" error if connection was established before. --- static/js/controllers/uicontroller.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/js/controllers/uicontroller.js b/static/js/controllers/uicontroller.js index 40e70774..65af5ef7 100644 --- a/static/js/controllers/uicontroller.js +++ b/static/js/controllers/uicontroller.js @@ -638,8 +638,11 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web $scope.setConnectedStatus(); break; case "failed": + var wasConnected = !currentcall.closed; mediaStream.webrtc.doHangup("failed", currentcall.id); - alertify.dialog.alert(translation._("Peer connection failed. Check your settings.")); + if (!wasConnected) { + alertify.dialog.alert(translation._("Peer connection failed. Check your settings.")); + } break; } }); From ee1f73ba404e8d2ec479eb2d2302685a4a66e34d Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 20 Jun 2016 09:20:13 +0200 Subject: [PATCH 2/4] Don't add dummy remote streams if call is closed. --- static/js/directives/audiovideo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/directives/audiovideo.js b/static/js/directives/audiovideo.js index daff2b3d..468391d0 100644 --- a/static/js/directives/audiovideo.js +++ b/static/js/directives/audiovideo.js @@ -354,7 +354,7 @@ define(['jquery', 'underscore', 'text!partials/audiovideo.html', 'text!partials/ mediaStream.webrtc.e.on("statechange", function(event, iceConnectionState, currentcall) { - if (!$scope.haveStreams) { + if (!$scope.haveStreams || currentcall.closed) { return; } From c8763deb58d75ad537fd94771185cb370e9c1833 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 20 Jun 2016 09:49:39 +0200 Subject: [PATCH 3/4] Try to recover from (some) lost p2p connection state. When the ICE connection state changes to "disconnected"/"failed", these calls are marked and get re-called for conferences once the connection is back and are allowed to send an "Offer" again. This works in cases where the complete connectivity is lost for one client while being in a conference once it comes back afterwards for him. Doesn't work reliably on Firefox as no "disconnected"/"failed" is triggered there. --- static/js/mediastream/peerconference.js | 36 ++++++++++++++++++++++--- static/js/mediastream/webrtc.js | 30 ++++++++++++++++++--- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/static/js/mediastream/peerconference.js b/static/js/mediastream/peerconference.js index 8f96065c..67b3d028 100644 --- a/static/js/mediastream/peerconference.js +++ b/static/js/mediastream/peerconference.js @@ -36,6 +36,9 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall this.callsCount = 0; this.callStates = {}; this.connectedCalls = {}; + // Ids of calls that "seem" to be disconnected (i.e. had a p2p state + // change of "disconnected" without a "Bye"). + this.disconnectedCalls = {}; this.conferenceMode = false; this.e = $({}); @@ -70,9 +73,13 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall }; PeerConference.prototype._addCallWithState = function(id, call, state) { - if (this.calls.hasOwnProperty(id)) { - console.warn("Already has a call for", id); - return false; + var oldcall = this.calls[id]; + if (oldcall) { + if (!this.disconnectedCalls[id]) { + console.warn("Already has a call for", id); + return false; + } + oldcall.close(); // This will remove the call from the conference. } this.calls[id] = call; @@ -101,6 +108,9 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall }; PeerConference.prototype.getCall = function(id) { + if (this.disconnectedCalls[id]) { + return null; + } return this.calls[id] || null; }; @@ -121,10 +131,23 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall delete this.calls[id]; delete this.callStates[id]; delete this.connectedCalls[id]; + delete this.disconnectedCalls[id]; this.callsCount -= 1; return call; }; + PeerConference.prototype.markDisconnected = function(id) { + this.disconnectedCalls[id] = true; + }; + + PeerConference.prototype.isDisconnected = function(id) { + return this.disconnectedCalls[id] || false; + }; + + PeerConference.prototype.getDisconnectedIds = function(id) { + return _.keys(this.disconnectedCalls); + }; + PeerConference.prototype.close = function() { var api = this.webrtc.api; @@ -161,13 +184,18 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall }; - PeerConference.prototype.pushUpdate = function() { + PeerConference.prototype.pushUpdate = function(forceAll) { if (this.webrtc.isConferenceRoom()) { // Conference is managed on the server. return; } var ids = _.keys(this.connectedCalls); + if (forceAll) { + // Include "disconnected" calls to try to recover from a previous + // lost connection. + ids = _.union(ids, this.getDisconnectedIds()); + } if (ids.length > 1) { ids.push(this.webrtc.api.id); console.log("Calls in conference:", ids); diff --git a/static/js/mediastream/webrtc.js b/static/js/mediastream/webrtc.js index 93bd0896..4cfe54b0 100644 --- a/static/js/mediastream/webrtc.js +++ b/static/js/mediastream/webrtc.js @@ -161,6 +161,17 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u }; WebRTC.prototype.receivedRoom = function(event, room) { + if (this.currentroom && room && this.currentroom.Name == room.Name) { + // No room change, usually happens on reconnect. + var ids = this.conference.getDisconnectedIds(); + if (ids.length > 1 && this.conference.id) { + // User was in a conference before, try to re-establish. + console.log("Re-establishing conference", this.conference.id, ids); + this.conference.pushUpdate(true); + } + return; + } + if (this.isConferenceRoom()) { // Switching from a conference room closes all current connections. _.defer(_.bind(function() { @@ -288,7 +299,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u // Clean own internal data before feeding into browser. delete data._conference; autoaccept = true; - } else if (this.conference.hasCalls()) { + } else if (this.conference.hasCalls() && !this.conference.isDisconnected(from)) { // 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"); @@ -360,8 +371,11 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u 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) { + // Peer-to-peer call will be upgraded to conference. Only is allowed + // if currently active call is in the list of conference participants + // and the "Conference" is received from him. Upgrading is always + // allowed for server-managed conference rooms. + if ((from !== ids[0] || data.indexOf(ids[0]) === -1) && !this.isConferenceRoom()) { console.warn("Received Conference for unknown call -> ignore.", to, data); return; } @@ -450,6 +464,14 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u call.e.on("closed", _.bind(function() { this.conference.removeCall(id); }, this)); + call.e.on("connectionStateChange", _.bind(function(event, state, currentcall) { + switch (state) { + case "disconnected": + case "failed": + this.conference.markDisconnected(currentcall.id); + break; + } + }, this)); return call; }; @@ -755,6 +777,8 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u }, this)); this.stop(); } else if (calls.length === 1) { + // Downgraded to peer-to-peer again. + this.conference.id = null; this.e.triggerHandler("peerconference", [null]); this.e.triggerHandler("peercall", [calls[0]]); } From 6ff9ee6b47c99cf53545f907763efe821b9394bd Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 20 Jun 2016 10:13:17 +0200 Subject: [PATCH 4/4] Fix moving between conference rooms. --- static/js/mediastream/webrtc.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/static/js/mediastream/webrtc.js b/static/js/mediastream/webrtc.js index 4cfe54b0..0d976d7f 100644 --- a/static/js/mediastream/webrtc.js +++ b/static/js/mediastream/webrtc.js @@ -95,6 +95,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u this.msgQueues = {}; this.usermediaReady = false; this.pendingMediaCalls = []; + this.pendingMessages = []; this.usermedia = null; this.audioMute = false; @@ -174,6 +175,16 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u if (this.isConferenceRoom()) { // Switching from a conference room closes all current connections. + this.leavingConference = true; + this.e.one("stop", _.bind(function() { + _.defer(_.bind(function() { + this.leavingConference = false; + while (this.pendingMessages.length) { + var args = this.pendingMessages.shift(); + this.processReceivedMessage.apply(this, args); + } + }, this)); + }, this)); _.defer(_.bind(function() { this.doHangup(); }, this)); @@ -220,6 +231,13 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u return; } + if (this.leavingConference) { + // Defer evaluating of messages until the previous conference room + // has been left. + this.pendingMessages.push([to, data, type, to2, from]); + return; + } + this.processReceivedMessage(to, data, type, to2, from); };