diff --git a/static/js/controllers/uicontroller.js b/static/js/controllers/uicontroller.js index 865857e4..e46bbe17 100644 --- a/static/js/controllers/uicontroller.js +++ b/static/js/controllers/uicontroller.js @@ -165,6 +165,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web $scope.peer = null; $scope.dialing = null; $scope.conference = null; + $scope.conferenceObject = null; $scope.conferencePeers = []; $scope.incoming = null; $scope.microphoneMute = false; @@ -199,6 +200,38 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web localStatus.update(status); }; + $scope.updatePeerFromConference = function() { + if (!$scope.conferenceObject) { + return; + } + + var peerIds = $scope.conferenceObject.peerIds(); + if ($scope.peer && peerIds.indexOf($scope.peer) === -1) { + $scope.peer = null; + } + if (!$scope.peer) { + $scope.peer = peerIds.length > 0 ? peerIds.shift() : null; + } else { + peerIds = _.without(peerIds, $scope.peer); + } + $scope.conferencePeers = peerIds; + }; + + $scope.setConnectedStatus = function() { + // Don't set connected states if no peer is known yet. Otherwise + // there would be "Someone" visible in the UI. + $scope.updatePeerFromConference(); + if (!$scope.peer) { + return; + } + + if ($scope.conference) { + $scope.setStatus("conference"); + } else { + $scope.setStatus("connected"); + } + }; + $scope.refreshWebrtcSettings = function() { var settings = $scope.master.settings; // Refresh SDP params. @@ -440,14 +473,18 @@ 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(); }); }); mediaStream.webrtc.e.on("peerconference", function(event, peerconference) { safeApply($scope, function(scope) { scope.conference = peerconference ? peerconference.id : null; - scope.conferencePeers = peerconference ? peerconference.peerIds() : []; + scope.conferenceObject = peerconference ? peerconference : null; + scope.updatePeerFromConference(); + scope.setConnectedStatus(); }); }); @@ -568,6 +605,10 @@ 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); @@ -586,11 +627,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web /* falls through */ case "completed": case "connected": - if ($scope.conference) { - $scope.setStatus('conference'); - } else { - $scope.setStatus('connected'); - } + $scope.setConnectedStatus(); break; case "failed": mediaStream.webrtc.doHangup("failed", currentcall.id); @@ -607,11 +644,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web $scope.$on("active", function(event, currentcall) { console.info("Video state active (assuming connected)", currentcall.id); - if ($scope.conference) { - $scope.setStatus('conference'); - } else { - $scope.setStatus('connected'); - } + $scope.setConnectedStatus(); $timeout(function() { if ($scope.peer) { $scope.layout.buddylist = false; diff --git a/static/js/mediastream/peerconference.js b/static/js/mediastream/peerconference.js index 19f1272e..536f57fc 100644 --- a/static/js/mediastream/peerconference.js +++ b/static/js/mediastream/peerconference.js @@ -40,6 +40,14 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall 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); @@ -87,7 +95,7 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall PeerConference.prototype.doCall = function(id, autocall) { - if (id === this.currentcall.id || this.calls.hasOwnProperty(id)) { + 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; @@ -146,7 +154,7 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall PeerConference.prototype.autoAnswer = function(from, rtcsdp) { - if (from === this.currentcall.id || this.calls.hasOwnProperty(from)) { + 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; } @@ -220,6 +228,10 @@ define(['jquery', 'underscore', 'mediastream/peercall'], function($, _, PeerCall }; PeerConference.prototype.pushUpdate = function() { + if (this.webrtc.isConferenceRoom()) { + // Conference is managed on the server. + return; + } var calls = _.keys(this.callsIn); if (calls) { diff --git a/static/js/mediastream/webrtc.js b/static/js/mediastream/webrtc.js index c39f9fec..59c6255c 100644 --- a/static/js/mediastream/webrtc.js +++ b/static/js/mediastream/webrtc.js @@ -42,6 +42,43 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u console.log("This seems to be Android"); } + var InternalPC = function(call) { + this.currentcall = call; + this.isinternal = true; + }; + + InternalPC.prototype.close = function() { + this.currentcall.e.triggerHandler("connectionStateChange", ["closed", this.currentcall]); + }; + + InternalPC.prototype.addStream = function() { + }; + + InternalPC.prototype.negotiationNeeded = function() { + }; + + var InternalCall = function(webrtc) { + this.id = null; + this.webrtc = webrtc; + this.e = $({}); + this.isinternal = true; + this.pc = new InternalPC(this); + + this.mediaConstraints = $.extend(true, {}, this.webrtc.settings.mediaConstraints); + }; + + InternalCall.prototype.setInitiate = function(initiate) { + }; + + InternalCall.prototype.createPeerConnection = function(success_cb, error_cb) { + success_cb(this.pc); + }; + + InternalCall.prototype.close = function() { + this.pc.close(); + this.e.triggerHandler("closed", [this]); + }; + var WebRTC = function(api) { this.api = api; @@ -50,6 +87,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u this.currentcall = null; this.currentconference = null; + this.currentroom = null; this.msgQueue = []; this.started = false; @@ -116,7 +154,15 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u }; this.api.e.bind("received.offer received.candidate received.answer received.bye received.conference", _.bind(this.processReceived, this)); + this.api.e.bind("received.room", _.bind(this.receivedRoom, this)); + }; + + WebRTC.prototype.receivedRoom = function(event, room) { + this.currentroom = room; + }; + WebRTC.prototype.isConferenceRoom = function() { + return this.currentroom && this.currentroom.Type === "Conference"; }; WebRTC.prototype.processReceived = function(event, to, data, type, to2, from) { @@ -137,13 +183,18 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u if (!this.initiator && !this.started) { switch (type) { case "Offer": - if (this.currentcall) { + 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. @@ -163,6 +214,15 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u // 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; @@ -218,7 +278,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u WebRTC.prototype.processReceivedMessage = function(to, data, type, to2, from) { - if (!this.started) { + if (!this.started && type !== "Conference") { console.log('PeerConnection has not been created yet!'); return; } @@ -314,7 +374,7 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u this.e.triggerHandler("bye", [data.Reason, from, to, to2]); break; case "Conference": - if (!this.currentcall || data.indexOf(this.currentcall.id) === -1) { + if ((!this.currentcall || data.indexOf(this.currentcall.id) === -1) && !this.isConferenceRoom()) { console.warn("Received Conference for unknown call -> ignore.", to, data); return; } else { @@ -379,6 +439,15 @@ function($, _, PeerCall, PeerConference, PeerXfer, PeerScreenshare, UserMedia, u }; + WebRTC.prototype.doUserMediaWithInternalCall = function() { + if (this.currentcall) { + console.warn("Already have a current call, not doing internal getUM"); + return; + } + var currentcall = this.currentcall = new InternalCall(this); + this.doUserMedia(currentcall); + }; + WebRTC.prototype.doUserMedia = function(currentcall) { // Create default media (audio/video).