diff --git a/src/webrtc/call.js b/src/webrtc/call.js index d35622079..f0d6eac33 100644 --- a/src/webrtc/call.js +++ b/src/webrtc/call.js @@ -409,12 +409,12 @@ MatrixCall.prototype.answer = function() { if (!this.localAVStream && !this.waitForLocalAVStream) { this.webRtc.getUserMedia( _getUserMediaVideoContraints(this.type), - hookCallback(self, self._gotUserMediaForAnswer), - hookCallback(self, self._getUserMediaFailed), + hookCallback(self, self._maybeGotUserMediaForAnswer), + hookCallback(self, self._maybeGotUserMediaForAnswer), ); setState(this, 'wait_local_media'); } else if (this.localAVStream) { - this._gotUserMediaForAnswer(this.localAVStream); + this._maybeGotUserMediaForAnswer(this.localAVStream); } else if (this.waitForLocalAVStream) { setState(this, 'wait_local_media'); } @@ -433,11 +433,11 @@ MatrixCall.prototype._replacedBy = function(newCall) { newCall.waitForLocalAVStream = true; } else if (this.state == 'create_offer') { debuglog("Handing local stream to new call"); - newCall._gotUserMediaForAnswer(this.localAVStream); + newCall._maybeGotUserMediaForAnswer(this.localAVStream); delete(this.localAVStream); } else if (this.state == 'invite_sent') { debuglog("Handing local stream to new call"); - newCall._gotUserMediaForAnswer(this.localAVStream); + newCall._maybeGotUserMediaForAnswer(this.localAVStream); delete(this.localAVStream); } newCall.localVideoElement = this.localVideoElement; @@ -523,48 +523,70 @@ MatrixCall.prototype.isMicrophoneMuted = function() { * @private * @param {Object} stream */ -MatrixCall.prototype._gotUserMediaForInvite = function(stream) { +MatrixCall.prototype._maybeGotUserMediaForInvite = function(stream) { if (this.successor) { - this.successor._gotUserMediaForAnswer(stream); + this.successor._maybeGotUserMediaForAnswer(stream); return; } if (this.state == 'ended') { return; } - debuglog("_gotUserMediaForInvite -> " + this.type); + debuglog("_maybeGotUserMediaForInvite -> " + this.type); const self = this; - const videoEl = this.getLocalVideoElement(); - if (videoEl && this.type == 'video') { - videoEl.autoplay = true; - if (this.screenSharingStream) { - debuglog("Setting screen sharing stream to the local video element"); - this.assignElement(videoEl, this.screenSharingStream, "localVideo"); - } else { - this.assignElement(videoEl, stream, "localVideo"); - } - videoEl.muted = true; - setTimeout(function() { - const vel = self.getLocalVideoElement(); - if (vel.play) { - self.playElement(vel, "localVideo"); + const error = stream; + let constraints = null; + if (stream instanceof MediaStream) { + const videoEl = this.getLocalVideoElement(); + + if (videoEl && this.type == 'video') { + videoEl.autoplay = true; + if (this.screenSharingStream) { + debuglog("Setting screen sharing stream to the local video" + + " element"); + this.assignElement(videoEl, this.screenSharingStream, "localVideo"); + } else { + this.assignElement(videoEl, stream, "localVideo"); } - }, 0); + videoEl.muted = true; + setTimeout(function() { + const vel = self.getLocalVideoElement(); + if (vel.play) { + self.playElement(vel, "localVideo"); + } + }, 0); + } + + if (this.screenSharingStream) { + this.screenSharingStream.addTrack(stream.getAudioTracks()[0]); + stream = this.screenSharingStream; + } + + this.localAVStream = stream; + // why do we enable audio (and only audio) tracks here? -- matthew + setTracksEnabled(stream.getAudioTracks(), true); + this.peerConn = _createPeerConnection(this); + this.peerConn.addStream(stream); + } else if (error.name === 'PermissionDeniedError') { + debuglog('User denied access to camera/microphone.' + + ' Or possibly you are using an insecure domain. Receiving only.'); + constraints = { + 'mandatory': { + 'OfferToReceiveAudio': true, + 'OfferToReceiveVideo': self.type == 'video', + }, + }; + this.peerConn = _createPeerConnection(this); + } else { + debuglog('Failed to getUserMedia.'); + this._getUserMediaFailed(error); + return; } - if (this.screenSharingStream) { - this.screenSharingStream.addTrack(stream.getAudioTracks()[0]); - stream = this.screenSharingStream; - } - - this.localAVStream = stream; - // why do we enable audio (and only audio) tracks here? -- matthew - setTracksEnabled(stream.getAudioTracks(), true); - this.peerConn = _createPeerConnection(this); - this.peerConn.addStream(stream); this.peerConn.createOffer( hookCallback(self, self._gotLocalOffer), hookCallback(self, self._getLocalOfferFailed), + constraints, ); setState(self, 'create_offer'); }; @@ -574,29 +596,40 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) { * @private * @param {Object} stream */ -MatrixCall.prototype._gotUserMediaForAnswer = function(stream) { +MatrixCall.prototype._maybeGotUserMediaForAnswer = function(stream) { const self = this; if (self.state == 'ended') { return; } - const localVidEl = self.getLocalVideoElement(); - if (localVidEl && self.type == 'video') { - localVidEl.autoplay = true; - this.assignElement(localVidEl, stream, "localVideo"); - localVidEl.muted = true; - setTimeout(function() { - const vel = self.getLocalVideoElement(); - if (vel.play) { - self.playElement(vel, "localVideo"); - } - }, 0); + const error = stream; + if (stream instanceof MediaStream) { + const localVidEl = self.getLocalVideoElement(); + + if (localVidEl && self.type == 'video') { + localVidEl.autoplay = true; + this.assignElement(localVidEl, stream, "localVideo"); + localVidEl.muted = true; + setTimeout(function() { + const vel = self.getLocalVideoElement(); + if (vel.play) { + self.playElement(vel, "localVideo"); + } + }, 0); + } + + self.localAVStream = stream; + setTracksEnabled(stream.getAudioTracks(), true); + self.peerConn.addStream(stream); + } else if (error.name === 'PermissionDeniedError') { + debuglog('User denied access to camera/microphone.' + + ' Or possibly you are using an insecure domain. Receiving only.'); + } else { + debuglog('Failed to getUserMedia.'); + this._getUserMediaFailed(error); + return; } - self.localAVStream = stream; - setTracksEnabled(stream.getAudioTracks(), true); - self.peerConn.addStream(stream); - const constraints = { 'mandatory': { 'OfferToReceiveAudio': true, @@ -1126,8 +1159,8 @@ const _placeCallWithConstraints = function(self, constraints) { self.client.callList[self.callId] = self; self.webRtc.getUserMedia( constraints, - hookCallback(self, self._gotUserMediaForInvite), - hookCallback(self, self._getUserMediaFailed), + hookCallback(self, self._maybeGotUserMediaForInvite), + hookCallback(self, self._maybeGotUserMediaForInvite), ); setState(self, 'wait_local_media'); self.direction = 'outbound';