@webex/plugin-meetings 2.33.2 → 2.35.0

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.
@@ -240,13 +240,19 @@ var TurnDiscovery = /*#__PURE__*/function () {
240
240
  if (isAnyClusterReachable) {
241
241
  _loggerProxy.default.logger.info('Roap:turnDiscovery#doTurnDiscovery --> reachability has not failed, skipping TURN discovery');
242
242
 
243
- return _promise.default.resolve(undefined);
243
+ return _promise.default.resolve({
244
+ turnServerInfo: undefined,
245
+ turnDiscoverySkippedReason: 'reachability'
246
+ });
244
247
  }
245
248
 
246
249
  if (!meeting.config.experimental.enableTurnDiscovery) {
247
250
  _loggerProxy.default.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery disabled in config, skipping it');
248
251
 
249
- return _promise.default.resolve(undefined);
252
+ return _promise.default.resolve({
253
+ turnServerInfo: undefined,
254
+ turnDiscoverySkippedReason: 'config'
255
+ });
250
256
  }
251
257
 
252
258
  return this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting).then(function () {
@@ -258,7 +264,10 @@ var TurnDiscovery = /*#__PURE__*/function () {
258
264
 
259
265
  _loggerProxy.default.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');
260
266
 
261
- return _this2.turnInfo;
267
+ return {
268
+ turnServerInfo: _this2.turnInfo,
269
+ turnDiscoverySkippedReason: undefined
270
+ };
262
271
  }).catch(function (e) {
263
272
  // we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN
264
273
  _loggerProxy.default.logger.info("Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ".concat(e));
@@ -270,7 +279,10 @@ var TurnDiscovery = /*#__PURE__*/function () {
270
279
  stack: e.stack
271
280
  });
272
281
 
273
- return _promise.default.resolve(undefined);
282
+ return _promise.default.resolve({
283
+ turnServerInfo: undefined,
284
+ turnDiscoverySkippedReason: undefined
285
+ });
274
286
  });
275
287
  }
276
288
  }]);
@@ -1 +1 @@
1
- {"version":3,"names":["TURN_DISCOVERY_TIMEOUT","TurnDiscovery","roapRequest","turnInfo","url","username","password","defer","LoggerProxy","logger","warn","reject","Error","responseTimer","setTimeout","info","promise","roapMessage","headers","expectedHeaders","headerName","field","foundHeaders","forEach","receivedHeader","expectedHeader","startsWith","substring","length","clearTimeout","undefined","resolve","meeting","isReconnecting","seq","roapSeq","Defer","messageType","ROAP","ROAP_TYPES","TURN_DISCOVERY_REQUEST","version","ROAP_VERSION","sendRoap","correlationId","locusSelfUrl","selfUrl","mediaId","audioMuted","isAudioMuted","videoMuted","isVideoMuted","meetingId","id","then","mediaConnections","setRoapSeq","updateMediaConnections","OK","isAnyClusterReachable","webex","meetings","reachability","config","experimental","enableTurnDiscovery","sendRoapTurnDiscoveryRequest","waitForTurnDiscoveryResponse","sendRoapOK","catch","e","Metrics","sendBehavioralMetric","BEHAVIORAL_METRICS","TURN_DISCOVERY_FAILURE","correlation_id","locus_id","locusUrl","split","pop","reason","message","stack"],"sources":["turnDiscovery.ts"],"sourcesContent":["import {Defer} from '@webex/common';\n\nimport Metrics from '../metrics';\nimport BEHAVIORAL_METRICS from '../metrics/constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ROAP} from '../constants';\n\nimport RoapRequest from './request';\n\nconst TURN_DISCOVERY_TIMEOUT = 10; // in seconds\n\n/**\n * Handles the process of finding out TURN server information from Linus.\n * This is achieved by sending a TURN_DISCOVERY_REQUEST.\n */\nexport default class TurnDiscovery {\n private roapRequest: RoapRequest;\n\n private defer?: Defer; // used for waiting for the response\n\n private turnInfo: {\n url: string;\n username: string;\n password: string;\n };\n\n private responseTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Constructor\n *\n * @param {RoapRequest} roapRequest\n */\n constructor(roapRequest: RoapRequest) {\n this.roapRequest = roapRequest;\n this.turnInfo = {\n url: '',\n username: '',\n password: '',\n };\n }\n\n\n /**\n * waits for TURN_DISCOVERY_RESPONSE message to arrive\n *\n * @returns {Promise}\n * @private\n * @memberof Roap\n */\n waitForTurnDiscoveryResponse() {\n if (!this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#waitForTurnDiscoveryResponse --> TURN discovery is not in progress');\n\n return Promise.reject(new Error('waitForTurnDiscoveryResponse() called before sendRoapTurnDiscoveryRequest()'));\n }\n\n const {defer} = this;\n\n this.responseTimer = setTimeout(() => {\n LoggerProxy.logger.warn(`Roap:turnDiscovery#waitForTurnDiscoveryResponse --> timeout! no response arrived within ${TURN_DISCOVERY_TIMEOUT} seconds`);\n\n defer.reject(new Error('Timed out waiting for TURN_DISCOVERY_RESPONSE'));\n }, TURN_DISCOVERY_TIMEOUT * 1000);\n\n LoggerProxy.logger.info('Roap:turnDiscovery#waitForTurnDiscoveryResponse --> waiting for TURN_DISCOVERY_RESPONSE...');\n\n return defer.promise;\n }\n\n /**\n * handles TURN_DISCOVERY_RESPONSE roap message\n *\n * @param {Object} roapMessage\n * @returns {void}\n * @public\n * @memberof Roap\n */\n handleTurnDiscoveryResponse(roapMessage) {\n const {headers} = roapMessage;\n\n if (!this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#handleTurnDiscoveryResponse --> unexpected TURN discovery response');\n\n return;\n }\n\n const expectedHeaders = [\n {headerName: 'x-cisco-turn-url', field: 'url'},\n {headerName: 'x-cisco-turn-username', field: 'username'},\n {headerName: 'x-cisco-turn-password', field: 'password'},\n ];\n\n let foundHeaders = 0;\n\n headers?.forEach((receivedHeader) => {\n // check if it matches any of our expected headers\n expectedHeaders.forEach((expectedHeader) => {\n if (receivedHeader.startsWith(`${expectedHeader.headerName}=`)) {\n this.turnInfo[expectedHeader.field] = receivedHeader.substring(expectedHeader.headerName.length + 1);\n foundHeaders += 1;\n }\n });\n });\n\n clearTimeout(this.responseTimer);\n this.responseTimer = undefined;\n\n if (foundHeaders !== expectedHeaders.length) {\n LoggerProxy.logger.warn(`Roap:turnDiscovery#handleTurnDiscoveryResponse --> missing some headers, received: ${JSON.stringify(headers)}`);\n this.defer.reject(new Error(`TURN_DISCOVERY_RESPONSE missing some headers: ${JSON.stringify(headers)}`));\n }\n else {\n LoggerProxy.logger.info(`Roap:turnDiscovery#handleTurnDiscoveryResponse --> received a valid response, url=${this.turnInfo.url}`);\n this.defer.resolve();\n }\n }\n\n /**\n * sends the TURN_DISCOVERY_REQUEST roap request\n *\n * @param {Meeting} meeting\n * @param {Boolean} isReconnecting\n * @returns {Promise}\n * @private\n * @memberof Roap\n */\n sendRoapTurnDiscoveryRequest(meeting, isReconnecting) {\n const seq = meeting.roapSeq + 1;\n\n if (this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> already in progress');\n\n return Promise.resolve();\n }\n\n this.defer = new Defer();\n\n const roapMessage = {\n messageType: ROAP.ROAP_TYPES.TURN_DISCOVERY_REQUEST,\n version: ROAP.ROAP_VERSION,\n seq,\n };\n\n LoggerProxy.logger.info('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> sending TURN_DISCOVERY_REQUEST');\n\n return this.roapRequest\n .sendRoap({\n roapMessage,\n correlationId: meeting.correlationId,\n locusSelfUrl: meeting.selfUrl,\n mediaId: isReconnecting ? '' : meeting.mediaId,\n audioMuted: meeting.isAudioMuted(),\n videoMuted: meeting.isVideoMuted(),\n meetingId: meeting.id\n })\n .then(({mediaConnections}) => {\n meeting.setRoapSeq(seq);\n\n if (mediaConnections) {\n meeting.updateMediaConnections(mediaConnections);\n }\n });\n }\n\n /**\n * Sends the OK message that server expects to receive\n * after it sends us TURN_DISCOVERY_RESPONSE\n *\n * @param {Meeting} meeting\n * @returns {Promise}\n */\n sendRoapOK(meeting) {\n LoggerProxy.logger.info('Roap:turnDiscovery#sendRoapOK --> sending OK');\n\n return this.roapRequest.sendRoap({\n roapMessage: {\n messageType: ROAP.ROAP_TYPES.OK,\n version: ROAP.ROAP_VERSION,\n seq: meeting.roapSeq\n },\n locusSelfUrl: meeting.selfUrl,\n mediaId: meeting.mediaId,\n correlationId: meeting.correlationId,\n audioMuted: meeting.isAudioMuted(),\n videoMuted: meeting.isVideoMuted(),\n meetingId: meeting.id\n });\n }\n\n /**\n * Retrieves TURN server information from the backend by doing\n * a roap message exchange:\n * client server\n * | -----TURN_DISCOVERY_REQUEST-----> |\n * | <----TURN_DISCOVERY_RESPONSE----- |\n * | --------------OK----------------> |\n *\n * @param {Meeting} meeting\n * @param {Boolean} isReconnecting should be set to true if this is a new\n * media connection just after a reconnection\n * @returns {Promise}\n */\n doTurnDiscovery(meeting, isReconnecting) {\n const isAnyClusterReachable = meeting.webex.meetings.reachability.isAnyClusterReachable();\n\n if (isAnyClusterReachable) {\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> reachability has not failed, skipping TURN discovery');\n return Promise.resolve(undefined);\n }\n\n if (!meeting.config.experimental.enableTurnDiscovery) {\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery disabled in config, skipping it');\n\n return Promise.resolve(undefined);\n }\n\n return this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting)\n .then(() => this.waitForTurnDiscoveryResponse())\n .then(() => this.sendRoapOK(meeting))\n .then(() => {\n this.defer = undefined;\n\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');\n\n return this.turnInfo;\n })\n .catch((e) => {\n // we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN\n LoggerProxy.logger.info(`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${e}`);\n\n Metrics.sendBehavioralMetric(\n BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE,\n {\n correlation_id: meeting.correlationId,\n locus_id: meeting.locusUrl.split('/').pop(),\n reason: e.message,\n stack: e.stack\n }\n );\n\n return Promise.resolve(undefined);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["TURN_DISCOVERY_TIMEOUT","TurnDiscovery","roapRequest","turnInfo","url","username","password","defer","LoggerProxy","logger","warn","reject","Error","responseTimer","setTimeout","info","promise","roapMessage","headers","expectedHeaders","headerName","field","foundHeaders","forEach","receivedHeader","expectedHeader","startsWith","substring","length","clearTimeout","undefined","resolve","meeting","isReconnecting","seq","roapSeq","Defer","messageType","ROAP","ROAP_TYPES","TURN_DISCOVERY_REQUEST","version","ROAP_VERSION","sendRoap","correlationId","locusSelfUrl","selfUrl","mediaId","audioMuted","isAudioMuted","videoMuted","isVideoMuted","meetingId","id","then","mediaConnections","setRoapSeq","updateMediaConnections","OK","isAnyClusterReachable","webex","meetings","reachability","turnServerInfo","turnDiscoverySkippedReason","config","experimental","enableTurnDiscovery","sendRoapTurnDiscoveryRequest","waitForTurnDiscoveryResponse","sendRoapOK","catch","e","Metrics","sendBehavioralMetric","BEHAVIORAL_METRICS","TURN_DISCOVERY_FAILURE","correlation_id","locus_id","locusUrl","split","pop","reason","message","stack"],"sources":["turnDiscovery.ts"],"sourcesContent":["import {Defer} from '@webex/common';\n\nimport Metrics from '../metrics';\nimport BEHAVIORAL_METRICS from '../metrics/constants';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport {ROAP} from '../constants';\n\nimport RoapRequest from './request';\n\nconst TURN_DISCOVERY_TIMEOUT = 10; // in seconds\n\n/**\n * Handles the process of finding out TURN server information from Linus.\n * This is achieved by sending a TURN_DISCOVERY_REQUEST.\n */\nexport default class TurnDiscovery {\n private roapRequest: RoapRequest;\n\n private defer?: Defer; // used for waiting for the response\n\n private turnInfo: {\n url: string;\n username: string;\n password: string;\n };\n\n private responseTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Constructor\n *\n * @param {RoapRequest} roapRequest\n */\n constructor(roapRequest: RoapRequest) {\n this.roapRequest = roapRequest;\n this.turnInfo = {\n url: '',\n username: '',\n password: '',\n };\n }\n\n\n /**\n * waits for TURN_DISCOVERY_RESPONSE message to arrive\n *\n * @returns {Promise}\n * @private\n * @memberof Roap\n */\n waitForTurnDiscoveryResponse() {\n if (!this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#waitForTurnDiscoveryResponse --> TURN discovery is not in progress');\n\n return Promise.reject(new Error('waitForTurnDiscoveryResponse() called before sendRoapTurnDiscoveryRequest()'));\n }\n\n const {defer} = this;\n\n this.responseTimer = setTimeout(() => {\n LoggerProxy.logger.warn(`Roap:turnDiscovery#waitForTurnDiscoveryResponse --> timeout! no response arrived within ${TURN_DISCOVERY_TIMEOUT} seconds`);\n\n defer.reject(new Error('Timed out waiting for TURN_DISCOVERY_RESPONSE'));\n }, TURN_DISCOVERY_TIMEOUT * 1000);\n\n LoggerProxy.logger.info('Roap:turnDiscovery#waitForTurnDiscoveryResponse --> waiting for TURN_DISCOVERY_RESPONSE...');\n\n return defer.promise;\n }\n\n /**\n * handles TURN_DISCOVERY_RESPONSE roap message\n *\n * @param {Object} roapMessage\n * @returns {void}\n * @public\n * @memberof Roap\n */\n handleTurnDiscoveryResponse(roapMessage) {\n const {headers} = roapMessage;\n\n if (!this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#handleTurnDiscoveryResponse --> unexpected TURN discovery response');\n\n return;\n }\n\n const expectedHeaders = [\n {headerName: 'x-cisco-turn-url', field: 'url'},\n {headerName: 'x-cisco-turn-username', field: 'username'},\n {headerName: 'x-cisco-turn-password', field: 'password'},\n ];\n\n let foundHeaders = 0;\n\n headers?.forEach((receivedHeader) => {\n // check if it matches any of our expected headers\n expectedHeaders.forEach((expectedHeader) => {\n if (receivedHeader.startsWith(`${expectedHeader.headerName}=`)) {\n this.turnInfo[expectedHeader.field] = receivedHeader.substring(expectedHeader.headerName.length + 1);\n foundHeaders += 1;\n }\n });\n });\n\n clearTimeout(this.responseTimer);\n this.responseTimer = undefined;\n\n if (foundHeaders !== expectedHeaders.length) {\n LoggerProxy.logger.warn(`Roap:turnDiscovery#handleTurnDiscoveryResponse --> missing some headers, received: ${JSON.stringify(headers)}`);\n this.defer.reject(new Error(`TURN_DISCOVERY_RESPONSE missing some headers: ${JSON.stringify(headers)}`));\n }\n else {\n LoggerProxy.logger.info(`Roap:turnDiscovery#handleTurnDiscoveryResponse --> received a valid response, url=${this.turnInfo.url}`);\n this.defer.resolve();\n }\n }\n\n /**\n * sends the TURN_DISCOVERY_REQUEST roap request\n *\n * @param {Meeting} meeting\n * @param {Boolean} isReconnecting\n * @returns {Promise}\n * @private\n * @memberof Roap\n */\n sendRoapTurnDiscoveryRequest(meeting, isReconnecting) {\n const seq = meeting.roapSeq + 1;\n\n if (this.defer) {\n LoggerProxy.logger.warn('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> already in progress');\n\n return Promise.resolve();\n }\n\n this.defer = new Defer();\n\n const roapMessage = {\n messageType: ROAP.ROAP_TYPES.TURN_DISCOVERY_REQUEST,\n version: ROAP.ROAP_VERSION,\n seq,\n };\n\n LoggerProxy.logger.info('Roap:turnDiscovery#sendRoapTurnDiscoveryRequest --> sending TURN_DISCOVERY_REQUEST');\n\n return this.roapRequest\n .sendRoap({\n roapMessage,\n correlationId: meeting.correlationId,\n locusSelfUrl: meeting.selfUrl,\n mediaId: isReconnecting ? '' : meeting.mediaId,\n audioMuted: meeting.isAudioMuted(),\n videoMuted: meeting.isVideoMuted(),\n meetingId: meeting.id\n })\n .then(({mediaConnections}) => {\n meeting.setRoapSeq(seq);\n\n if (mediaConnections) {\n meeting.updateMediaConnections(mediaConnections);\n }\n });\n }\n\n /**\n * Sends the OK message that server expects to receive\n * after it sends us TURN_DISCOVERY_RESPONSE\n *\n * @param {Meeting} meeting\n * @returns {Promise}\n */\n sendRoapOK(meeting) {\n LoggerProxy.logger.info('Roap:turnDiscovery#sendRoapOK --> sending OK');\n\n return this.roapRequest.sendRoap({\n roapMessage: {\n messageType: ROAP.ROAP_TYPES.OK,\n version: ROAP.ROAP_VERSION,\n seq: meeting.roapSeq\n },\n locusSelfUrl: meeting.selfUrl,\n mediaId: meeting.mediaId,\n correlationId: meeting.correlationId,\n audioMuted: meeting.isAudioMuted(),\n videoMuted: meeting.isVideoMuted(),\n meetingId: meeting.id\n });\n }\n\n /**\n * Retrieves TURN server information from the backend by doing\n * a roap message exchange:\n * client server\n * | -----TURN_DISCOVERY_REQUEST-----> |\n * | <----TURN_DISCOVERY_RESPONSE----- |\n * | --------------OK----------------> |\n *\n * @param {Meeting} meeting\n * @param {Boolean} isReconnecting should be set to true if this is a new\n * media connection just after a reconnection\n * @returns {Promise}\n */\n doTurnDiscovery(meeting, isReconnecting) {\n const isAnyClusterReachable = meeting.webex.meetings.reachability.isAnyClusterReachable();\n\n if (isAnyClusterReachable) {\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> reachability has not failed, skipping TURN discovery');\n return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: 'reachability'});\n }\n\n if (!meeting.config.experimental.enableTurnDiscovery) {\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery disabled in config, skipping it');\n\n return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});\n }\n\n return this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting)\n .then(() => this.waitForTurnDiscoveryResponse())\n .then(() => this.sendRoapOK(meeting))\n .then(() => {\n this.defer = undefined;\n\n LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');\n\n return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};\n })\n .catch((e) => {\n // we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN\n LoggerProxy.logger.info(`Roap:turnDiscovery#doTurnDiscovery --> TURN discovery failed, continuing without TURN: ${e}`);\n\n Metrics.sendBehavioralMetric(\n BEHAVIORAL_METRICS.TURN_DISCOVERY_FAILURE,\n {\n correlation_id: meeting.correlationId,\n locus_id: meeting.locusUrl.split('/').pop(),\n reason: e.message,\n stack: e.stack\n }\n );\n\n return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});\n });\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": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "contributors": [
@@ -24,30 +24,30 @@
24
24
  ]
25
25
  },
26
26
  "devDependencies": {
27
- "@webex/plugin-meetings": "2.33.2",
28
- "@webex/test-helper-chai": "2.33.2",
29
- "@webex/test-helper-mocha": "2.33.2",
30
- "@webex/test-helper-mock-webex": "2.33.2",
31
- "@webex/test-helper-retry": "2.33.2",
32
- "@webex/test-helper-test-users": "2.33.2",
27
+ "@webex/plugin-meetings": "2.35.0",
28
+ "@webex/test-helper-chai": "2.35.0",
29
+ "@webex/test-helper-mocha": "2.35.0",
30
+ "@webex/test-helper-mock-webex": "2.35.0",
31
+ "@webex/test-helper-retry": "2.35.0",
32
+ "@webex/test-helper-test-users": "2.35.0",
33
33
  "chai": "^4.3.4",
34
34
  "chai-as-promised": "^7.1.1",
35
35
  "jsdom-global": "3.0.2",
36
36
  "sinon": "^9.2.4"
37
37
  },
38
38
  "dependencies": {
39
- "@webex/common": "2.33.2",
39
+ "@webex/common": "2.35.0",
40
40
  "@webex/internal-media-core": "^0.0.7-beta",
41
- "@webex/internal-plugin-conversation": "2.33.2",
42
- "@webex/internal-plugin-device": "2.33.2",
43
- "@webex/internal-plugin-mercury": "2.33.2",
44
- "@webex/internal-plugin-metrics": "2.33.2",
45
- "@webex/internal-plugin-support": "2.33.2",
46
- "@webex/internal-plugin-user": "2.33.2",
47
- "@webex/plugin-people": "2.33.2",
48
- "@webex/plugin-rooms": "2.33.2",
41
+ "@webex/internal-plugin-conversation": "2.35.0",
42
+ "@webex/internal-plugin-device": "2.35.0",
43
+ "@webex/internal-plugin-mercury": "2.35.0",
44
+ "@webex/internal-plugin-metrics": "2.35.0",
45
+ "@webex/internal-plugin-support": "2.35.0",
46
+ "@webex/internal-plugin-user": "2.35.0",
47
+ "@webex/plugin-people": "2.35.0",
48
+ "@webex/plugin-rooms": "2.35.0",
49
49
  "@webex/ts-sdp": "^1.0.1",
50
- "@webex/webex-core": "2.33.2",
50
+ "@webex/webex-core": "2.35.0",
51
51
  "bowser": "^2.11.0",
52
52
  "btoa": "^1.2.1",
53
53
  "dotenv": "^4.0.0",
package/src/constants.ts CHANGED
@@ -972,7 +972,7 @@ export const QUALITY_LEVELS = {
972
972
  };
973
973
 
974
974
 
975
- export const AVALIABLE_RESOLUTIONS = {
975
+ export const AVAILABLE_RESOLUTIONS = {
976
976
  '360p': {
977
977
  video: {
978
978
  width: {
@@ -1024,13 +1024,13 @@ export const AVALIABLE_RESOLUTIONS = {
1024
1024
  };
1025
1025
 
1026
1026
  export const VIDEO_RESOLUTIONS = {
1027
- [QUALITY_LEVELS.LOW]: AVALIABLE_RESOLUTIONS['480p'],
1028
- [QUALITY_LEVELS.MEDIUM]: AVALIABLE_RESOLUTIONS['720p'],
1029
- [QUALITY_LEVELS.HIGH]: AVALIABLE_RESOLUTIONS['1080p'],
1030
- [QUALITY_LEVELS['360p']]: AVALIABLE_RESOLUTIONS['360p'],
1031
- [QUALITY_LEVELS['480p']]: AVALIABLE_RESOLUTIONS['480p'],
1032
- [QUALITY_LEVELS['720p']]: AVALIABLE_RESOLUTIONS['720p'],
1033
- [QUALITY_LEVELS['1080p']]: AVALIABLE_RESOLUTIONS['1080p'],
1027
+ [QUALITY_LEVELS.LOW]: AVAILABLE_RESOLUTIONS['480p'],
1028
+ [QUALITY_LEVELS.MEDIUM]: AVAILABLE_RESOLUTIONS['720p'],
1029
+ [QUALITY_LEVELS.HIGH]: AVAILABLE_RESOLUTIONS['1080p'],
1030
+ [QUALITY_LEVELS['360p']]: AVAILABLE_RESOLUTIONS['360p'],
1031
+ [QUALITY_LEVELS['480p']]: AVAILABLE_RESOLUTIONS['480p'],
1032
+ [QUALITY_LEVELS['720p']]: AVAILABLE_RESOLUTIONS['720p'],
1033
+ [QUALITY_LEVELS['1080p']]: AVAILABLE_RESOLUTIONS['1080p'],
1034
1034
  };
1035
1035
 
1036
1036
  /**
@@ -1,5 +1,7 @@
1
1
  import {
2
+ ICE_STATE,
2
3
  MEETINGS,
4
+ PC_BAIL_TIMEOUT,
3
5
  QUALITY_LEVELS
4
6
  } from '../constants';
5
7
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -195,4 +197,99 @@ export default class MediaProperties {
195
197
  this.unsetLocalVideoTrack();
196
198
  this.unsetRemoteMedia();
197
199
  }
200
+
201
+ /**
202
+ * Waits until ice connection is established
203
+ *
204
+ * @returns {Promise<void>}
205
+ */
206
+ waitForIceConnectedState() {
207
+ const isIceConnected = () => (
208
+ this.peerConnection.iceConnectionState === ICE_STATE.CONNECTED ||
209
+ this.peerConnection.iceConnectionState === ICE_STATE.COMPLETED
210
+ );
211
+
212
+ if (isIceConnected()) {
213
+ return Promise.resolve();
214
+ }
215
+
216
+ return new Promise((resolve, reject) => {
217
+ let timer;
218
+
219
+ const iceListener = () => {
220
+ LoggerProxy.logger.log(`Media:properties#waitForIceConnectedState --> ice state: ${this.peerConnection.iceConnectionState}, conn state: ${this.peerConnection.connectionState}`);
221
+
222
+ if (isIceConnected()) {
223
+ clearTimeout(timer);
224
+ this.peerConnection.removeEventListener('iceconnectionstatechange', iceListener);
225
+ resolve();
226
+ }
227
+ };
228
+
229
+ timer = setTimeout(() => {
230
+ this.peerConnection.removeEventListener('iceconnectionstatechange', iceListener);
231
+ reject();
232
+ }, PC_BAIL_TIMEOUT);
233
+
234
+ this.peerConnection.addEventListener('iceconnectionstatechange', iceListener);
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Returns the type of a connection that has been established
240
+ *
241
+ * @returns {Promise<'UDP' | 'TCP' | 'TURN-TLS' | 'TURN-TCP' | 'TURN-UDP' | 'unknown'>}
242
+ */
243
+ async getCurrentConnectionType() {
244
+ // we can only get the connection type after ICE connection has been established
245
+ await this.waitForIceConnectedState();
246
+
247
+ const allStatsReports = [];
248
+
249
+ try {
250
+ // eslint-disable-next-line no-await-in-loop
251
+ const statsResult = await this.peerConnection.getStats();
252
+
253
+ statsResult.forEach((report) => allStatsReports.push(report));
254
+ }
255
+ catch (error) {
256
+ LoggerProxy.logger.warn(`Media:properties#getCurrentConnectionType --> getStats() failed: ${error}`);
257
+ }
258
+
259
+ const successfulCandidatePairs = allStatsReports.filter(
260
+ (report) => report.type === 'candidate-pair' && report.state?.toLowerCase() === 'succeeded'
261
+ );
262
+
263
+ let foundConnectionType = 'unknown';
264
+
265
+ // all of the successful pairs should have the same connection type, so just return the type for the first one
266
+ successfulCandidatePairs.some((pair) => {
267
+ const localCandidate = allStatsReports.find((report) => report.type === 'local-candidate' && report.id === pair.localCandidateId);
268
+
269
+ if (localCandidate === undefined) {
270
+ LoggerProxy.logger.warn(`Media:properties#getCurrentConnectionType --> failed to find local candidate "${pair.localCandidateId}" in getStats() results`);
271
+
272
+ return false;
273
+ }
274
+
275
+ let connectionType;
276
+
277
+ if (localCandidate.relayProtocol) {
278
+ connectionType = `TURN-${localCandidate.relayProtocol.toUpperCase()}`;
279
+ }
280
+ else {
281
+ connectionType = localCandidate.protocol?.toUpperCase(); // it will be UDP or TCP
282
+ }
283
+
284
+ if (connectionType) {
285
+ foundConnectionType = connectionType;
286
+
287
+ return true;
288
+ }
289
+
290
+ return false;
291
+ });
292
+
293
+ return foundConnectionType;
294
+ }
198
295
  }
@@ -1,5 +1,5 @@
1
1
  import uuid from 'uuid';
2
- import {cloneDeep, isEqual, pick} from 'lodash';
2
+ import {cloneDeep, isEqual, pick, isString} from 'lodash';
3
3
  import {StatelessWebexPlugin} from '@webex/webex-core';
4
4
  import {Media as WebRTCMedia} from '@webex/internal-media-core';
5
5
 
@@ -36,7 +36,6 @@ import {
36
36
  _INCOMING_,
37
37
  _JOIN_,
38
38
  AUDIO,
39
- CONNECTION_STATE,
40
39
  CONTENT,
41
40
  ENDED,
42
41
  EVENT_TRIGGERS,
@@ -58,7 +57,6 @@ import {
58
57
  ONLINE,
59
58
  OFFLINE,
60
59
  PASSWORD_STATUS,
61
- PC_BAIL_TIMEOUT,
62
60
  PSTN_STATUS,
63
61
  QUALITY_LEVELS,
64
62
  RECORDING_STATE,
@@ -2746,7 +2744,7 @@ export default class Meeting extends StatelessWebexPlugin {
2746
2744
  const {localQualityLevel} = this.mediaProperties;
2747
2745
 
2748
2746
  if (Number(localQualityLevel.slice(0, -1)) > height) {
2749
- LoggerProxy.logger.error(`Meeting:index#setLocalVideoTrack --> Local video quality of ${localQualityLevel} not supported,
2747
+ LoggerProxy.logger.warn(`Meeting:index#setLocalVideoTrack --> Local video quality of ${localQualityLevel} not supported,
2750
2748
  downscaling to highest possible resolution of ${height}p`);
2751
2749
 
2752
2750
  this.mediaProperties.setLocalQualityLevel(`${height}p`);
@@ -4014,6 +4012,16 @@ export default class Meeting extends StatelessWebexPlugin {
4014
4012
  LoggerProxy.logger.warn('Meeting:index#getMediaStreams --> Please use `meeting.shareScreen()` to manually start the screen share after successfully joining the meeting');
4015
4013
  }
4016
4014
 
4015
+ if (audioVideo && isString(audioVideo)) {
4016
+ if (Object.keys(VIDEO_RESOLUTIONS).includes(audioVideo)) {
4017
+ this.mediaProperties.setLocalQualityLevel(audioVideo);
4018
+ audioVideo = {video: VIDEO_RESOLUTIONS[audioVideo].video};
4019
+ }
4020
+ else {
4021
+ throw new ParameterError(`${audioVideo} not supported. Either pass level from pre-defined resolutions or pass complete audioVideo object`);
4022
+ }
4023
+ }
4024
+
4017
4025
  if (!audioVideo.video) {
4018
4026
  audioVideo = {...audioVideo, video: {...audioVideo.video, ...VIDEO_RESOLUTIONS[this.mediaProperties.localQualityLevel].video}};
4019
4027
  }
@@ -4184,6 +4192,9 @@ export default class Meeting extends StatelessWebexPlugin {
4184
4192
  addMedia(options = {}) {
4185
4193
  const LOG_HEADER = 'Meeting:index#addMedia -->';
4186
4194
 
4195
+ let turnDiscoverySkippedReason;
4196
+ let turnServerUsed = false;
4197
+
4187
4198
  if (this.meetingState !== FULL_STATE.ACTIVE) {
4188
4199
  return Promise.reject(new MeetingNotActiveError());
4189
4200
  }
@@ -4225,7 +4236,12 @@ export default class Meeting extends StatelessWebexPlugin {
4225
4236
 
4226
4237
  return MeetingUtil.validateOptions(options)
4227
4238
  .then(() => this.roap.doTurnDiscovery(this, false))
4228
- .then((turnServerInfo) => {
4239
+ .then((turnDiscoveryObject) => {
4240
+ ({turnDiscoverySkippedReason} = turnDiscoveryObject);
4241
+ turnServerUsed = !turnDiscoverySkippedReason;
4242
+
4243
+ const {turnServerInfo} = turnDiscoveryObject;
4244
+
4229
4245
  this.mediaProperties.setMediaPeerConnection(MediaUtil.createPeerConnection(turnServerInfo));
4230
4246
  this.setMercuryListener();
4231
4247
  PeerConnectionManager.setPeerConnectionEvents(this);
@@ -4238,127 +4254,110 @@ export default class Meeting extends StatelessWebexPlugin {
4238
4254
  enableRtx: this.config.enableRtx,
4239
4255
  enableExtmap: this.config.enableExtmap,
4240
4256
  setStartLocalSDPGenRemoteSDPRecvDelay: this.setStartLocalSDPGenRemoteSDPRecvDelay.bind(this)
4241
- })
4242
- .then((peerConnection) => this.getDevices().then((devices) => {
4243
- MeetingUtil.handleDeviceLogging(devices);
4257
+ }))
4258
+ .then((peerConnection) => this.getDevices().then((devices) => {
4259
+ MeetingUtil.handleDeviceLogging(devices);
4244
4260
 
4245
- return peerConnection;
4246
- }))
4247
- .then((peerConnection) => {
4248
- this.handleMediaLogging(this.mediaProperties);
4249
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection Received from attachMedia `);
4261
+ return peerConnection;
4262
+ }))
4263
+ .then((peerConnection) => {
4264
+ this.handleMediaLogging(this.mediaProperties);
4265
+ LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection Received from attachMedia `);
4266
+
4267
+ this.setRemoteStream(peerConnection);
4268
+ if (this.config.stats.enableStatsAnalyzer) {
4269
+ // TODO: ** Dont re create StatsAnalyzer on reconnect or rejoin
4270
+ this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
4271
+ this.statsAnalyzer = new StatsAnalyzer(this.config.stats, this.networkQualityMonitor);
4272
+ this.setupStatsAnalyzerEventHandlers();
4273
+ this.networkQualityMonitor.on(EVENT_TRIGGERS.NETWORK_QUALITY, this.sendNetworkQualityEvent.bind(this));
4274
+ }
4275
+ })
4276
+ .catch((error) => {
4277
+ LoggerProxy.logger.error(`${LOG_HEADER} Error adding media , setting up peerconnection, `, error);
4250
4278
 
4251
- this.setRemoteStream(peerConnection);
4252
- if (this.config.stats.enableStatsAnalyzer) {
4253
- // TODO: ** Dont re create StatsAnalyzer on reconnect or rejoin
4254
- this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
4255
- this.statsAnalyzer = new StatsAnalyzer(this.config.stats, this.networkQualityMonitor);
4256
- this.setupStatsAnalyzerEventHandlers();
4257
- this.networkQualityMonitor.on(EVENT_TRIGGERS.NETWORK_QUALITY, this.sendNetworkQualityEvent.bind(this));
4279
+ Metrics.sendBehavioralMetric(
4280
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
4281
+ {
4282
+ correlation_id: this.correlationId,
4283
+ locus_id: this.locusUrl.split('/').pop(),
4284
+ reason: error.message,
4285
+ stack: error.stack,
4286
+ turnDiscoverySkippedReason,
4287
+ turnServerUsed
4258
4288
  }
4259
- })
4260
- .catch((error) => {
4261
- LoggerProxy.logger.error(`${LOG_HEADER} Error adding media , setting up peerconnection, `, error);
4262
-
4263
- Metrics.sendBehavioralMetric(
4264
- BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
4265
- {
4266
- correlation_id: this.correlationId,
4267
- locus_id: this.locusUrl.split('/').pop(),
4268
- reason: error.message,
4269
- stack: error.stack
4270
- }
4271
- );
4289
+ );
4272
4290
 
4273
- throw error;
4274
- })
4275
- .then(() => new Promise((resolve, reject) => {
4276
- let timerCount = 0;
4291
+ throw error;
4292
+ })
4293
+ .then(() => new Promise((resolve, reject) => {
4294
+ let timerCount = 0;
4277
4295
 
4278
- // eslint-disable-next-line func-names
4279
- // eslint-disable-next-line prefer-arrow-callback
4280
- if (this.type === _CALL_) {
4296
+ // eslint-disable-next-line func-names
4297
+ // eslint-disable-next-line prefer-arrow-callback
4298
+ if (this.type === _CALL_) {
4299
+ resolve();
4300
+ }
4301
+ const joiningTimer = setInterval(() => {
4302
+ timerCount += 1;
4303
+ if (this.meetingState === FULL_STATE.ACTIVE) {
4304
+ clearInterval(joiningTimer);
4281
4305
  resolve();
4282
4306
  }
4283
- const joiningTimer = setInterval(() => {
4284
- timerCount += 1;
4285
- if (this.meetingState === FULL_STATE.ACTIVE) {
4286
- clearInterval(joiningTimer);
4287
- resolve();
4288
- }
4289
4307
 
4290
- if (timerCount === 4) {
4291
- clearInterval(joiningTimer);
4292
- reject(new Error('Meeting is still not active '));
4293
- }
4294
- }, 1000);
4308
+ if (timerCount === 4) {
4309
+ clearInterval(joiningTimer);
4310
+ reject(new Error('Meeting is still not active '));
4311
+ }
4312
+ }, 1000);
4313
+ }))
4314
+ .then(() =>
4315
+ logRequest(this.roap
4316
+ .sendRoapMediaRequest({
4317
+ sdp: this.mediaProperties.peerConnection.sdp,
4318
+ roapSeq: this.roapSeq,
4319
+ meeting: this // or can pass meeting ID
4320
+ }), {
4321
+ header: `${LOG_HEADER} Send Roap Media Request.`,
4322
+ success: `${LOG_HEADER} Successfully send roap media request`,
4323
+ failure: `${LOG_HEADER} Error joining the call on send roap media request, `
4295
4324
  }))
4296
- .then(() =>
4297
- logRequest(this.roap
4298
- .sendRoapMediaRequest({
4299
- sdp: this.mediaProperties.peerConnection.sdp,
4300
- roapSeq: this.roapSeq,
4301
- meeting: this // or can pass meeting ID
4302
- }), {
4303
- header: `${LOG_HEADER} Send Roap Media Request.`,
4304
- success: `${LOG_HEADER} Successfully send roap media request`,
4305
- failure: `${LOG_HEADER} Error joining the call on send roap media request, `
4306
- }))
4307
- .then(() => {
4308
- const {peerConnection} = this.mediaProperties;
4309
-
4310
- return new Promise((resolve, reject) => {
4311
- if (peerConnection.connectionState === CONNECTION_STATE.CONNECTED) {
4312
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection CONNECTED`);
4313
-
4314
- resolve(peerConnection);
4315
-
4316
- return;
4317
- }
4318
- // Check if Peer Connection is STABLE (connected)
4319
- const stabilityTimeout = setTimeout(() => {
4320
- if (peerConnection.connectionState !== CONNECTION_STATE.CONNECTED) {
4321
- // TODO: Fix this after the error code pr goes in
4322
- reject(createMeetingsError(30202, 'Meeting connection failed'));
4323
- }
4324
- else {
4325
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection CONNECTED`);
4326
- resolve(peerConnection);
4327
- }
4328
- }, PC_BAIL_TIMEOUT);
4329
-
4330
- this.once(EVENT_TRIGGERS.MEDIA_READY, () => {
4331
- LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection CONNECTED, clearing stability timer.`);
4332
- clearTimeout(stabilityTimeout);
4333
- resolve(peerConnection);
4334
- });
4335
- });
4336
- })
4337
- .then(() => {
4338
- if (mediaSettings && mediaSettings.sendShare && localShare) {
4339
- if (this.state === MEETING_STATE.STATES.JOINED) {
4340
- return this.share();
4341
- }
4325
+ .then(
4326
+ () => this.mediaProperties.waitForIceConnectedState()
4327
+ .catch(() => {
4328
+ throw createMeetingsError(30202, 'Meeting connection failed');
4329
+ })
4330
+ )
4331
+ .then(() => {
4332
+ LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection CONNECTED`);
4342
4333
 
4343
- // When the self state changes to JOINED then request the floor
4344
- this.floorGrantPending = true;
4334
+ if (mediaSettings && mediaSettings.sendShare && localShare) {
4335
+ if (this.state === MEETING_STATE.STATES.JOINED) {
4336
+ return this.share();
4345
4337
  }
4346
4338
 
4347
- Metrics.sendBehavioralMetric(
4348
- BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
4349
- {
4350
- correlation_id: this.correlationId,
4351
- locus_id: this.locusUrl.split('/').pop()
4352
- }
4353
- );
4339
+ // When the self state changes to JOINED then request the floor
4340
+ this.floorGrantPending = true;
4341
+ }
4354
4342
 
4355
- return Promise.resolve();
4356
- }))
4343
+ return {};
4344
+ })
4345
+ .then(() => this.mediaProperties.getCurrentConnectionType())
4346
+ .then((connectionType) => {
4347
+ Metrics.sendBehavioralMetric(
4348
+ BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
4349
+ {
4350
+ correlation_id: this.correlationId,
4351
+ locus_id: this.locusUrl.split('/').pop(),
4352
+ connectionType
4353
+ }
4354
+ );
4355
+ })
4357
4356
  .catch((error) => {
4358
4357
  // Clean up stats analyzer, peer connection, and turn off listeners
4359
4358
  const stopStatsAnalyzer = (this.statsAnalyzer) ? this.statsAnalyzer.stopAnalyzer() : Promise.resolve();
4360
4359
 
4361
- stopStatsAnalyzer
4360
+ return stopStatsAnalyzer
4362
4361
  .then(() => {
4363
4362
  this.statsAnalyzer = null;
4364
4363
 
@@ -4376,7 +4375,9 @@ export default class Meeting extends StatelessWebexPlugin {
4376
4375
  locus_id: this.locusUrl.split('/').pop(),
4377
4376
  reason: error.message,
4378
4377
  stack: error.stack,
4379
- code: error.code
4378
+ code: error.code,
4379
+ turnDiscoverySkippedReason,
4380
+ turnServerUsed
4380
4381
  }
4381
4382
  );
4382
4383
 
@@ -206,13 +206,13 @@ export default class TurnDiscovery {
206
206
 
207
207
  if (isAnyClusterReachable) {
208
208
  LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> reachability has not failed, skipping TURN discovery');
209
- return Promise.resolve(undefined);
209
+ return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: 'reachability'});
210
210
  }
211
211
 
212
212
  if (!meeting.config.experimental.enableTurnDiscovery) {
213
213
  LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery disabled in config, skipping it');
214
214
 
215
- return Promise.resolve(undefined);
215
+ return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});
216
216
  }
217
217
 
218
218
  return this.sendRoapTurnDiscoveryRequest(meeting, isReconnecting)
@@ -223,7 +223,7 @@ export default class TurnDiscovery {
223
223
 
224
224
  LoggerProxy.logger.info('Roap:turnDiscovery#doTurnDiscovery --> TURN discovery completed');
225
225
 
226
- return this.turnInfo;
226
+ return {turnServerInfo: this.turnInfo, turnDiscoverySkippedReason: undefined};
227
227
  })
228
228
  .catch((e) => {
229
229
  // we catch any errors and resolve with no turn information so that the normal call join flow can continue without TURN
@@ -239,7 +239,7 @@ export default class TurnDiscovery {
239
239
  }
240
240
  );
241
241
 
242
- return Promise.resolve(undefined);
242
+ return Promise.resolve({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
243
243
  });
244
244
  }
245
245
  }