@webex/plugin-meetings 3.0.0-beta.21 → 3.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/locus-info/index.js +2 -2
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/index.js +0 -29
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/index.js +846 -617
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +3 -3
- package/dist/meeting/muteState.js.map +1 -1
- package/package.json +18 -18
- package/src/locus-info/index.ts +2 -2
- package/src/media/index.ts +0 -31
- package/src/meeting/index.ts +180 -50
- package/src/meeting/muteState.ts +2 -3
- package/test/unit/spec/meeting/index.js +186 -8
- package/test/unit/spec/meeting/muteState.js +1 -1
- package/dist/multistream/multistreamMedia.js +0 -110
- package/dist/multistream/multistreamMedia.js.map +0 -1
- package/src/multistream/multistreamMedia.ts +0 -97
|
@@ -5,7 +5,7 @@ var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequ
|
|
|
5
5
|
_Object$defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.
|
|
8
|
+
exports.createMuteState = void 0;
|
|
9
9
|
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
|
|
10
10
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
|
|
11
11
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
|
|
@@ -20,6 +20,7 @@ var _constants = require("../constants");
|
|
|
20
20
|
If we ever need to support it, search for REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION string to find the places that need updating
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
23
24
|
var createMuteState = function createMuteState(type, meeting, mediaDirection) {
|
|
24
25
|
if (type === _constants.AUDIO && !mediaDirection.sendAudio) {
|
|
25
26
|
return null;
|
|
@@ -36,6 +37,7 @@ var createMuteState = function createMuteState(type, meeting, mediaDirection) {
|
|
|
36
37
|
|
|
37
38
|
More info about Locus muting API: https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html#
|
|
38
39
|
*/
|
|
40
|
+
exports.createMuteState = createMuteState;
|
|
39
41
|
var MuteState = /*#__PURE__*/function () {
|
|
40
42
|
/**
|
|
41
43
|
* Constructor
|
|
@@ -315,6 +317,4 @@ var MuteState = /*#__PURE__*/function () {
|
|
|
315
317
|
}]);
|
|
316
318
|
return MuteState;
|
|
317
319
|
}();
|
|
318
|
-
var _default = createMuteState;
|
|
319
|
-
exports.default = _default;
|
|
320
320
|
//# sourceMappingURL=muteState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["createMuteState","type","meeting","mediaDirection","AUDIO","sendAudio","VIDEO","sendVideo","LoggerProxy","logger","info","id","MuteState","ParameterError","state","client","localMute","server","remoteMute","remoteMuted","unmuteAllowed","syncToServerInProgress","pendingPromiseResolve","pendingPromiseReject","mute","reject","PermissionError","applyClientStateLocally","resolve","applyClientStateToServer","Media","setLocalTrack","mediaProperties","audioTrack","videoTrack","localMuteRequiresSync","remoteMuteRequiresSync","localMuteSyncPromise","sendLocalMuteRequestToServer","then","sendRemoteMuteRequestToServer","catch","e","audioMuted","audio","videoMuted","video","MeetingUtil","remoteUpdateAudioVideo","locus","locusInfo","onFullLocus","remoteUpdateError","warn","members","muteMember","selfId","muted","Error","isMuted","isSelf"],"sources":["muteState.ts"],"sourcesContent":["import LoggerProxy from '../common/logs/logger-proxy';\nimport ParameterError from '../common/errors/parameter';\nimport PermissionError from '../common/errors/permission';\nimport Media from '../media';\nimport MeetingUtil from './util';\nimport {AUDIO, VIDEO} from '../constants';\n\n/* Certain aspects of server interaction for video muting are not implemented as we currently don't support remote muting of video.\n If we ever need to support it, search for REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION string to find the places that need updating\n*/\n\nconst createMuteState = (type, meeting, mediaDirection) => {\n if (type === AUDIO && !mediaDirection.sendAudio) {\n return null;\n }\n if (type === VIDEO && !mediaDirection.sendVideo) {\n return null;\n }\n\n LoggerProxy.logger.info(\n `Meeting:muteState#createMuteState --> ${type}: creating MuteState for meeting id ${meeting?.id}`\n );\n\n return new MuteState(type, meeting);\n};\n\n/** The purpose of this class is to manage the local and remote mute state and make sure that the server state always matches\n the last requested state by the client.\n\n More info about Locus muting API: https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html#\n*/\nclass MuteState {\n pendingPromiseReject: any;\n pendingPromiseResolve: any;\n state: any;\n type: any;\n\n /**\n * Constructor\n *\n * @param {String} type - audio or video\n * @param {Object} meeting - the meeting object (used for reading current remote mute status)\n */\n constructor(type: string, meeting: any) {\n if (type !== AUDIO && type !== VIDEO) {\n throw new ParameterError('Mute state is designed for handling audio or video only');\n }\n this.type = type;\n this.state = {\n client: {\n localMute: false,\n },\n server: {\n localMute: false,\n // initial values available only for audio (REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION)\n remoteMute: type === AUDIO ? meeting.remoteMuted : false,\n unmuteAllowed: type === AUDIO ? meeting.unmuteAllowed : true,\n },\n syncToServerInProgress: false,\n };\n // these 2 hold the resolve, reject methods for the promise we returned to the client in last handleClientRequest() call\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n }\n\n /**\n * Handles mute/unmute request from the client/user. Returns a promise that's resolved once the server update is completed or\n * at the point that this request becomese superseded by another client request.\n *\n * The client doesn't have to wait for the returned promise to resolve before calling handleClientRequest() again. If\n * handleClientRequest() is called again before the previous one resolved, the MuteState class will make sure that eventually\n * the server state will match the last requested state from the client.\n *\n * @public\n * @memberof MuteState\n * @param {Object} [meeting] the meeting object\n * @param {Boolean} [mute] true for muting, false for unmuting request\n * @returns {Promise}\n */\n public handleClientRequest(meeting?: object, mute?: boolean) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleClientRequest --> ${this.type}: user requesting new mute state: ${mute}`\n );\n\n if (!mute && !this.state.server.unmuteAllowed) {\n return Promise.reject(\n new PermissionError('User is not allowed to unmute self (hard mute feature is being used)')\n );\n }\n\n // we don't check if we're already in the same state, because even if we were, we would still have to apply the mute state locally,\n // because the client may have changed the audio/vidoe tracks\n this.state.client.localMute = mute;\n this.applyClientStateLocally(meeting);\n\n return new Promise((resolve, reject) => {\n if (this.pendingPromiseResolve) {\n // resolve the last promise we returned to the client as the client has issued a new request that has superseded the previous one\n this.pendingPromiseResolve();\n }\n this.pendingPromiseResolve = resolve;\n this.pendingPromiseReject = reject;\n this.applyClientStateToServer(meeting);\n });\n }\n\n /**\n * Applies the current mute state to the local track (by enabling or disabling it accordingly)\n *\n * @public\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {void}\n */\n public applyClientStateLocally(meeting?: any) {\n Media.setLocalTrack(\n !this.state.client.localMute,\n this.type === AUDIO ? meeting.mediaProperties.audioTrack : meeting.mediaProperties.videoTrack\n );\n }\n\n /**\n * Updates the server local and remote mute values so that they match the current client desired state.\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {void}\n */\n private applyClientStateToServer(meeting?: object) {\n if (this.state.syncToServerInProgress) {\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: request to server in progress, we need to wait for it to complete`\n );\n\n return;\n }\n\n const localMuteRequiresSync = this.state.client.localMute !== this.state.server.localMute;\n const remoteMuteRequiresSync = !this.state.client.localMute && this.state.server.remoteMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: localMuteRequiresSync: ${localMuteRequiresSync} (${this.state.client.localMute} ?= ${this.state.server.localMute})`\n );\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: remoteMuteRequiresSync: ${remoteMuteRequiresSync}`\n );\n\n if (!localMuteRequiresSync && !remoteMuteRequiresSync) {\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: client state already matching server state, nothing to do`\n );\n\n if (this.pendingPromiseResolve) {\n this.pendingPromiseResolve();\n }\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n\n return;\n }\n\n this.state.syncToServerInProgress = true;\n\n // first sync local mute with server\n const localMuteSyncPromise = localMuteRequiresSync\n ? this.sendLocalMuteRequestToServer(meeting)\n : Promise.resolve();\n\n localMuteSyncPromise\n .then(() =>\n // then follow it up with remote mute sync\n remoteMuteRequiresSync ? this.sendRemoteMuteRequestToServer(meeting) : Promise.resolve()\n )\n .then(() => {\n this.state.syncToServerInProgress = false;\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: sync with server completed`\n );\n\n // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync\n this.applyClientStateToServer(meeting);\n })\n .catch((e) => {\n this.state.syncToServerInProgress = false;\n\n if (this.pendingPromiseReject) {\n this.pendingPromiseReject(e);\n }\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n });\n }\n\n /**\n * Sets the local mute value in the server\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {Promise}\n */\n private sendLocalMuteRequestToServer(meeting?: any) {\n const audioMuted =\n this.type === AUDIO ? this.state.client.localMute : meeting.audio?.state.client.localMute;\n const videoMuted =\n this.type === VIDEO ? this.state.client.localMute : meeting.video?.state.client.localMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: sending local mute (audio=${audioMuted}, video=${videoMuted}) to server`\n );\n\n return MeetingUtil.remoteUpdateAudioVideo(audioMuted, videoMuted, meeting)\n .then((locus) => {\n LoggerProxy.logger.info(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`\n );\n\n this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;\n\n meeting.locusInfo.onFullLocus(locus);\n\n return locus;\n })\n .catch((remoteUpdateError) => {\n LoggerProxy.logger.warn(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: failed to apply local mute (audio=${audioMuted}, video=${videoMuted}) to server: ${remoteUpdateError}`\n );\n\n return Promise.reject(remoteUpdateError);\n });\n }\n\n /**\n * Sets the remote mute value in the server\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {Promise}\n */\n private sendRemoteMuteRequestToServer(meeting?: any) {\n if (this.type === AUDIO) {\n const remoteMute = this.state.client.localMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: sending remote mute:${remoteMute} to server`\n );\n\n return meeting.members\n .muteMember(meeting.members.selfId, remoteMute)\n .then(() => {\n LoggerProxy.logger.info(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: remote mute:${remoteMute} applied to server`\n );\n\n this.state.server.remoteMute = remoteMute;\n })\n .catch((remoteUpdateError) => {\n LoggerProxy.logger.warn(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: failed to apply remote mute ${remoteMute} to server: ${remoteUpdateError}`\n );\n\n return Promise.reject(remoteUpdateError);\n });\n }\n\n // for now we don't need to support remote muting of video (REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION)\n this.state.server.remoteMute = this.state.client.localMute;\n\n return Promise.resolve();\n }\n\n /**\n * This method should be called whenever the server remote mute state is changed\n *\n * @public\n * @memberof MuteState\n * @param {Boolean} [muted] true if user is remotely muted, false otherwise\n * @param {Boolean} [unmuteAllowed] indicates if user is allowed to unmute self (false when \"hard mute\" feature is used)\n * @returns {undefined}\n */\n public handleServerRemoteMuteUpdate(muted?: boolean, unmuteAllowed?: boolean) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleServerRemoteMuteUpdate --> ${this.type}: updating server remoteMute to (${muted})`\n );\n this.state.server.remoteMute = muted;\n this.state.server.unmuteAllowed = unmuteAllowed;\n }\n\n /**\n * This method should be called whenever we receive from the server a requirement to locally unmute\n *\n * @public\n * @memberof MuteState\n * @param {Object} [meeting] the meeting object\n * @returns {undefined}\n */\n public handleServerLocalUnmuteRequired(meeting?: object) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleServerLocalUnmuteRequired --> ${this.type}: localAudioUnmuteRequired received -> doing local unmute`\n );\n\n this.state.server.remoteMute = false;\n this.state.client.localMute = false;\n\n if (this.pendingPromiseReject) {\n this.pendingPromiseReject(\n new Error('Server requested local unmute - this overrides any client request in progress')\n );\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n }\n\n this.applyClientStateLocally(meeting);\n this.applyClientStateToServer(meeting);\n }\n\n /**\n * Returns true if the user is locally or remotely muted\n *\n * @public\n * @memberof MuteState\n * @returns {Boolean}\n */\n public isMuted() {\n return (\n this.state.client.localMute || this.state.server.localMute || this.state.server.remoteMute\n );\n }\n\n /**\n * Returns true if the user is muted as a result of the client request (and not remotely muted)\n *\n * @public\n * @memberof MuteState\n * @returns {Boolean}\n */\n public isSelf() {\n return this.state.client.localMute && !this.state.server.remoteMute;\n }\n\n // defined for backwards compatibility with the old AudioStateMachine/VideoStateMachine classes\n get muted() {\n return this.isMuted();\n }\n\n // defined for backwards compatibility with the old AudioStateMachine/VideoStateMachine classes\n get self() {\n return this.isSelf();\n }\n}\n\nexport default createMuteState;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["createMuteState","type","meeting","mediaDirection","AUDIO","sendAudio","VIDEO","sendVideo","LoggerProxy","logger","info","id","MuteState","ParameterError","state","client","localMute","server","remoteMute","remoteMuted","unmuteAllowed","syncToServerInProgress","pendingPromiseResolve","pendingPromiseReject","mute","reject","PermissionError","applyClientStateLocally","resolve","applyClientStateToServer","Media","setLocalTrack","mediaProperties","audioTrack","videoTrack","localMuteRequiresSync","remoteMuteRequiresSync","localMuteSyncPromise","sendLocalMuteRequestToServer","then","sendRemoteMuteRequestToServer","catch","e","audioMuted","audio","videoMuted","video","MeetingUtil","remoteUpdateAudioVideo","locus","locusInfo","onFullLocus","remoteUpdateError","warn","members","muteMember","selfId","muted","Error","isMuted","isSelf"],"sources":["muteState.ts"],"sourcesContent":["import LoggerProxy from '../common/logs/logger-proxy';\nimport ParameterError from '../common/errors/parameter';\nimport PermissionError from '../common/errors/permission';\nimport Media from '../media';\nimport MeetingUtil from './util';\nimport {AUDIO, VIDEO} from '../constants';\n\n/* Certain aspects of server interaction for video muting are not implemented as we currently don't support remote muting of video.\n If we ever need to support it, search for REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION string to find the places that need updating\n*/\n\n// eslint-disable-next-line import/prefer-default-export\nexport const createMuteState = (type, meeting, mediaDirection) => {\n if (type === AUDIO && !mediaDirection.sendAudio) {\n return null;\n }\n if (type === VIDEO && !mediaDirection.sendVideo) {\n return null;\n }\n\n LoggerProxy.logger.info(\n `Meeting:muteState#createMuteState --> ${type}: creating MuteState for meeting id ${meeting?.id}`\n );\n\n return new MuteState(type, meeting);\n};\n\n/** The purpose of this class is to manage the local and remote mute state and make sure that the server state always matches\n the last requested state by the client.\n\n More info about Locus muting API: https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html#\n*/\nclass MuteState {\n pendingPromiseReject: any;\n pendingPromiseResolve: any;\n state: any;\n type: any;\n\n /**\n * Constructor\n *\n * @param {String} type - audio or video\n * @param {Object} meeting - the meeting object (used for reading current remote mute status)\n */\n constructor(type: string, meeting: any) {\n if (type !== AUDIO && type !== VIDEO) {\n throw new ParameterError('Mute state is designed for handling audio or video only');\n }\n this.type = type;\n this.state = {\n client: {\n localMute: false,\n },\n server: {\n localMute: false,\n // initial values available only for audio (REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION)\n remoteMute: type === AUDIO ? meeting.remoteMuted : false,\n unmuteAllowed: type === AUDIO ? meeting.unmuteAllowed : true,\n },\n syncToServerInProgress: false,\n };\n // these 2 hold the resolve, reject methods for the promise we returned to the client in last handleClientRequest() call\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n }\n\n /**\n * Handles mute/unmute request from the client/user. Returns a promise that's resolved once the server update is completed or\n * at the point that this request becomese superseded by another client request.\n *\n * The client doesn't have to wait for the returned promise to resolve before calling handleClientRequest() again. If\n * handleClientRequest() is called again before the previous one resolved, the MuteState class will make sure that eventually\n * the server state will match the last requested state from the client.\n *\n * @public\n * @memberof MuteState\n * @param {Object} [meeting] the meeting object\n * @param {Boolean} [mute] true for muting, false for unmuting request\n * @returns {Promise}\n */\n public handleClientRequest(meeting?: object, mute?: boolean) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleClientRequest --> ${this.type}: user requesting new mute state: ${mute}`\n );\n\n if (!mute && !this.state.server.unmuteAllowed) {\n return Promise.reject(\n new PermissionError('User is not allowed to unmute self (hard mute feature is being used)')\n );\n }\n\n // we don't check if we're already in the same state, because even if we were, we would still have to apply the mute state locally,\n // because the client may have changed the audio/vidoe tracks\n this.state.client.localMute = mute;\n this.applyClientStateLocally(meeting);\n\n return new Promise((resolve, reject) => {\n if (this.pendingPromiseResolve) {\n // resolve the last promise we returned to the client as the client has issued a new request that has superseded the previous one\n this.pendingPromiseResolve();\n }\n this.pendingPromiseResolve = resolve;\n this.pendingPromiseReject = reject;\n this.applyClientStateToServer(meeting);\n });\n }\n\n /**\n * Applies the current mute state to the local track (by enabling or disabling it accordingly)\n *\n * @public\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {void}\n */\n public applyClientStateLocally(meeting?: any) {\n Media.setLocalTrack(\n !this.state.client.localMute,\n this.type === AUDIO ? meeting.mediaProperties.audioTrack : meeting.mediaProperties.videoTrack\n );\n }\n\n /**\n * Updates the server local and remote mute values so that they match the current client desired state.\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {void}\n */\n private applyClientStateToServer(meeting?: object) {\n if (this.state.syncToServerInProgress) {\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: request to server in progress, we need to wait for it to complete`\n );\n\n return;\n }\n\n const localMuteRequiresSync = this.state.client.localMute !== this.state.server.localMute;\n const remoteMuteRequiresSync = !this.state.client.localMute && this.state.server.remoteMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: localMuteRequiresSync: ${localMuteRequiresSync} (${this.state.client.localMute} ?= ${this.state.server.localMute})`\n );\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: remoteMuteRequiresSync: ${remoteMuteRequiresSync}`\n );\n\n if (!localMuteRequiresSync && !remoteMuteRequiresSync) {\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: client state already matching server state, nothing to do`\n );\n\n if (this.pendingPromiseResolve) {\n this.pendingPromiseResolve();\n }\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n\n return;\n }\n\n this.state.syncToServerInProgress = true;\n\n // first sync local mute with server\n const localMuteSyncPromise = localMuteRequiresSync\n ? this.sendLocalMuteRequestToServer(meeting)\n : Promise.resolve();\n\n localMuteSyncPromise\n .then(() =>\n // then follow it up with remote mute sync\n remoteMuteRequiresSync ? this.sendRemoteMuteRequestToServer(meeting) : Promise.resolve()\n )\n .then(() => {\n this.state.syncToServerInProgress = false;\n LoggerProxy.logger.info(\n `Meeting:muteState#applyClientStateToServer --> ${this.type}: sync with server completed`\n );\n\n // need to check if a new sync is required, because this.state.client may have changed while we were doing the current sync\n this.applyClientStateToServer(meeting);\n })\n .catch((e) => {\n this.state.syncToServerInProgress = false;\n\n if (this.pendingPromiseReject) {\n this.pendingPromiseReject(e);\n }\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n });\n }\n\n /**\n * Sets the local mute value in the server\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {Promise}\n */\n private sendLocalMuteRequestToServer(meeting?: any) {\n const audioMuted =\n this.type === AUDIO ? this.state.client.localMute : meeting.audio?.state.client.localMute;\n const videoMuted =\n this.type === VIDEO ? this.state.client.localMute : meeting.video?.state.client.localMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: sending local mute (audio=${audioMuted}, video=${videoMuted}) to server`\n );\n\n return MeetingUtil.remoteUpdateAudioVideo(audioMuted, videoMuted, meeting)\n .then((locus) => {\n LoggerProxy.logger.info(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`\n );\n\n this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;\n\n meeting.locusInfo.onFullLocus(locus);\n\n return locus;\n })\n .catch((remoteUpdateError) => {\n LoggerProxy.logger.warn(\n `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: failed to apply local mute (audio=${audioMuted}, video=${videoMuted}) to server: ${remoteUpdateError}`\n );\n\n return Promise.reject(remoteUpdateError);\n });\n }\n\n /**\n * Sets the remote mute value in the server\n *\n * @private\n * @param {Object} [meeting] the meeting object\n * @memberof MuteState\n * @returns {Promise}\n */\n private sendRemoteMuteRequestToServer(meeting?: any) {\n if (this.type === AUDIO) {\n const remoteMute = this.state.client.localMute;\n\n LoggerProxy.logger.info(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: sending remote mute:${remoteMute} to server`\n );\n\n return meeting.members\n .muteMember(meeting.members.selfId, remoteMute)\n .then(() => {\n LoggerProxy.logger.info(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: remote mute:${remoteMute} applied to server`\n );\n\n this.state.server.remoteMute = remoteMute;\n })\n .catch((remoteUpdateError) => {\n LoggerProxy.logger.warn(\n `Meeting:muteState#sendRemoteMuteRequestToServer --> ${this.type}: failed to apply remote mute ${remoteMute} to server: ${remoteUpdateError}`\n );\n\n return Promise.reject(remoteUpdateError);\n });\n }\n\n // for now we don't need to support remote muting of video (REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION)\n this.state.server.remoteMute = this.state.client.localMute;\n\n return Promise.resolve();\n }\n\n /**\n * This method should be called whenever the server remote mute state is changed\n *\n * @public\n * @memberof MuteState\n * @param {Boolean} [muted] true if user is remotely muted, false otherwise\n * @param {Boolean} [unmuteAllowed] indicates if user is allowed to unmute self (false when \"hard mute\" feature is used)\n * @returns {undefined}\n */\n public handleServerRemoteMuteUpdate(muted?: boolean, unmuteAllowed?: boolean) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleServerRemoteMuteUpdate --> ${this.type}: updating server remoteMute to (${muted})`\n );\n this.state.server.remoteMute = muted;\n this.state.server.unmuteAllowed = unmuteAllowed;\n }\n\n /**\n * This method should be called whenever we receive from the server a requirement to locally unmute\n *\n * @public\n * @memberof MuteState\n * @param {Object} [meeting] the meeting object\n * @returns {undefined}\n */\n public handleServerLocalUnmuteRequired(meeting?: object) {\n LoggerProxy.logger.info(\n `Meeting:muteState#handleServerLocalUnmuteRequired --> ${this.type}: localAudioUnmuteRequired received -> doing local unmute`\n );\n\n this.state.server.remoteMute = false;\n this.state.client.localMute = false;\n\n if (this.pendingPromiseReject) {\n this.pendingPromiseReject(\n new Error('Server requested local unmute - this overrides any client request in progress')\n );\n this.pendingPromiseResolve = null;\n this.pendingPromiseReject = null;\n }\n\n this.applyClientStateLocally(meeting);\n this.applyClientStateToServer(meeting);\n }\n\n /**\n * Returns true if the user is locally or remotely muted\n *\n * @public\n * @memberof MuteState\n * @returns {Boolean}\n */\n public isMuted() {\n return (\n this.state.client.localMute || this.state.server.localMute || this.state.server.remoteMute\n );\n }\n\n /**\n * Returns true if the user is muted as a result of the client request (and not remotely muted)\n *\n * @public\n * @memberof MuteState\n * @returns {Boolean}\n */\n public isSelf() {\n return this.state.client.localMute && !this.state.server.remoteMute;\n }\n\n // defined for backwards compatibility with the old AudioStateMachine/VideoStateMachine classes\n get muted() {\n return this.isMuted();\n }\n\n // defined for backwards compatibility with the old AudioStateMachine/VideoStateMachine classes\n get self() {\n return this.isSelf();\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.22",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"build": "yarn run -T tsc --declaration true --declarationDir ./dist/types"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@webex/plugin-meetings": "3.0.0-beta.
|
|
36
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
37
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
38
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
39
|
-
"@webex/test-helper-retry": "3.0.0-beta.
|
|
40
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
35
|
+
"@webex/plugin-meetings": "3.0.0-beta.22",
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-beta.22",
|
|
37
|
+
"@webex/test-helper-mocha": "3.0.0-beta.22",
|
|
38
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.22",
|
|
39
|
+
"@webex/test-helper-retry": "3.0.0-beta.22",
|
|
40
|
+
"@webex/test-helper-test-users": "3.0.0-beta.22",
|
|
41
41
|
"chai": "^4.3.4",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"jsdom-global": "3.0.2",
|
|
@@ -45,18 +45,18 @@
|
|
|
45
45
|
"typed-emitter": "^2.1.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@webex/common": "3.0.0-beta.
|
|
48
|
+
"@webex/common": "3.0.0-beta.22",
|
|
49
49
|
"@webex/internal-media-core": "1.33.0",
|
|
50
|
-
"@webex/internal-plugin-conversation": "3.0.0-beta.
|
|
51
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
52
|
-
"@webex/internal-plugin-llm": "3.0.0-beta.
|
|
53
|
-
"@webex/internal-plugin-mercury": "3.0.0-beta.
|
|
54
|
-
"@webex/internal-plugin-metrics": "3.0.0-beta.
|
|
55
|
-
"@webex/internal-plugin-support": "3.0.0-beta.
|
|
56
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
57
|
-
"@webex/plugin-people": "3.0.0-beta.
|
|
58
|
-
"@webex/plugin-rooms": "3.0.0-beta.
|
|
59
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
50
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.22",
|
|
51
|
+
"@webex/internal-plugin-device": "3.0.0-beta.22",
|
|
52
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.22",
|
|
53
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.22",
|
|
54
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.22",
|
|
55
|
+
"@webex/internal-plugin-support": "3.0.0-beta.22",
|
|
56
|
+
"@webex/internal-plugin-user": "3.0.0-beta.22",
|
|
57
|
+
"@webex/plugin-people": "3.0.0-beta.22",
|
|
58
|
+
"@webex/plugin-rooms": "3.0.0-beta.22",
|
|
59
|
+
"@webex/webex-core": "3.0.0-beta.22",
|
|
60
60
|
"ampersand-collection": "^2.0.2",
|
|
61
61
|
"bowser": "^2.11.0",
|
|
62
62
|
"btoa": "^1.2.1",
|
package/src/locus-info/index.ts
CHANGED
|
@@ -998,6 +998,8 @@ export default class LocusInfo extends EventsScope {
|
|
|
998
998
|
const parsedMediaShares = MediaSharesUtils.getMediaShares(this.mediaShares, mediaShares);
|
|
999
999
|
|
|
1000
1000
|
this.updateMeeting(parsedMediaShares.current);
|
|
1001
|
+
this.parsedLocus.mediaShares = parsedMediaShares.current;
|
|
1002
|
+
this.mediaShares = mediaShares;
|
|
1001
1003
|
this.emitScoped(
|
|
1002
1004
|
{
|
|
1003
1005
|
file: 'locus-info',
|
|
@@ -1009,8 +1011,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1009
1011
|
previous: parsedMediaShares.previous,
|
|
1010
1012
|
}
|
|
1011
1013
|
);
|
|
1012
|
-
this.parsedLocus.mediaShares = parsedMediaShares.current;
|
|
1013
|
-
this.mediaShares = mediaShares;
|
|
1014
1014
|
}
|
|
1015
1015
|
}
|
|
1016
1016
|
|
package/src/media/index.ts
CHANGED
|
@@ -425,37 +425,6 @@ Media.stopTracks = (track: any) => {
|
|
|
425
425
|
});
|
|
426
426
|
};
|
|
427
427
|
|
|
428
|
-
/**
|
|
429
|
-
*
|
|
430
|
-
* Stop input stream
|
|
431
|
-
* @param {Stream} stream A media stream
|
|
432
|
-
* @returns {null}
|
|
433
|
-
* @deprecated after v1.89.3
|
|
434
|
-
*/
|
|
435
|
-
Media.stopStream = (stream: any) => {
|
|
436
|
-
LoggerProxy.logger.warn(
|
|
437
|
-
'Media:index#stopStream --> [DEPRECATION WARNING]: stopStream has been deprecated after v1.89.3'
|
|
438
|
-
);
|
|
439
|
-
if (!stream) {
|
|
440
|
-
return Promise.resolve();
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/*
|
|
444
|
-
* To release local media
|
|
445
|
-
* 1) Chrome requires all tracks to be stopped (stream.stop got deprecated)
|
|
446
|
-
* 2) Firefox requires the stream to be stopped
|
|
447
|
-
*/
|
|
448
|
-
return Promise.resolve().then(() => {
|
|
449
|
-
if (stream.getTracks) {
|
|
450
|
-
stream.getTracks().forEach((track) => {
|
|
451
|
-
track.stop();
|
|
452
|
-
});
|
|
453
|
-
} else if (stream.stop) {
|
|
454
|
-
stream.stop();
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
};
|
|
458
|
-
|
|
459
428
|
/**
|
|
460
429
|
* generates streams for audio video and share
|
|
461
430
|
* @param {object} mediaSetting parameter
|
package/src/meeting/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import Roap from '../roap/index';
|
|
|
27
27
|
import Media from '../media';
|
|
28
28
|
import MediaProperties from '../media/properties';
|
|
29
29
|
import MeetingStateMachine from './state';
|
|
30
|
-
import createMuteState from './muteState';
|
|
30
|
+
import {createMuteState} from './muteState';
|
|
31
31
|
import createEffectsState from './effectsState';
|
|
32
32
|
import LocusInfo from '../locus-info';
|
|
33
33
|
import Metrics from '../metrics';
|
|
@@ -94,7 +94,6 @@ import {
|
|
|
94
94
|
RemoteMediaManager,
|
|
95
95
|
Event as RemoteMediaManagerEvent,
|
|
96
96
|
} from '../multistream/remoteMediaManager';
|
|
97
|
-
import {MultistreamMedia} from '../multistream/multistreamMedia';
|
|
98
97
|
import {
|
|
99
98
|
Reaction,
|
|
100
99
|
ReactionServerType,
|
|
@@ -470,7 +469,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
470
469
|
keepAliveTimerId: NodeJS.Timeout;
|
|
471
470
|
lastVideoLayoutInfo: any;
|
|
472
471
|
locusInfo: any;
|
|
473
|
-
media: MultistreamMedia;
|
|
474
472
|
mediaProperties: MediaProperties;
|
|
475
473
|
mediaRequestManagers: {
|
|
476
474
|
audio: MediaRequestManager;
|
|
@@ -1104,8 +1102,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1104
1102
|
this.locusInfo.init(attrs.locus ? attrs.locus : {});
|
|
1105
1103
|
this.hasJoinedOnce = false;
|
|
1106
1104
|
|
|
1107
|
-
this.media = new MultistreamMedia(this);
|
|
1108
|
-
|
|
1109
1105
|
/**
|
|
1110
1106
|
* helper class for managing remote streams
|
|
1111
1107
|
*/
|
|
@@ -1945,7 +1941,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1945
1941
|
*/
|
|
1946
1942
|
private setUpLocusMediaSharesListener() {
|
|
1947
1943
|
// Will get triggered on local and remote share
|
|
1948
|
-
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, (payload) => {
|
|
1944
|
+
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, async (payload) => {
|
|
1949
1945
|
const {content: contentShare, whiteboard: whiteboardShare} = payload.current;
|
|
1950
1946
|
const previousContentShare = payload.previous?.content;
|
|
1951
1947
|
const previousWhiteboardShare = payload.previous?.whiteboard;
|
|
@@ -1978,14 +1974,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1978
1974
|
contentShare.disposition === FLOOR_ACTION.GRANTED
|
|
1979
1975
|
) {
|
|
1980
1976
|
if (this.mediaProperties.shareTrack?.readyState === 'ended') {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1977
|
+
try {
|
|
1978
|
+
if (this.isMultistream) {
|
|
1979
|
+
await this.unpublishTracks([this.mediaProperties.shareTrack]); // todo screen share audio (SPARK-399690)
|
|
1980
|
+
} else {
|
|
1981
|
+
await this.stopShare({
|
|
1982
|
+
skipSignalingCheck: true,
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
} catch (error) {
|
|
1984
1986
|
LoggerProxy.logger.log(
|
|
1985
1987
|
'Meeting:index#setUpLocusMediaSharesListener --> Error stopping share: ',
|
|
1986
1988
|
error
|
|
1987
1989
|
);
|
|
1988
|
-
}
|
|
1990
|
+
}
|
|
1989
1991
|
} else {
|
|
1990
1992
|
// CONTENT - sharing content local
|
|
1991
1993
|
newShareStatus = SHARE_STATUS.LOCAL_SHARE_ACTIVE;
|
|
@@ -2077,19 +2079,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2077
2079
|
);
|
|
2078
2080
|
};
|
|
2079
2081
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2082
|
+
try {
|
|
2083
|
+
// if a remote participant is stealing the presentation from us
|
|
2084
|
+
if (
|
|
2085
|
+
this.mediaProperties.mediaDirection?.sendShare &&
|
|
2086
|
+
oldShareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE
|
|
2087
|
+
) {
|
|
2088
|
+
if (this.isMultistream) {
|
|
2089
|
+
await this.unpublishTracks([this.mediaProperties.shareTrack]); // todo screen share audio (SPARK-399690)
|
|
2090
|
+
} else {
|
|
2091
|
+
await this.updateShare({
|
|
2092
|
+
sendShare: false,
|
|
2093
|
+
receiveShare: this.mediaProperties.mediaDirection.receiveShare,
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
} finally {
|
|
2085
2098
|
sendStartedSharingRemote();
|
|
2086
|
-
} else {
|
|
2087
|
-
this.updateShare({
|
|
2088
|
-
sendShare: false,
|
|
2089
|
-
receiveShare: this.mediaProperties.mediaDirection.receiveShare,
|
|
2090
|
-
}).finally(() => {
|
|
2091
|
-
sendStartedSharingRemote();
|
|
2092
|
-
});
|
|
2093
2099
|
}
|
|
2094
2100
|
break;
|
|
2095
2101
|
}
|
|
@@ -3066,6 +3072,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3066
3072
|
);
|
|
3067
3073
|
this.mediaProperties.setLocalAudioTrack(audioTrack);
|
|
3068
3074
|
if (this.audio) this.audio.applyClientStateLocally(this);
|
|
3075
|
+
} else {
|
|
3076
|
+
this.mediaProperties.setLocalAudioTrack(null);
|
|
3069
3077
|
}
|
|
3070
3078
|
|
|
3071
3079
|
if (emitEvent) {
|
|
@@ -3112,6 +3120,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3112
3120
|
'Meeting:index#setLocalVideoTrack --> Video settings.',
|
|
3113
3121
|
JSON.stringify(this.mediaProperties.mediaSettings.video)
|
|
3114
3122
|
);
|
|
3123
|
+
} else {
|
|
3124
|
+
this.mediaProperties.setLocalVideoTrack(null);
|
|
3115
3125
|
}
|
|
3116
3126
|
|
|
3117
3127
|
if (emitEvent) {
|
|
@@ -3139,35 +3149,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3139
3149
|
|
|
3140
3150
|
/**
|
|
3141
3151
|
* Sets the local media stream on the class and emits an event to the developer
|
|
3142
|
-
* @param {
|
|
3152
|
+
* @param {MediaStreamTrack} localShareTrack the local media stream
|
|
3143
3153
|
* @returns {undefined}
|
|
3144
3154
|
* @public
|
|
3145
3155
|
* @memberof Meeting
|
|
3146
3156
|
*/
|
|
3147
|
-
public setLocalShareTrack(
|
|
3157
|
+
public setLocalShareTrack(localShareTrack: MediaStreamTrack) {
|
|
3148
3158
|
let settings = null;
|
|
3149
3159
|
|
|
3150
|
-
if (
|
|
3151
|
-
this.mediaProperties.setLocalShareTrack(
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
settings
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
JSON.stringify(this.mediaProperties.mediaSettings.screen)
|
|
3167
|
-
);
|
|
3168
|
-
}
|
|
3160
|
+
if (localShareTrack) {
|
|
3161
|
+
this.mediaProperties.setLocalShareTrack(localShareTrack);
|
|
3162
|
+
|
|
3163
|
+
settings = localShareTrack.getSettings();
|
|
3164
|
+
this.mediaProperties.setMediaSettings('screen', {
|
|
3165
|
+
aspectRatio: settings.aspectRatio,
|
|
3166
|
+
frameRate: settings.frameRate,
|
|
3167
|
+
height: settings.height,
|
|
3168
|
+
width: settings.width,
|
|
3169
|
+
displaySurface: settings.displaySurface,
|
|
3170
|
+
cursor: settings.cursor,
|
|
3171
|
+
});
|
|
3172
|
+
LoggerProxy.logger.log(
|
|
3173
|
+
'Meeting:index#setLocalShareTrack --> Screen settings.',
|
|
3174
|
+
JSON.stringify(this.mediaProperties.mediaSettings.screen)
|
|
3175
|
+
);
|
|
3169
3176
|
|
|
3170
|
-
|
|
3177
|
+
localShareTrack.addEventListener('ended', this.handleShareTrackEnded);
|
|
3171
3178
|
|
|
3172
3179
|
Trigger.trigger(
|
|
3173
3180
|
this,
|
|
@@ -3178,9 +3185,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3178
3185
|
EVENT_TRIGGERS.MEDIA_READY,
|
|
3179
3186
|
{
|
|
3180
3187
|
type: EVENT_TYPES.LOCAL_SHARE,
|
|
3181
|
-
|
|
3188
|
+
track: localShareTrack,
|
|
3182
3189
|
}
|
|
3183
3190
|
);
|
|
3191
|
+
} else if (this.mediaProperties.shareTrack) {
|
|
3192
|
+
this.mediaProperties.shareTrack.removeEventListener('ended', this.handleShareTrackEnded);
|
|
3193
|
+
this.mediaProperties.setLocalShareTrack(null);
|
|
3184
3194
|
}
|
|
3185
3195
|
}
|
|
3186
3196
|
|
|
@@ -5715,7 +5725,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5715
5725
|
|
|
5716
5726
|
const previousSendShareStatus = this.mediaProperties.mediaDirection.sendShare;
|
|
5717
5727
|
|
|
5718
|
-
this.setLocalShareTrack(
|
|
5728
|
+
this.setLocalShareTrack(track);
|
|
5719
5729
|
|
|
5720
5730
|
return MeetingUtil.validateOptions({sendShare, localShare: stream})
|
|
5721
5731
|
.then(() => this.checkForStopShare(sendShare, previousSendShareStatus))
|
|
@@ -5772,7 +5782,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5772
5782
|
this.video = this.video || createMuteState(VIDEO, this, this.mediaProperties.mediaDirection);
|
|
5773
5783
|
// Validation is already done in addMedia so no need to check if the lenght is greater then 0
|
|
5774
5784
|
this.setLocalTracks(localStream);
|
|
5775
|
-
this.setLocalShareTrack(localShare);
|
|
5785
|
+
this.setLocalShareTrack(MeetingUtil.getTrack(localShare).videoTrack);
|
|
5776
5786
|
}
|
|
5777
5787
|
|
|
5778
5788
|
/**
|
|
@@ -6560,9 +6570,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6560
6570
|
* @param {MediaStream} localShare
|
|
6561
6571
|
* @returns {undefined}
|
|
6562
6572
|
*/
|
|
6563
|
-
private handleShareTrackEnded(
|
|
6573
|
+
private handleShareTrackEnded = async () => {
|
|
6564
6574
|
if (this.wirelessShare) {
|
|
6565
6575
|
this.leave({reason: MEETING_REMOVED_REASON.USER_ENDED_SHARE_STREAMS});
|
|
6576
|
+
} else if (this.isMultistream) {
|
|
6577
|
+
try {
|
|
6578
|
+
if (this.mediaProperties.mediaDirection.sendShare) {
|
|
6579
|
+
await this.releaseScreenShareFloor();
|
|
6580
|
+
}
|
|
6581
|
+
} catch (error) {
|
|
6582
|
+
LoggerProxy.logger.log(
|
|
6583
|
+
'Meeting:index#handleShareTrackEnded --> Error stopping share: ',
|
|
6584
|
+
error
|
|
6585
|
+
);
|
|
6586
|
+
} finally {
|
|
6587
|
+
this.setLocalShareTrack(null);
|
|
6588
|
+
this.mediaProperties.mediaDirection.sendShare = false;
|
|
6589
|
+
}
|
|
6566
6590
|
} else {
|
|
6567
6591
|
// Skip checking for a stable peerConnection
|
|
6568
6592
|
// to allow immediately stopping screenshare
|
|
@@ -6585,10 +6609,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6585
6609
|
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
|
|
6586
6610
|
{
|
|
6587
6611
|
type: EVENT_TYPES.LOCAL_SHARE,
|
|
6588
|
-
stream: localShare,
|
|
6589
6612
|
}
|
|
6590
6613
|
);
|
|
6591
|
-
}
|
|
6614
|
+
};
|
|
6592
6615
|
|
|
6593
6616
|
/**
|
|
6594
6617
|
* Emits the 'network:quality' event
|
|
@@ -7117,4 +7140,111 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7117
7140
|
requestingParticipantId: this.members.selfId,
|
|
7118
7141
|
});
|
|
7119
7142
|
}
|
|
7143
|
+
|
|
7144
|
+
/**
|
|
7145
|
+
* Throws if we don't have a media connection created
|
|
7146
|
+
*
|
|
7147
|
+
* @returns {void}
|
|
7148
|
+
*/
|
|
7149
|
+
private checkMediaConnection() {
|
|
7150
|
+
if (this.mediaProperties?.webrtcMediaConnection) {
|
|
7151
|
+
return;
|
|
7152
|
+
}
|
|
7153
|
+
throw new Error('Webrtc media connection is missing, call addMedia() first');
|
|
7154
|
+
}
|
|
7155
|
+
|
|
7156
|
+
/**
|
|
7157
|
+
* Publishes specified local tracks in the meeting
|
|
7158
|
+
*
|
|
7159
|
+
* @param {Object} tracks
|
|
7160
|
+
* @returns {Promise}
|
|
7161
|
+
*/
|
|
7162
|
+
async publishTracks(tracks: {
|
|
7163
|
+
microphone?: MediaStreamTrack;
|
|
7164
|
+
camera?: MediaStreamTrack;
|
|
7165
|
+
screenShare: {
|
|
7166
|
+
audio?: MediaStreamTrack; // todo: for now screen share audio is not supported (will be done in SPARK-399690)
|
|
7167
|
+
video?: MediaStreamTrack;
|
|
7168
|
+
};
|
|
7169
|
+
}): Promise<void> {
|
|
7170
|
+
this.checkMediaConnection();
|
|
7171
|
+
|
|
7172
|
+
if (tracks.screenShare?.video) {
|
|
7173
|
+
// we are starting a screen share
|
|
7174
|
+
this.setLocalShareTrack(tracks.screenShare.video);
|
|
7175
|
+
|
|
7176
|
+
await this.requestScreenShareFloor();
|
|
7177
|
+
this.mediaProperties.mediaDirection.sendShare = true;
|
|
7178
|
+
|
|
7179
|
+
await this.mediaProperties.webrtcMediaConnection.publishTrack(
|
|
7180
|
+
tracks.screenShare.video,
|
|
7181
|
+
'slides'
|
|
7182
|
+
);
|
|
7183
|
+
}
|
|
7184
|
+
|
|
7185
|
+
if (tracks.microphone) {
|
|
7186
|
+
this.setLocalAudioTrack(tracks.microphone);
|
|
7187
|
+
this.mediaProperties.mediaDirection.sendAudio = true;
|
|
7188
|
+
|
|
7189
|
+
// audio mute state could be undefined if you have not sent audio before
|
|
7190
|
+
this.audio = this.audio || createMuteState(AUDIO, this, this.mediaProperties.mediaDirection);
|
|
7191
|
+
|
|
7192
|
+
await this.mediaProperties.webrtcMediaConnection.publishTrack(tracks.microphone, 'main');
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
if (tracks.camera) {
|
|
7196
|
+
this.setLocalVideoTrack(tracks.camera);
|
|
7197
|
+
this.mediaProperties.mediaDirection.sendVideo = true;
|
|
7198
|
+
|
|
7199
|
+
// video state could be undefined if you have not sent video before
|
|
7200
|
+
this.video = this.video || createMuteState(VIDEO, this, this.mediaProperties.mediaDirection);
|
|
7201
|
+
|
|
7202
|
+
await this.mediaProperties.webrtcMediaConnection.publishTrack(tracks.camera, 'main');
|
|
7203
|
+
}
|
|
7204
|
+
}
|
|
7205
|
+
|
|
7206
|
+
/**
|
|
7207
|
+
* Un-publishes specified local tracks in the meeting
|
|
7208
|
+
*
|
|
7209
|
+
* @param {Array<MediaStreamTrack>} tracks
|
|
7210
|
+
* @returns {Promise}
|
|
7211
|
+
*/
|
|
7212
|
+
async unpublishTracks(tracks: MediaStreamTrack[]): Promise<void> {
|
|
7213
|
+
this.checkMediaConnection();
|
|
7214
|
+
|
|
7215
|
+
const unpublishPromises = [];
|
|
7216
|
+
|
|
7217
|
+
for (const track of tracks) {
|
|
7218
|
+
if (track === this.mediaProperties.shareTrack) {
|
|
7219
|
+
this.setLocalShareTrack(null);
|
|
7220
|
+
|
|
7221
|
+
this.releaseScreenShareFloor(); // we ignore the returned promise here on purpose
|
|
7222
|
+
this.mediaProperties.mediaDirection.sendShare = false;
|
|
7223
|
+
|
|
7224
|
+
unpublishPromises.push(
|
|
7225
|
+
this.mediaProperties.webrtcMediaConnection.unpublishTrack(track, 'slides')
|
|
7226
|
+
);
|
|
7227
|
+
}
|
|
7228
|
+
|
|
7229
|
+
if (track === this.mediaProperties.audioTrack) {
|
|
7230
|
+
this.setLocalAudioTrack(null);
|
|
7231
|
+
this.mediaProperties.mediaDirection.sendAudio = false;
|
|
7232
|
+
|
|
7233
|
+
unpublishPromises.push(
|
|
7234
|
+
this.mediaProperties.webrtcMediaConnection.unpublishTrack(track, 'main')
|
|
7235
|
+
);
|
|
7236
|
+
}
|
|
7237
|
+
|
|
7238
|
+
if (track === this.mediaProperties.videoTrack) {
|
|
7239
|
+
this.setLocalVideoTrack(null);
|
|
7240
|
+
this.mediaProperties.mediaDirection.sendVideo = false;
|
|
7241
|
+
|
|
7242
|
+
unpublishPromises.push(
|
|
7243
|
+
this.mediaProperties.webrtcMediaConnection.unpublishTrack(track, 'main')
|
|
7244
|
+
);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
await Promise.all(unpublishPromises);
|
|
7249
|
+
}
|
|
7120
7250
|
}
|
package/src/meeting/muteState.ts
CHANGED
|
@@ -9,7 +9,8 @@ import {AUDIO, VIDEO} from '../constants';
|
|
|
9
9
|
If we ever need to support it, search for REMOTE_MUTE_VIDEO_MISSING_IMPLEMENTATION string to find the places that need updating
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
13
|
+
export const createMuteState = (type, meeting, mediaDirection) => {
|
|
13
14
|
if (type === AUDIO && !mediaDirection.sendAudio) {
|
|
14
15
|
return null;
|
|
15
16
|
}
|
|
@@ -350,5 +351,3 @@ class MuteState {
|
|
|
350
351
|
return this.isSelf();
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
|
-
|
|
354
|
-
export default createMuteState;
|