diff --git a/lib/mcs-core/lib/media/media-controller.js b/lib/mcs-core/lib/media/media-controller.js index 288154f0..7bc45f04 100644 --- a/lib/mcs-core/lib/media/media-controller.js +++ b/lib/mcs-core/lib/media/media-controller.js @@ -160,12 +160,16 @@ module.exports = class MediaController { return; } - _isAboveThreshold () { - if (GLOBAL_MEDIA_THRESHOLD > 0 && this.medias.size >= GLOBAL_MEDIA_THRESHOLD) { - Logger.error(LOG_PREFIX, `Server has exceeded the media threshold`, - { threshold: GLOBAL_MEDIA_THRESHOLD, current: this.medias.size } - ); - return true; + isAboveGlobalMediaThreshold ({ mediaId, ignoreThresholds = false }) { + if (GLOBAL_MEDIA_THRESHOLD > 0 && !ignoreThresholds) { + const preExistantMedia = mediaId ? this.hasMediaSession(mediaId) : false; + + if (!preExistantMedia && this.medias.size >= GLOBAL_MEDIA_THRESHOLD) { + Logger.error(LOG_PREFIX, `Server has exceeded the media threshold`, + { threshold: GLOBAL_MEDIA_THRESHOLD, current: this.medias.size } + ); + return true; + } } return false; } @@ -202,6 +206,13 @@ module.exports = class MediaController { throw (this._handleError(C.ERROR.MEDIA_ADAPTER_OBJECT_NOT_FOUND)); } + if (this.isAboveGlobalMediaThreshold(params)) { + throw (this._handleError({ + ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, + details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, + })); + } + try { user = this.getUser(userId); room = this.getRoom(user.roomId); @@ -210,13 +221,6 @@ module.exports = class MediaController { throw error; } - if (!params.ignoreThresholds && this.isAboveMediaThresholds(room, user)) { - throw (this._handleError({ - ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, - details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, - })); - } - try { ({ session, answer } = await user.publish(params.descriptor, type, params)); } catch (error) { @@ -224,7 +228,6 @@ module.exports = class MediaController { } this.addMediaSession(session); - room.addMediaSession(session); if (source) { try { @@ -252,6 +255,13 @@ module.exports = class MediaController { throw (this._handleError(C.ERROR.MEDIA_ADAPTER_OBJECT_NOT_FOUND)); } + if (this.isAboveGlobalMediaThreshold(params)) { + throw (this._handleError({ + ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, + details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, + })); + } + try { user = this.getUser(userId); room = this.getRoom(user.roomId); @@ -260,13 +270,6 @@ module.exports = class MediaController { throw error; } - if (!params.ignoreThresholds && this.isAboveMediaThresholds(room, user)) { - throw (this._handleError({ - ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, - details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, - })); - } - try { ({ session, answer } = await user.publish(params.descriptor, type, params)); } catch (error) { @@ -274,7 +277,6 @@ module.exports = class MediaController { } this.addMediaSession(session); - room.addMediaSession(session); session.sessionStarted(); return ({ descriptor: answer, mediaId: session.id }); } @@ -295,6 +297,13 @@ module.exports = class MediaController { throw (this._handleError(C.ERROR.MEDIA_ADAPTER_OBJECT_NOT_FOUND)); } + if (this.isAboveGlobalMediaThreshold(params)) { + throw (this._handleError({ + ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, + details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, + })); + } + try { user = this.getUser(userId); room = this.getRoom(user.roomId); @@ -303,13 +312,6 @@ module.exports = class MediaController { throw error; } - if (!params.ignoreThresholds && this.isAboveMediaThresholds(room, user)) { - throw (this._handleError({ - ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, - details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, - })); - } - try { if (sourceId === C.MEDIA_PROFILE.CONTENT) { source = this.getMediaSession(room._contentFloor.id); @@ -329,7 +331,6 @@ module.exports = class MediaController { } this.addMediaSession(session); - room.addMediaSession(session); session.sessionStarted(); return ({descriptor: answer, mediaId: session.id}); } @@ -388,6 +389,13 @@ module.exports = class MediaController { throw (this._handleError(C.ERROR.MEDIA_ADAPTER_OBJECT_NOT_FOUND)); } + if (this.isAboveGlobalMediaThreshold(params)) { + throw (this._handleError({ + ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, + details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, + })); + } + try { user = this.getUser(userId); room = this.getRoom(user.roomId); @@ -398,13 +406,6 @@ module.exports = class MediaController { throw (this._handleError(error)); } - if (!params.ignoreThresholds && this.isAboveMediaThresholds(room, user)) { - throw (this._handleError({ - ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, - details: `Threshold exceeded. Threshold: ${GLOBAL_MEDIA_THRESHOLD}`, - })); - } - try { ({ recordingSession, answer } = await user.startRecording( recordingPath, @@ -419,7 +420,6 @@ module.exports = class MediaController { } this.addMediaSession(recordingSession); - room.addMediaSession(recordingSession); recordingSession.sessionStarted(); return answer; } @@ -613,7 +613,7 @@ module.exports = class MediaController { } // No pre-existing externalUserId sent in the join procedure, create a new one - user = new User(roomId, type, params); + user = new User(room, type, params); this.users.set(user.id, user); if (user.externalUserId !== user.id) this.users.set(user.externalUserId, user); room.addUser(user); diff --git a/lib/mcs-core/lib/model/media-session.js b/lib/mcs-core/lib/model/media-session.js index 45440864..060fd611 100644 --- a/lib/mcs-core/lib/model/media-session.js +++ b/lib/mcs-core/lib/model/media-session.js @@ -452,6 +452,10 @@ module.exports = class MediaSession { this._mediaProfile = options.mediaProfile; // Media specs for the media. If not specified, falls back to the default this.mediaSpecs = options.mediaSpecs? options.mediaSpecs : {...MEDIA_SPECS}; + // API-specified media profiles (audio|video|content: 'string'|boolean => + // 'recvonly'|'sendonly'|'sendrecv'|true|false this.profiles = options.profiles || DEFAULT_PROFILES; + // Whether this media session should be ignored by the media limiter + this.ignoreThresholds = options.ignoreThresholds || false; } } diff --git a/lib/mcs-core/lib/model/user.js b/lib/mcs-core/lib/model/user.js index 00a90d41..7e183288 100644 --- a/lib/mcs-core/lib/model/user.js +++ b/lib/mcs-core/lib/model/user.js @@ -21,18 +21,22 @@ const MCS_USER_EJECTION_TIMER = config.has('mcsUserEjectionTimer') const LOG_PREFIX = "[mcs-user]"; module.exports = class User extends EventEmitter { - constructor(roomId, type, params = {}) { + constructor(room, type, params = {}) { super(); this.id = rid(); this.externalUserId = params.externalUserId || this.id; this.autoLeave = typeof params.autoLeave !== 'undefined'? params.autoLeave : false; - this.roomId = roomId; + this.room = room; this.type = type; this.name = params.name ? params.name : this.id; this.mediaSessions = {} this._clientTrackingIds = {}; } + get roomId () { + return this.room.id; + } + async _startSession (sessionId) { const session = this.mediaSessions[sessionId]; try { @@ -70,6 +74,14 @@ module.exports = class User extends EventEmitter { return false; } + addMediaSession (mediaSession) { + this.mediaSessions[mediaSession.id] = mediaSession; + } + + getMediaSession (id) { + return this.mediaSessions[id]; + } + createMediaSession (descriptor, type, params = {}) { const { mediaId } = params; @@ -79,7 +91,7 @@ module.exports = class User extends EventEmitter { // If it doesn't exist, just create a new one since it's an optional parameter // which should be ignored in case it doesn't make sense if (mediaId) { - const targetMediaSession = this.mediaSessions[mediaId]; + const targetMediaSession = this.getMediaSession(mediaId); if (targetMediaSession) { const updatedParams = { ...targetMediaSession.options, ...params }; targetMediaSession.remoteDescriptor = descriptor; @@ -88,6 +100,14 @@ module.exports = class User extends EventEmitter { } } + if (!params.ignoreThresholds + && (this.isAboveThreshold() || this.room.isAboveThreshold())) { + throw (this._handleError({ + ...C.ERROR.MEDIA_SERVER_NO_RESOURCES, + details: 'Threshold exceeded', + })); + } + const mediaSession = MediaFactory.createMediaSession( descriptor, type, @@ -96,8 +116,10 @@ module.exports = class User extends EventEmitter { params ); + this.addMediaSession(mediaSession); + this.room.addMediaSession(mediaSession); + this._trackMediaDisconnection(mediaSession); - this.mediaSessions[mediaSession.id] = mediaSession; if (this.ejectionRoutine) this._clearEjectionTimeout(); return mediaSession; @@ -245,10 +267,6 @@ module.exports = class User extends EventEmitter { }); } - getMediaSession (id) { - return this.mediaSessions[id]; - } - getUserInfo () { const mediasList = Object.keys(this.mediaSessions).map(key => { let mi = this.mediaSessions[key].getMediaInfo();