mirror of https://github.com/OpenVidu/openvidu.git
339 lines
15 KiB
JavaScript
339 lines
15 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 = function (d, b) {
|
|
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 extendStatics(d, b);
|
|
};
|
|
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");
|
|
platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua.indexOf('Safari') === -1;
|
|
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) {
|
|
if (!!event.candidate) {
|
|
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) {
|
|
if (platform['isIonicIos']) {
|
|
// iOS Ionic. LIMITATION: must use deprecated WebRTC API
|
|
var pc2 = _this.pc;
|
|
pc2.addStream(_this.configuration.mediaStream);
|
|
}
|
|
else {
|
|
for (var _i = 0, _a = _this.configuration.mediaStream.getTracks(); _i < _a.length; _i++) {
|
|
var track = _a[_i];
|
|
_this.pc.addTrack(track, _this.configuration.mediaStream);
|
|
}
|
|
}
|
|
resolve();
|
|
}
|
|
});
|
|
};
|
|
/**
|
|
* This method frees the resources used by WebRtcPeer
|
|
*/
|
|
WebRtcPeer.prototype.dispose = function (videoSourceIsMediaStreamTrack) {
|
|
console.debug('Disposing WebRtcPeer');
|
|
try {
|
|
if (this.pc) {
|
|
if (this.pc.signalingState === 'closed') {
|
|
return;
|
|
}
|
|
this.remoteCandidatesQueue = [];
|
|
this.localCandidatesQueue = [];
|
|
if (platform['isIonicIos']) {
|
|
// iOS Ionic. LIMITATION: must use deprecated WebRTC API
|
|
// Stop senders deprecated
|
|
var pc1 = this.pc;
|
|
for (var _i = 0, _a = pc1.getLocalStreams(); _i < _a.length; _i++) {
|
|
var sender = _a[_i];
|
|
if (!videoSourceIsMediaStreamTrack) {
|
|
sender.stop();
|
|
}
|
|
pc1.removeStream(sender);
|
|
}
|
|
// Stop receivers deprecated
|
|
for (var _b = 0, _c = pc1.getRemoteStreams(); _b < _c.length; _b++) {
|
|
var receiver = _c[_b];
|
|
if (!!receiver.track) {
|
|
receiver.stop();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Stop senders
|
|
for (var _d = 0, _e = this.pc.getSenders(); _d < _e.length; _d++) {
|
|
var sender = _e[_d];
|
|
if (!videoSourceIsMediaStreamTrack) {
|
|
if (!!sender.track) {
|
|
sender.track.stop();
|
|
}
|
|
}
|
|
this.pc.removeTrack(sender);
|
|
}
|
|
// Stop receivers
|
|
for (var _f = 0, _g = this.pc.getReceivers(); _f < _g.length; _f++) {
|
|
var receiver = _g[_f];
|
|
if (!!receiver.track) {
|
|
receiver.track.stop();
|
|
}
|
|
}
|
|
}
|
|
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));
|
|
if (platform.name === 'Safari' && platform.ua.indexOf('Safari') !== -1) {
|
|
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
|
|
if (offerAudio) {
|
|
_this.pc.addTransceiver('audio', {
|
|
direction: _this.configuration.mode
|
|
});
|
|
}
|
|
if (offerVideo) {
|
|
_this.pc.addTransceiver('video', {
|
|
direction: _this.configuration.mode
|
|
});
|
|
}
|
|
_this.pc
|
|
.createOffer()
|
|
.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); });
|
|
}
|
|
else {
|
|
_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, needsTimeoutOnProcessAswer) {
|
|
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');
|
|
}
|
|
if (needsTimeoutOnProcessAswer && platform['isIonicIos']) {
|
|
setTimeout(function () {
|
|
console.info('setRemoteDescription run after timout for iOS device');
|
|
_this.pc.setRemoteDescription(answer).then(function () { return resolve(); })["catch"](function (error) { return reject(error); });
|
|
}, 250);
|
|
}
|
|
else {
|
|
_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();
|
|
}
|
|
});
|
|
};
|
|
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
|