"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. * */ exports.__esModule = true; var WebRtcPeer_1 = require("../OpenViduInternal/WebRtcPeer/WebRtcPeer"); var WebRtcStats_1 = require("../OpenViduInternal/WebRtcStats/WebRtcStats"); var PublisherSpeakingEvent_1 = require("../OpenViduInternal/Events/PublisherSpeakingEvent"); var EventEmitter = require("wolfy87-eventemitter"); var hark = require("hark"); /** * Represents each one of the media streams available in OpenVidu Server for certain session. * Each [[Publisher]] and [[Subscriber]] has an attribute of type Stream, as they give access * to one of them (sending and receiving it, respectively) */ var Stream = /** @class */ (function () { /** * @hidden */ function Stream(session, options) { var _this = this; /** * @hidden */ this.ee = new EventEmitter(); this.isSubscribeToRemote = false; /** * @hidden */ this.isLocalStreamReadyToPublish = false; /** * @hidden */ this.isLocalStreamPublished = false; this.session = session; if (options.hasOwnProperty('id')) { // InboundStreamOptions: stream belongs to a Subscriber this.inboundStreamOpts = options; this.streamId = this.inboundStreamOpts.id; this.hasAudio = this.inboundStreamOpts.recvAudio; this.hasVideo = this.inboundStreamOpts.recvVideo; this.typeOfVideo = (!this.inboundStreamOpts.typeOfVideo) ? undefined : this.inboundStreamOpts.typeOfVideo; this.frameRate = (this.inboundStreamOpts.frameRate === -1) ? undefined : this.inboundStreamOpts.frameRate; } else { // OutboundStreamOptions: stream belongs to a Publisher this.outboundStreamOpts = options; if (this.isSendVideo()) { if (this.isSendScreen()) { this.typeOfVideo = 'SCREEN'; } else { this.typeOfVideo = 'CAMERA'; } this.frameRate = this.outboundStreamOpts.publisherProperties.frameRate; } else { delete this.typeOfVideo; } this.hasAudio = this.isSendAudio(); this.hasVideo = this.isSendVideo(); } this.ee.on('mediastream-updated', function () { _this.streamManager.updateMediaStream(_this.mediaStream); console.debug('Video srcObject [' + _this.mediaStream + '] updated in stream [' + _this.streamId + ']'); }); } /* Hidden methods */ /** * @hidden */ Stream.prototype.getMediaStream = function () { return this.mediaStream; }; /** * @hidden */ Stream.prototype.setMediaStream = function (mediaStream) { this.mediaStream = mediaStream; }; /** * @hidden */ Stream.prototype.updateMediaStreamInVideos = function () { this.ee.emitEvent('mediastream-updated'); }; /** * @hidden */ Stream.prototype.getWebRtcPeer = function () { return this.webRtcPeer; }; /** * @hidden */ Stream.prototype.getRTCPeerConnection = function () { return this.webRtcPeer.pc; }; /** * @hidden */ Stream.prototype.subscribeToMyRemote = function (value) { this.isSubscribeToRemote = value; }; /** * @hidden */ Stream.prototype.setOutboundStreamOptions = function (outboundStreamOpts) { this.outboundStreamOpts = outboundStreamOpts; }; /** * @hidden */ Stream.prototype.subscribe = function () { var _this = this; return new Promise(function (resolve, reject) { _this.initWebRtcPeerReceive() .then(function () { resolve(); })["catch"](function (error) { reject(error); }); }); }; /** * @hidden */ Stream.prototype.publish = function () { var _this = this; return new Promise(function (resolve, reject) { if (_this.isLocalStreamReadyToPublish) { _this.initWebRtcPeerSend() .then(function () { resolve(); })["catch"](function (error) { reject(error); }); } else { _this.ee.once('stream-ready-to-publish', function () { _this.publish() .then(function () { resolve(); })["catch"](function (error) { reject(error); }); }); } }); }; /** * @hidden */ Stream.prototype.disposeWebRtcPeer = function () { if (this.webRtcPeer) { this.webRtcPeer.dispose(); } if (this.speechEvent) { this.speechEvent.stop(); } this.stopWebRtcStats(); console.info((!!this.outboundStreamOpts ? 'Outbound ' : 'Inbound ') + "WebRTCPeer from 'Stream' with id [" + this.streamId + '] is now closed'); }; /** * @hidden */ Stream.prototype.disposeMediaStream = function () { if (this.mediaStream) { this.mediaStream.getAudioTracks().forEach(function (track) { track.stop(); }); this.mediaStream.getVideoTracks().forEach(function (track) { track.stop(); }); delete this.mediaStream; } console.info((!!this.outboundStreamOpts ? 'Local ' : 'Remote ') + "MediaStream from 'Stream' with id [" + this.streamId + '] is now disposed'); }; /** * @hidden */ Stream.prototype.displayMyRemote = function () { return this.isSubscribeToRemote; }; /** * @hidden */ Stream.prototype.isSendAudio = function () { return (!!this.outboundStreamOpts && this.outboundStreamOpts.publisherProperties.audioSource !== null && this.outboundStreamOpts.publisherProperties.audioSource !== false); }; /** * @hidden */ Stream.prototype.isSendVideo = function () { return (!!this.outboundStreamOpts && this.outboundStreamOpts.publisherProperties.videoSource !== null && this.outboundStreamOpts.publisherProperties.videoSource !== false); }; /** * @hidden */ Stream.prototype.isSendScreen = function () { return (!!this.outboundStreamOpts && this.outboundStreamOpts.publisherProperties.videoSource === 'screen'); }; /** * @hidden */ Stream.prototype.setSpeechEventIfNotExists = function () { if (!this.speechEvent) { var harkOptions = this.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {}; harkOptions.interval = (typeof harkOptions.interval === 'number') ? harkOptions.interval : 50; harkOptions.threshold = (typeof harkOptions.threshold === 'number') ? harkOptions.threshold : -50; this.speechEvent = hark(this.mediaStream, harkOptions); } }; /** * @hidden */ Stream.prototype.enableSpeakingEvents = function () { var _this = this; this.setSpeechEventIfNotExists(); this.speechEvent.on('speaking', function () { _this.session.emitEvent('publisherStartSpeaking', [new PublisherSpeakingEvent_1.PublisherSpeakingEvent(_this.session, 'publisherStartSpeaking', _this.connection, _this.streamId)]); }); this.speechEvent.on('stopped_speaking', function () { _this.session.emitEvent('publisherStopSpeaking', [new PublisherSpeakingEvent_1.PublisherSpeakingEvent(_this.session, 'publisherStopSpeaking', _this.connection, _this.streamId)]); }); }; /** * @hidden */ Stream.prototype.enableOnceSpeakingEvents = function () { var _this = this; this.setSpeechEventIfNotExists(); this.speechEvent.on('speaking', function () { _this.session.emitEvent('publisherStartSpeaking', [new PublisherSpeakingEvent_1.PublisherSpeakingEvent(_this.session, 'publisherStartSpeaking', _this.connection, _this.streamId)]); _this.disableSpeakingEvents(); }); this.speechEvent.on('stopped_speaking', function () { _this.session.emitEvent('publisherStopSpeaking', [new PublisherSpeakingEvent_1.PublisherSpeakingEvent(_this.session, 'publisherStopSpeaking', _this.connection, _this.streamId)]); _this.disableSpeakingEvents(); }); }; /** * @hidden */ Stream.prototype.disableSpeakingEvents = function () { this.speechEvent.stop(); this.speechEvent = undefined; }; /** * @hidden */ Stream.prototype.isLocal = function () { // inbound options undefined and outbound options defined return (!this.inboundStreamOpts && !!this.outboundStreamOpts); }; /** * @hidden */ Stream.prototype.getSelectedIceCandidate = function () { var _this = this; return new Promise(function (resolve, reject) { _this.webRtcStats.getSelectedIceCandidateInfo() .then(function (report) { return resolve(report); })["catch"](function (error) { return reject(error); }); }); }; /** * @hidden */ Stream.prototype.getRemoteIceCandidateList = function () { return this.webRtcPeer.remoteCandidatesQueue; }; /** * @hidden */ Stream.prototype.getLocalIceCandidateList = function () { return this.webRtcPeer.localCandidatesQueue; }; /* Private methods */ Stream.prototype.initWebRtcPeerSend = function () { var _this = this; return new Promise(function (resolve, reject) { var userMediaConstraints = { audio: _this.isSendAudio(), video: _this.isSendVideo() }; var options = { mediaStream: _this.mediaStream, mediaConstraints: userMediaConstraints, onicecandidate: _this.connection.sendIceCandidate.bind(_this.connection), iceServers: _this.getIceServersConf(), simulcast: false }; var successCallback = function (sdpOfferParam) { console.debug('Sending SDP offer to publish as ' + _this.streamId, sdpOfferParam); _this.session.openvidu.sendRequest('publishVideo', { sdpOffer: sdpOfferParam, doLoopback: _this.displayMyRemote() || false, audioActive: _this.isSendAudio(), videoActive: _this.isSendVideo(), typeOfVideo: ((_this.isSendVideo()) ? (_this.isSendScreen() ? 'SCREEN' : 'CAMERA') : ''), frameRate: !!_this.frameRate ? _this.frameRate : -1 }, function (error, response) { if (error) { reject('Error on publishVideo: ' + JSON.stringify(error)); } else { _this.webRtcPeer.processAnswer(response.sdpAnswer) .then(function () { _this.streamId = response.id; _this.isLocalStreamPublished = true; if (_this.displayMyRemote()) { _this.remotePeerSuccesfullyEstablished(); } _this.ee.emitEvent('stream-created-by-publisher'); _this.initWebRtcStats(); resolve(); })["catch"](function (error) { reject(error); }); console.info("'Publisher' successfully published to session"); } }); }; if (_this.displayMyRemote()) { _this.webRtcPeer = new WebRtcPeer_1.WebRtcPeerSendrecv(options); } else { _this.webRtcPeer = new WebRtcPeer_1.WebRtcPeerSendonly(options); } _this.webRtcPeer.generateOffer().then(function (offer) { successCallback(offer); })["catch"](function (error) { reject(new Error('(publish) SDP offer error: ' + JSON.stringify(error))); }); }); }; Stream.prototype.initWebRtcPeerReceive = function () { var _this = this; return new Promise(function (resolve, reject) { var offerConstraints = { audio: _this.inboundStreamOpts.recvAudio, video: _this.inboundStreamOpts.recvVideo }; console.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer", offerConstraints); var options = { onicecandidate: _this.connection.sendIceCandidate.bind(_this.connection), mediaConstraints: offerConstraints, iceServers: _this.getIceServersConf(), simulcast: false }; var successCallback = function (sdpOfferParam) { console.debug('Sending SDP offer to subscribe to ' + _this.streamId, sdpOfferParam); _this.session.openvidu.sendRequest('receiveVideoFrom', { sender: _this.streamId, sdpOffer: sdpOfferParam }, function (error, response) { if (error) { reject(new Error('Error on recvVideoFrom: ' + JSON.stringify(error))); } else { _this.webRtcPeer.processAnswer(response.sdpAnswer).then(function () { _this.remotePeerSuccesfullyEstablished(); _this.initWebRtcStats(); resolve(); })["catch"](function (error) { reject(error); }); } }); }; _this.webRtcPeer = new WebRtcPeer_1.WebRtcPeerRecvonly(options); _this.webRtcPeer.generateOffer() .then(function (offer) { successCallback(offer); })["catch"](function (error) { reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error))); }); }); }; Stream.prototype.remotePeerSuccesfullyEstablished = function () { this.mediaStream = this.webRtcPeer.pc.getRemoteStreams()[0]; console.debug('Peer remote stream', this.mediaStream); if (!!this.mediaStream) { this.ee.emitEvent('mediastream-updated'); if (!this.displayMyRemote() && !!this.mediaStream.getAudioTracks()[0] && this.session.speakingEventsEnabled) { this.enableSpeakingEvents(); } } }; Stream.prototype.initWebRtcStats = function () { this.webRtcStats = new WebRtcStats_1.WebRtcStats(this); this.webRtcStats.initWebRtcStats(); }; Stream.prototype.stopWebRtcStats = function () { if (!!this.webRtcStats && this.webRtcStats.isEnabled()) { this.webRtcStats.stopWebRtcStats(); } }; Stream.prototype.getIceServersConf = function () { var returnValue; if (!!this.session.openvidu.advancedConfiguration.iceServers) { returnValue = this.session.openvidu.advancedConfiguration.iceServers === 'freeice' ? undefined : this.session.openvidu.advancedConfiguration.iceServers; } else if (this.session.openvidu.iceServers) { returnValue = this.session.openvidu.iceServers; } else { returnValue = undefined; } return returnValue; }; return Stream; }()); exports.Stream = Stream; //# sourceMappingURL=Stream.js.map