openvidu/openvidu-browser/lib/OpenViduInternal/WebRtcPeer/WebRtcPeer.js

264 lines
11 KiB
JavaScript

"use strict";
/*
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
var freeice = require("freeice");
var uuid = require("uuid");
var platform = require("platform");
var WebRtcPeer = /** @class */ (function () {
function WebRtcPeer(configuration) {
var _this = this;
this.configuration = configuration;
this.remoteCandidatesQueue = [];
this.localCandidatesQueue = [];
this.iceCandidateList = [];
this.candidategatheringdone = false;
this.configuration.iceServers = (!!this.configuration.iceServers && this.configuration.iceServers.length > 0) ? this.configuration.iceServers : freeice();
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
this.id = !!configuration.id ? configuration.id : uuid.v4();
this.pc.onicecandidate = function (event) {
var candidate = event.candidate;
if (candidate) {
_this.localCandidatesQueue.push({ candidate: candidate.candidate });
_this.candidategatheringdone = false;
_this.configuration.onicecandidate(event.candidate);
}
else if (!_this.candidategatheringdone) {
_this.candidategatheringdone = true;
}
};
this.pc.onsignalingstatechange = function () {
if (_this.pc.signalingState === 'stable') {
while (_this.iceCandidateList.length > 0) {
_this.pc.addIceCandidate(_this.iceCandidateList.shift());
}
}
};
this.start();
}
/**
* This function creates the RTCPeerConnection object taking into account the
* properties received in the constructor. It starts the SDP negotiation
* process: generates the SDP offer and invokes the onsdpoffer callback. This
* callback is expected to send the SDP offer, in order to obtain an SDP
* answer from another peer.
*/
WebRtcPeer.prototype.start = function () {
var _this = this;
return new Promise(function (resolve, reject) {
if (_this.pc.signalingState === 'closed') {
reject('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');
}
if (!!_this.configuration.mediaStream) {
_this.pc.addStream(_this.configuration.mediaStream);
}
// [Hack] https://code.google.com/p/chromium/issues/detail?id=443558
if (_this.configuration.mode === 'sendonly' &&
(platform.name === 'Chrome' && platform.version.toString().substring(0, 2) === '39')) {
_this.configuration.mode = 'sendrecv';
}
resolve();
});
};
/**
* This method frees the resources used by WebRtcPeer
*/
WebRtcPeer.prototype.dispose = function () {
var _this = this;
console.debug('Disposing WebRtcPeer');
try {
if (this.pc) {
if (this.pc.signalingState === 'closed') {
return;
}
this.remoteCandidatesQueue = [];
this.localCandidatesQueue = [];
this.pc.getLocalStreams().forEach(function (str) {
_this.streamStop(str);
});
// FIXME This is not yet implemented in firefox
// if(videoStream) pc.removeStream(videoStream);
// if(audioStream) pc.removeStream(audioStream);
this.pc.close();
}
}
catch (err) {
console.warn('Exception disposing webrtc peer ' + err);
}
};
/**
* 1) Function that creates an offer, sets it as local description and returns the offer param
* to send to OpenVidu Server (will be the remote description of other peer)
*/
WebRtcPeer.prototype.generateOffer = function () {
var _this = this;
return new Promise(function (resolve, reject) {
var offerAudio, offerVideo = true;
// Constraints must have both blocks
if (!!_this.configuration.mediaConstraints) {
offerAudio = (typeof _this.configuration.mediaConstraints.audio === 'boolean') ?
_this.configuration.mediaConstraints.audio : true;
offerVideo = (typeof _this.configuration.mediaConstraints.video === 'boolean') ?
_this.configuration.mediaConstraints.video : true;
}
var constraints = {
offerToReceiveAudio: +(_this.configuration.mode !== 'sendonly' && offerAudio),
offerToReceiveVideo: +(_this.configuration.mode !== 'sendonly' && offerVideo)
};
console.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
_this.pc.createOffer(constraints).then(function (offer) {
console.debug('Created SDP offer');
return _this.pc.setLocalDescription(offer);
}).then(function () {
var localDescription = _this.pc.localDescription;
if (!!localDescription) {
console.debug('Local description set', localDescription.sdp);
resolve(localDescription.sdp);
}
else {
reject('Local description is not defined');
}
})["catch"](function (error) { return reject(error); });
});
};
/**
* 2) Function to invoke when a SDP offer is received. Sets it as remote description,
* generates and answer and returns it to send it to OpenVidu Server
*/
WebRtcPeer.prototype.processOffer = function (sdpOffer) {
var _this = this;
return new Promise(function (resolve, reject) {
var offer = {
type: 'offer',
sdp: sdpOffer
};
console.debug('SDP offer received, setting remote description');
if (_this.pc.signalingState === 'closed') {
reject('PeerConnection is closed');
}
_this.pc.setRemoteDescription(offer)
.then(function () {
return _this.pc.createAnswer();
}).then(function (answer) {
console.debug('Created SDP answer');
return _this.pc.setLocalDescription(answer);
}).then(function () {
var localDescription = _this.pc.localDescription;
if (!!localDescription) {
console.debug('Local description set', localDescription.sdp);
resolve(localDescription.sdp);
}
else {
reject('Local description is not defined');
}
})["catch"](function (error) { return reject(error); });
});
};
/**
* 3) Function invoked when a SDP answer is received. Final step in SDP negotiation, the peer
* just needs to set the answer as its remote description
*/
WebRtcPeer.prototype.processAnswer = function (sdpAnswer) {
var _this = this;
return new Promise(function (resolve, reject) {
var answer = {
type: 'answer',
sdp: sdpAnswer
};
console.debug('SDP answer received, setting remote description');
if (_this.pc.signalingState === 'closed') {
reject('RTCPeerConnection is closed');
}
_this.pc.setRemoteDescription(answer).then(function () { return resolve(); })["catch"](function (error) { return reject(error); });
});
};
/**
* Callback function invoked when an ICE candidate is received
*/
WebRtcPeer.prototype.addIceCandidate = function (iceCandidate) {
var _this = this;
return new Promise(function (resolve, reject) {
console.debug('Remote ICE candidate received', iceCandidate);
_this.remoteCandidatesQueue.push(iceCandidate);
switch (_this.pc.signalingState) {
case 'closed':
reject(new Error('PeerConnection object is closed'));
break;
case 'stable':
if (!!_this.pc.remoteDescription) {
_this.pc.addIceCandidate(iceCandidate).then(function () { return resolve(); })["catch"](function (error) { return reject(error); });
}
break;
default:
_this.iceCandidateList.push(iceCandidate);
resolve();
}
});
};
WebRtcPeer.prototype.streamStop = function (stream) {
stream.getTracks().forEach(function (track) {
track.stop();
stream.removeTrack(track);
});
};
return WebRtcPeer;
}());
exports.WebRtcPeer = WebRtcPeer;
var WebRtcPeerRecvonly = /** @class */ (function (_super) {
__extends(WebRtcPeerRecvonly, _super);
function WebRtcPeerRecvonly(configuration) {
var _this = this;
configuration.mode = 'recvonly';
_this = _super.call(this, configuration) || this;
return _this;
}
return WebRtcPeerRecvonly;
}(WebRtcPeer));
exports.WebRtcPeerRecvonly = WebRtcPeerRecvonly;
var WebRtcPeerSendonly = /** @class */ (function (_super) {
__extends(WebRtcPeerSendonly, _super);
function WebRtcPeerSendonly(configuration) {
var _this = this;
configuration.mode = 'sendonly';
_this = _super.call(this, configuration) || this;
return _this;
}
return WebRtcPeerSendonly;
}(WebRtcPeer));
exports.WebRtcPeerSendonly = WebRtcPeerSendonly;
var WebRtcPeerSendrecv = /** @class */ (function (_super) {
__extends(WebRtcPeerSendrecv, _super);
function WebRtcPeerSendrecv(configuration) {
var _this = this;
configuration.mode = 'sendrecv';
_this = _super.call(this, configuration) || this;
return _this;
}
return WebRtcPeerSendrecv;
}(WebRtcPeer));
exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
//# sourceMappingURL=WebRtcPeer.js.map