@webex/plugin-meetings 3.4.0-next.3 → 3.4.0-next.5

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.
@@ -48,7 +48,7 @@ var RtcMetrics = exports.default = /*#__PURE__*/function () {
48
48
  (0, _defineProperty2.default)(this, "meetingId", void 0);
49
49
  (0, _defineProperty2.default)(this, "correlationId", void 0);
50
50
  (0, _defineProperty2.default)(this, "connectionId", void 0);
51
- (0, _defineProperty2.default)(this, "initialMetricsSent", void 0);
51
+ (0, _defineProperty2.default)(this, "shouldSendMetricsOnNextStatsReport", void 0);
52
52
  // `window` is used to prevent typescript from returning a NodeJS.Timer.
53
53
  this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);
54
54
  this.meetingId = meetingId;
@@ -71,6 +71,20 @@ var RtcMetrics = exports.default = /*#__PURE__*/function () {
71
71
  }
72
72
  }
73
73
 
74
+ /**
75
+ * Forces sending metrics when we get the next stats-report
76
+ *
77
+ * This is useful for cases when something important happens that affects the media connection,
78
+ * for example when we move from lobby into the meeting.
79
+ *
80
+ * @returns {void}
81
+ */
82
+ }, {
83
+ key: "sendNextMetrics",
84
+ value: function sendNextMetrics() {
85
+ this.shouldSendMetricsOnNextStatsReport = true;
86
+ }
87
+
74
88
  /**
75
89
  * Add metrics items to the metrics queue.
76
90
  *
@@ -86,11 +100,11 @@ var RtcMetrics = exports.default = /*#__PURE__*/function () {
86
100
  data.payload = data.payload.map(this.anonymizeIp);
87
101
  }
88
102
  this.metricsQueue.push(data);
89
- if (!this.initialMetricsSent && data.name === 'stats-report') {
103
+ if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {
90
104
  // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately
91
105
  // in case the user is unhappy and closes the browser early
92
106
  this.sendMetricsInQueue();
93
- this.initialMetricsSent = true;
107
+ this.shouldSendMetricsOnNextStatsReport = false;
94
108
  }
95
109
  try {
96
110
  // If a connection fails, send the rest of the metrics in queue and get a new connection id.
@@ -145,7 +159,7 @@ var RtcMetrics = exports.default = /*#__PURE__*/function () {
145
159
  key: "resetConnection",
146
160
  value: function resetConnection() {
147
161
  this.connectionId = _uuid.default.v4();
148
- this.initialMetricsSent = false;
162
+ this.shouldSendMetricsOnNextStatsReport = true;
149
163
  }
150
164
 
151
165
  /**
@@ -1 +1 @@
1
- {"version":3,"names":["_internalPluginMetrics","require","_uuid","_interopRequireDefault","_constants","parseJsonPayload","payload","JSON","parse","_","RtcMetrics","exports","default","webex","meetingId","correlationId","_classCallCheck2","_defineProperty2","intervalId","window","setInterval","sendMetricsInQueue","bind","resetConnection","_createClass2","key","value","metricsQueue","length","sendMetrics","addMetrics","data","name","map","anonymizeIp","push","initialMetricsSent","parsedPayload","e","console","error","closeMetrics","clearInterval","stats","type","ip","CallDiagnosticUtils","anonymizeIPAddress","undefined","address","relatedAddress","_stringify","connectionId","uuid","v4","request","method","service","resource","headers","appId","RTC_METRICS","APP_ID","body","metrics","version","userId","internal","device"],"sources":["index.ts"],"sourcesContent":["/* eslint-disable class-methods-use-this */\nimport {CallDiagnosticUtils} from '@webex/internal-plugin-metrics';\nimport uuid from 'uuid';\nimport RTC_METRICS from './constants';\n\nconst parseJsonPayload = (payload: any[]): any | null => {\n try {\n if (payload && payload[0]) {\n return JSON.parse(payload[0]);\n }\n\n return null;\n } catch (_) {\n return null;\n }\n};\n\n/**\n * Rtc Metrics\n */\nexport default class RtcMetrics {\n /**\n * Array of MetricData items to be sent to the metrics service.\n */\n metricsQueue = [];\n\n intervalId: number;\n\n webex: any;\n\n meetingId: string;\n\n correlationId: string;\n\n connectionId: string;\n\n initialMetricsSent: boolean;\n\n /**\n * Initialize the interval.\n *\n * @param {object} webex - The main `webex` object.\n * @param {string} meetingId - The meeting id.\n * @param {string} correlationId - The correlation id.\n */\n constructor(webex, meetingId, correlationId) {\n // `window` is used to prevent typescript from returning a NodeJS.Timer.\n this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);\n this.meetingId = meetingId;\n this.webex = webex;\n this.correlationId = correlationId;\n this.resetConnection();\n }\n\n /**\n * Check to see if the metrics queue has any items.\n *\n * @returns {void}\n */\n public sendMetricsInQueue() {\n if (this.metricsQueue.length) {\n this.sendMetrics();\n this.metricsQueue = [];\n }\n }\n\n /**\n * Add metrics items to the metrics queue.\n *\n * @param {object} data - An object with a payload array of metrics items.\n *\n * @returns {void}\n */\n addMetrics(data) {\n if (data.payload.length) {\n if (data.name === 'stats-report') {\n data.payload = data.payload.map(this.anonymizeIp);\n }\n\n this.metricsQueue.push(data);\n\n if (!this.initialMetricsSent && data.name === 'stats-report') {\n // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately\n // in case the user is unhappy and closes the browser early\n this.sendMetricsInQueue();\n this.initialMetricsSent = true;\n }\n\n try {\n // If a connection fails, send the rest of the metrics in queue and get a new connection id.\n const parsedPayload = parseJsonPayload(data.payload);\n if (\n data.name === 'onconnectionstatechange' &&\n parsedPayload &&\n parsedPayload.value === 'failed'\n ) {\n this.sendMetricsInQueue();\n this.resetConnection();\n }\n } catch (e) {\n console.error(e);\n }\n }\n }\n\n /**\n * Clear the metrics interval.\n *\n * @returns {void}\n */\n closeMetrics() {\n this.sendMetricsInQueue();\n clearInterval(this.intervalId);\n }\n\n /**\n * Anonymize IP addresses.\n *\n * @param {array} stats - An RTCStatsReport organized into an array of strings.\n * @returns {string}\n */\n anonymizeIp(stats: string): string {\n const data = JSON.parse(stats);\n // on local and remote candidates, anonymize the last 4 bits.\n if (data.type === 'local-candidate' || data.type === 'remote-candidate') {\n data.ip = CallDiagnosticUtils.anonymizeIPAddress(data.ip) || undefined;\n data.address = CallDiagnosticUtils.anonymizeIPAddress(data.address) || undefined;\n data.relatedAddress =\n CallDiagnosticUtils.anonymizeIPAddress(data.relatedAddress) || undefined;\n }\n\n return JSON.stringify(data);\n }\n\n /**\n * Set a new connection id.\n *\n * @returns {void}\n */\n private resetConnection() {\n this.connectionId = uuid.v4();\n this.initialMetricsSent = false;\n }\n\n /**\n * Send metrics to the metrics service.\n *\n * @returns {void}\n */\n private sendMetrics() {\n this.webex.request({\n method: 'POST',\n service: 'unifiedTelemetry',\n resource: 'metric/v2',\n headers: {\n type: 'webrtcMedia',\n appId: RTC_METRICS.APP_ID,\n },\n body: {\n metrics: [\n {\n type: 'webrtc',\n version: '1.1.0',\n userId: this.webex.internal.device.userId,\n meetingId: this.meetingId,\n correlationId: this.correlationId,\n connectionId: this.connectionId,\n data: this.metricsQueue,\n },\n ],\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["_internalPluginMetrics","require","_uuid","_interopRequireDefault","_constants","parseJsonPayload","payload","JSON","parse","_","RtcMetrics","exports","default","webex","meetingId","correlationId","_classCallCheck2","_defineProperty2","intervalId","window","setInterval","sendMetricsInQueue","bind","resetConnection","_createClass2","key","value","metricsQueue","length","sendMetrics","sendNextMetrics","shouldSendMetricsOnNextStatsReport","addMetrics","data","name","map","anonymizeIp","push","parsedPayload","e","console","error","closeMetrics","clearInterval","stats","type","ip","CallDiagnosticUtils","anonymizeIPAddress","undefined","address","relatedAddress","_stringify","connectionId","uuid","v4","request","method","service","resource","headers","appId","RTC_METRICS","APP_ID","body","metrics","version","userId","internal","device"],"sources":["index.ts"],"sourcesContent":["/* eslint-disable class-methods-use-this */\nimport {CallDiagnosticUtils} from '@webex/internal-plugin-metrics';\nimport uuid from 'uuid';\nimport RTC_METRICS from './constants';\n\nconst parseJsonPayload = (payload: any[]): any | null => {\n try {\n if (payload && payload[0]) {\n return JSON.parse(payload[0]);\n }\n\n return null;\n } catch (_) {\n return null;\n }\n};\n\n/**\n * Rtc Metrics\n */\nexport default class RtcMetrics {\n /**\n * Array of MetricData items to be sent to the metrics service.\n */\n metricsQueue = [];\n\n intervalId: number;\n\n webex: any;\n\n meetingId: string;\n\n correlationId: string;\n\n connectionId: string;\n\n shouldSendMetricsOnNextStatsReport: boolean;\n\n /**\n * Initialize the interval.\n *\n * @param {object} webex - The main `webex` object.\n * @param {string} meetingId - The meeting id.\n * @param {string} correlationId - The correlation id.\n */\n constructor(webex, meetingId, correlationId) {\n // `window` is used to prevent typescript from returning a NodeJS.Timer.\n this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);\n this.meetingId = meetingId;\n this.webex = webex;\n this.correlationId = correlationId;\n this.resetConnection();\n }\n\n /**\n * Check to see if the metrics queue has any items.\n *\n * @returns {void}\n */\n public sendMetricsInQueue() {\n if (this.metricsQueue.length) {\n this.sendMetrics();\n this.metricsQueue = [];\n }\n }\n\n /**\n * Forces sending metrics when we get the next stats-report\n *\n * This is useful for cases when something important happens that affects the media connection,\n * for example when we move from lobby into the meeting.\n *\n * @returns {void}\n */\n public sendNextMetrics() {\n this.shouldSendMetricsOnNextStatsReport = true;\n }\n\n /**\n * Add metrics items to the metrics queue.\n *\n * @param {object} data - An object with a payload array of metrics items.\n *\n * @returns {void}\n */\n addMetrics(data) {\n if (data.payload.length) {\n if (data.name === 'stats-report') {\n data.payload = data.payload.map(this.anonymizeIp);\n }\n\n this.metricsQueue.push(data);\n\n if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {\n // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately\n // in case the user is unhappy and closes the browser early\n this.sendMetricsInQueue();\n this.shouldSendMetricsOnNextStatsReport = false;\n }\n\n try {\n // If a connection fails, send the rest of the metrics in queue and get a new connection id.\n const parsedPayload = parseJsonPayload(data.payload);\n if (\n data.name === 'onconnectionstatechange' &&\n parsedPayload &&\n parsedPayload.value === 'failed'\n ) {\n this.sendMetricsInQueue();\n this.resetConnection();\n }\n } catch (e) {\n console.error(e);\n }\n }\n }\n\n /**\n * Clear the metrics interval.\n *\n * @returns {void}\n */\n closeMetrics() {\n this.sendMetricsInQueue();\n clearInterval(this.intervalId);\n }\n\n /**\n * Anonymize IP addresses.\n *\n * @param {array} stats - An RTCStatsReport organized into an array of strings.\n * @returns {string}\n */\n anonymizeIp(stats: string): string {\n const data = JSON.parse(stats);\n // on local and remote candidates, anonymize the last 4 bits.\n if (data.type === 'local-candidate' || data.type === 'remote-candidate') {\n data.ip = CallDiagnosticUtils.anonymizeIPAddress(data.ip) || undefined;\n data.address = CallDiagnosticUtils.anonymizeIPAddress(data.address) || undefined;\n data.relatedAddress =\n CallDiagnosticUtils.anonymizeIPAddress(data.relatedAddress) || undefined;\n }\n\n return JSON.stringify(data);\n }\n\n /**\n * Set a new connection id.\n *\n * @returns {void}\n */\n private resetConnection() {\n this.connectionId = uuid.v4();\n this.shouldSendMetricsOnNextStatsReport = true;\n }\n\n /**\n * Send metrics to the metrics service.\n *\n * @returns {void}\n */\n private sendMetrics() {\n this.webex.request({\n method: 'POST',\n service: 'unifiedTelemetry',\n resource: 'metric/v2',\n headers: {\n type: 'webrtcMedia',\n appId: RTC_METRICS.APP_ID,\n },\n body: {\n metrics: [\n {\n type: 'webrtc',\n version: '1.1.0',\n userId: this.webex.internal.device.userId,\n meetingId: this.meetingId,\n correlationId: this.correlationId,\n connectionId: this.connectionId,\n data: this.metricsQueue,\n },\n ],\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -457,6 +457,7 @@ export default class Meeting extends StatelessWebexPlugin {
457
457
  private connectionStateHandler?;
458
458
  private iceCandidateErrors;
459
459
  private iceCandidatesCount;
460
+ private rtcMetrics?;
460
461
  /**
461
462
  * @param {Object} attrs
462
463
  * @param {Object} options
@@ -11,7 +11,7 @@ export default class RtcMetrics {
11
11
  meetingId: string;
12
12
  correlationId: string;
13
13
  connectionId: string;
14
- initialMetricsSent: boolean;
14
+ shouldSendMetricsOnNextStatsReport: boolean;
15
15
  /**
16
16
  * Initialize the interval.
17
17
  *
@@ -26,6 +26,15 @@ export default class RtcMetrics {
26
26
  * @returns {void}
27
27
  */
28
28
  sendMetricsInQueue(): void;
29
+ /**
30
+ * Forces sending metrics when we get the next stats-report
31
+ *
32
+ * This is useful for cases when something important happens that affects the media connection,
33
+ * for example when we move from lobby into the meeting.
34
+ *
35
+ * @returns {void}
36
+ */
37
+ sendNextMetrics(): void;
29
38
  /**
30
39
  * Add metrics items to the metrics queue.
31
40
  *
@@ -62,7 +62,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
62
62
  updateCanManageWebcast: function updateCanManageWebcast(canManageWebcast) {
63
63
  this.set('canManageWebcast', canManageWebcast);
64
64
  },
65
- version: "3.4.0-next.3"
65
+ version: "3.4.0-next.5"
66
66
  });
67
67
  var _default = exports.default = Webinar;
68
68
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,13 +43,13 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-meetings": "3.4.0-next.3",
47
- "@webex/plugin-rooms": "3.3.1-next.14",
48
- "@webex/test-helper-chai": "3.3.1-next.13",
49
- "@webex/test-helper-mocha": "3.3.1-next.13",
50
- "@webex/test-helper-mock-webex": "3.3.1-next.13",
51
- "@webex/test-helper-retry": "3.3.1-next.13",
52
- "@webex/test-helper-test-users": "3.3.1-next.13",
46
+ "@webex/plugin-meetings": "3.4.0-next.5",
47
+ "@webex/plugin-rooms": "3.4.0-next.1",
48
+ "@webex/test-helper-chai": "3.4.0-next.1",
49
+ "@webex/test-helper-mocha": "3.4.0-next.1",
50
+ "@webex/test-helper-mock-webex": "3.4.0-next.1",
51
+ "@webex/test-helper-retry": "3.4.0-next.1",
52
+ "@webex/test-helper-test-users": "3.4.0-next.1",
53
53
  "chai": "^4.3.4",
54
54
  "chai-as-promised": "^7.1.1",
55
55
  "eslint": "^8.24.0",
@@ -61,21 +61,21 @@
61
61
  "typescript": "^4.7.4"
62
62
  },
63
63
  "dependencies": {
64
- "@webex/common": "3.3.1-next.13",
64
+ "@webex/common": "3.4.0-next.1",
65
65
  "@webex/internal-media-core": "2.10.2",
66
- "@webex/internal-plugin-conversation": "3.3.1-next.14",
67
- "@webex/internal-plugin-device": "3.3.1-next.13",
68
- "@webex/internal-plugin-llm": "3.3.1-next.15",
69
- "@webex/internal-plugin-mercury": "3.3.1-next.14",
70
- "@webex/internal-plugin-metrics": "3.3.1-next.13",
71
- "@webex/internal-plugin-support": "3.3.1-next.14",
72
- "@webex/internal-plugin-user": "3.3.1-next.13",
73
- "@webex/internal-plugin-voicea": "3.4.0-next.3",
74
- "@webex/media-helpers": "3.4.0-next.1",
75
- "@webex/plugin-people": "3.3.1-next.14",
76
- "@webex/plugin-rooms": "3.3.1-next.14",
66
+ "@webex/internal-plugin-conversation": "3.4.0-next.1",
67
+ "@webex/internal-plugin-device": "3.4.0-next.1",
68
+ "@webex/internal-plugin-llm": "3.4.0-next.1",
69
+ "@webex/internal-plugin-mercury": "3.4.0-next.1",
70
+ "@webex/internal-plugin-metrics": "3.4.0-next.1",
71
+ "@webex/internal-plugin-support": "3.4.0-next.1",
72
+ "@webex/internal-plugin-user": "3.4.0-next.1",
73
+ "@webex/internal-plugin-voicea": "3.4.0-next.5",
74
+ "@webex/media-helpers": "3.4.0-next.2",
75
+ "@webex/plugin-people": "3.4.0-next.1",
76
+ "@webex/plugin-rooms": "3.4.0-next.1",
77
77
  "@webex/web-capabilities": "^1.4.0",
78
- "@webex/webex-core": "3.3.1-next.13",
78
+ "@webex/webex-core": "3.4.0-next.1",
79
79
  "ampersand-collection": "^2.0.2",
80
80
  "bowser": "^2.11.0",
81
81
  "btoa": "^1.2.1",
@@ -91,5 +91,5 @@
91
91
  "//": [
92
92
  "TODO: upgrade jwt-decode when moving to node 18"
93
93
  ],
94
- "version": "3.4.0-next.3"
94
+ "version": "3.4.0-next.5"
95
95
  }
@@ -104,9 +104,7 @@ Media.getDirection = (forceSendRecv: boolean, receive: boolean, send: boolean) =
104
104
  *
105
105
  * @param {boolean} isMultistream
106
106
  * @param {string} debugId string useful for debugging (will appear in media connection logs)
107
- * @param {object} webex main `webex` object.
108
107
  * @param {string} meetingId id for the meeting using this connection
109
- * @param {string} correlationId id used in requests to correlate to this session
110
108
  * @param {Object} options
111
109
  * @param {Object} [options.mediaProperties] contains mediaDirection and local tracks:
112
110
  * audioTrack, videoTrack, shareVideoTrack, and shareAudioTrack
@@ -120,10 +118,9 @@ Media.getDirection = (forceSendRecv: boolean, receive: boolean, send: boolean) =
120
118
  Media.createMediaConnection = (
121
119
  isMultistream: boolean,
122
120
  debugId: string,
123
- webex: object,
124
121
  meetingId: string,
125
- correlationId: string,
126
122
  options: {
123
+ rtcMetrics?: RtcMetrics;
127
124
  mediaProperties: {
128
125
  mediaDirection?: {
129
126
  receiveAudio: boolean;
@@ -150,6 +147,7 @@ Media.createMediaConnection = (
150
147
  }
151
148
  ) => {
152
149
  const {
150
+ rtcMetrics,
153
151
  mediaProperties,
154
152
  remoteQualityLevel,
155
153
  enableRtx,
@@ -192,15 +190,13 @@ Media.createMediaConnection = (
192
190
  config.bundlePolicy = bundlePolicy;
193
191
  }
194
192
 
195
- const rtcMetrics = new RtcMetrics(webex, meetingId, correlationId);
196
-
197
193
  return new MultistreamRoapMediaConnection(
198
194
  config,
199
195
  meetingId,
200
196
  /* the rtc metrics objects callbacks */
201
- (data) => rtcMetrics.addMetrics(data),
202
- () => rtcMetrics.closeMetrics(),
203
- () => rtcMetrics.sendMetricsInQueue()
197
+ (data) => rtcMetrics?.addMetrics(data),
198
+ () => rtcMetrics?.closeMetrics(),
199
+ () => rtcMetrics?.sendMetricsInQueue()
204
200
  );
205
201
  }
206
202
 
@@ -155,6 +155,7 @@ import ControlsOptionsManager from '../controls-options-manager';
155
155
  import PermissionError from '../common/errors/permission';
156
156
  import {LocusMediaRequest} from './locusMediaRequest';
157
157
  import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
158
+ import RtcMetrics from '../rtcMetrics';
158
159
 
159
160
  // default callback so we don't call an undefined function, but in practice it should never be used
160
161
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -696,6 +697,7 @@ export default class Meeting extends StatelessWebexPlugin {
696
697
  private connectionStateHandler?: ConnectionStateHandler;
697
698
  private iceCandidateErrors: Map<string, number>;
698
699
  private iceCandidatesCount: number;
700
+ private rtcMetrics?: RtcMetrics;
699
701
 
700
702
  /**
701
703
  * @param {Object} attrs
@@ -3156,6 +3158,7 @@ export default class Meeting extends StatelessWebexPlugin {
3156
3158
  options: {meetingId: this.id},
3157
3159
  });
3158
3160
  }
3161
+ this.rtcMetrics?.sendNextMetrics();
3159
3162
  this.updateLLMConnection();
3160
3163
  });
3161
3164
 
@@ -6307,14 +6310,17 @@ export default class Meeting extends StatelessWebexPlugin {
6307
6310
  * @returns {RoapMediaConnection | MultistreamRoapMediaConnection}
6308
6311
  */
6309
6312
  private async createMediaConnection(turnServerInfo, bundlePolicy?: BundlePolicy) {
6313
+ this.rtcMetrics = this.isMultistream
6314
+ ? // @ts-ignore
6315
+ new RtcMetrics(this.webex, this.id, this.correlationId)
6316
+ : undefined;
6317
+
6310
6318
  const mc = Media.createMediaConnection(
6311
6319
  this.isMultistream,
6312
6320
  this.getMediaConnectionDebugId(),
6313
- // @ts-ignore
6314
- this.webex,
6315
6321
  this.id,
6316
- this.correlationId,
6317
6322
  {
6323
+ rtcMetrics: this.rtcMetrics,
6318
6324
  mediaProperties: this.mediaProperties,
6319
6325
  remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
6320
6326
  // @ts-ignore - config coming from registerPlugin
@@ -34,7 +34,7 @@ export default class RtcMetrics {
34
34
 
35
35
  connectionId: string;
36
36
 
37
- initialMetricsSent: boolean;
37
+ shouldSendMetricsOnNextStatsReport: boolean;
38
38
 
39
39
  /**
40
40
  * Initialize the interval.
@@ -64,6 +64,18 @@ export default class RtcMetrics {
64
64
  }
65
65
  }
66
66
 
67
+ /**
68
+ * Forces sending metrics when we get the next stats-report
69
+ *
70
+ * This is useful for cases when something important happens that affects the media connection,
71
+ * for example when we move from lobby into the meeting.
72
+ *
73
+ * @returns {void}
74
+ */
75
+ public sendNextMetrics() {
76
+ this.shouldSendMetricsOnNextStatsReport = true;
77
+ }
78
+
67
79
  /**
68
80
  * Add metrics items to the metrics queue.
69
81
  *
@@ -79,11 +91,11 @@ export default class RtcMetrics {
79
91
 
80
92
  this.metricsQueue.push(data);
81
93
 
82
- if (!this.initialMetricsSent && data.name === 'stats-report') {
94
+ if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {
83
95
  // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately
84
96
  // in case the user is unhappy and closes the browser early
85
97
  this.sendMetricsInQueue();
86
- this.initialMetricsSent = true;
98
+ this.shouldSendMetricsOnNextStatsReport = false;
87
99
  }
88
100
 
89
101
  try {
@@ -139,7 +151,7 @@ export default class RtcMetrics {
139
151
  */
140
152
  private resetConnection() {
141
153
  this.connectionId = uuid.v4();
142
- this.initialMetricsSent = false;
154
+ this.shouldSendMetricsOnNextStatsReport = true;
143
155
  }
144
156
 
145
157
  /**
@@ -3,14 +3,12 @@ import Media from '@webex/plugin-meetings/src/media/index';
3
3
  import {assert} from '@webex/test-helper-chai';
4
4
  import sinon from 'sinon';
5
5
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
6
- import MockWebex from '@webex/test-helper-mock-webex';
7
6
 
8
7
  describe('createMediaConnection', () => {
9
8
  let clock;
10
9
  beforeEach(() => {
11
10
  clock = sinon.useFakeTimers();
12
11
  });
13
- const webex = MockWebex();
14
12
 
15
13
  const fakeRoapMediaConnection = {
16
14
  id: 'roap media connection',
@@ -61,7 +59,7 @@ describe('createMediaConnection', () => {
61
59
  const ENABLE_EXTMAP = false;
62
60
  const ENABLE_RTX = true;
63
61
 
64
- Media.createMediaConnection(false, 'some debug id', webex, 'meetingId', 'correlationId', {
62
+ Media.createMediaConnection(false, 'some debug id', 'meetingId', {
65
63
  mediaProperties: {
66
64
  mediaDirection: {
67
65
  sendAudio: false,
@@ -139,7 +137,13 @@ describe('createMediaConnection', () => {
139
137
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
140
138
  .returns(fakeRoapMediaConnection);
141
139
 
142
- Media.createMediaConnection(true, 'some debug id', webex, 'meeting id', 'correlationId', {
140
+ const rtcMetrics = {
141
+ addMetrics: sinon.stub(),
142
+ closeMetrics: sinon.stub(),
143
+ sendMetricsInQueue: sinon.stub(),
144
+ };
145
+
146
+ Media.createMediaConnection(true, 'some debug id', 'meeting id', {
143
147
  mediaProperties: {
144
148
  mediaDirection: {
145
149
  sendAudio: true,
@@ -150,6 +154,7 @@ describe('createMediaConnection', () => {
150
154
  receiveShare: true,
151
155
  },
152
156
  },
157
+ rtcMetrics,
153
158
  turnServerInfo: {
154
159
  url: 'turns:turn-server-url:443?transport=tcp',
155
160
  username: 'turn username',
@@ -177,6 +182,27 @@ describe('createMediaConnection', () => {
177
182
  },
178
183
  'meeting id'
179
184
  );
185
+
186
+ // check if rtcMetrics callbacks are configured correctly
187
+ const addMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[2];
188
+ const closeMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[3];
189
+ const sendMetricsInQueueCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[4];
190
+
191
+ assert.isFunction(addMetricsCallback);
192
+ assert.isFunction(closeMetricsCallback);
193
+ assert.isFunction(sendMetricsInQueueCallback);
194
+
195
+ const fakeMetricsData = {id: 'metrics data'};
196
+
197
+ addMetricsCallback(fakeMetricsData);
198
+ assert.calledOnceWithExactly(rtcMetrics.addMetrics, fakeMetricsData);
199
+
200
+ closeMetricsCallback();
201
+ assert.calledOnce(rtcMetrics.closeMetrics);
202
+
203
+ sendMetricsInQueueCallback();
204
+ assert.calledOnce(rtcMetrics.sendMetricsInQueue);
205
+
180
206
  });
181
207
 
182
208
  [
@@ -191,7 +217,7 @@ describe('createMediaConnection', () => {
191
217
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
192
218
  .returns(fakeRoapMediaConnection);
193
219
 
194
- Media.createMediaConnection(true, 'debug string', webex, 'meeting id', 'correlationId', {
220
+ Media.createMediaConnection(true, 'debug string', 'meeting id', {
195
221
  mediaProperties: {
196
222
  mediaDirection: {
197
223
  sendAudio: true,
@@ -220,7 +246,7 @@ describe('createMediaConnection', () => {
220
246
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
221
247
  .returns(fakeRoapMediaConnection);
222
248
 
223
- Media.createMediaConnection(true, 'debug string', webex, 'meeting id', 'correlationId', {
249
+ Media.createMediaConnection(true, 'debug string', 'meeting id', {
224
250
  mediaProperties: {
225
251
  mediaDirection: {
226
252
  sendAudio: true,
@@ -260,7 +286,7 @@ describe('createMediaConnection', () => {
260
286
  const ENABLE_EXTMAP = false;
261
287
  const ENABLE_RTX = true;
262
288
 
263
- Media.createMediaConnection(false, 'some debug id', webex, 'meeting id', 'correlationId', {
289
+ Media.createMediaConnection(false, 'some debug id', 'meeting id', {
264
290
  mediaProperties: {
265
291
  mediaDirection: {
266
292
  sendAudio: true,
@@ -5,6 +5,8 @@ import 'jsdom-global/register';
5
5
  import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
6
6
  import sinon from 'sinon';
7
7
  import * as InternalMediaCoreModule from '@webex/internal-media-core';
8
+ import * as RtcMetricsModule from '@webex/plugin-meetings/src/rtcMetrics';
9
+ import * as RemoteMediaManagerModule from '@webex/plugin-meetings/src/multistream/remoteMediaManager';
8
10
  import StateMachine from 'javascript-state-machine';
9
11
  import uuid from 'uuid';
10
12
  import {assert, expect} from '@webex/test-helper-chai';
@@ -2405,9 +2407,7 @@ describe('plugin-meetings', () => {
2405
2407
  Media.createMediaConnection,
2406
2408
  false,
2407
2409
  meeting.getMediaConnectionDebugId(),
2408
- webex,
2409
2410
  meeting.id,
2410
- meeting.correlationId,
2411
2411
  sinon.match({turnServerInfo: undefined})
2412
2412
  );
2413
2413
  assert.calledOnce(meeting.setMercuryListener);
@@ -2449,6 +2449,44 @@ describe('plugin-meetings', () => {
2449
2449
  checkWorking({allowMediaInLobby: true});
2450
2450
  });
2451
2451
 
2452
+ it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
2453
+ const fakeRtcMetrics = {id: 'fake rtc metrics object'};
2454
+ const rtcMetricsCtor = sinon.stub(RtcMetricsModule, 'default').returns(fakeRtcMetrics);
2455
+
2456
+ // setup the minimum mocks required for multistream connection
2457
+ fakeMediaConnection.createSendSlot = sinon.stub().returns({
2458
+ publishStream: sinon.stub(),
2459
+ unpublishStream: sinon.stub(),
2460
+ setNamedMediaGroups: sinon.stub(),
2461
+ });
2462
+ sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
2463
+ start: sinon.stub().resolves(),
2464
+ on: sinon.stub(),
2465
+ logAllReceiveSlots: sinon.stub(),
2466
+ });
2467
+
2468
+ meeting.meetingState = 'ACTIVE';
2469
+ meeting.isMultistream = true;
2470
+
2471
+ await meeting.addMedia({
2472
+ mediaSettings: {},
2473
+ });
2474
+
2475
+ assert.calledOnceWithExactly(rtcMetricsCtor, webex, meeting.id, meeting.correlationId);
2476
+
2477
+ // check that rtcMetrics was passed to Media.createMediaConnection
2478
+ assert.calledOnce(Media.createMediaConnection);
2479
+ assert.calledWith(
2480
+ Media.createMediaConnection,
2481
+ true,
2482
+ meeting.getMediaConnectionDebugId(),
2483
+ meeting.id,
2484
+ sinon.match({
2485
+ rtcMetrics: fakeRtcMetrics,
2486
+ })
2487
+ );
2488
+ });
2489
+
2452
2490
  it('should pass the turn server info to the peer connection', async () => {
2453
2491
  const FAKE_TURN_URL = 'turns:webex.com:3478';
2454
2492
  const FAKE_TURN_USER = 'some-turn-username';
@@ -2478,9 +2516,7 @@ describe('plugin-meetings', () => {
2478
2516
  Media.createMediaConnection,
2479
2517
  false,
2480
2518
  meeting.getMediaConnectionDebugId(),
2481
- webex,
2482
2519
  meeting.id,
2483
- meeting.correlationId,
2484
2520
  sinon.match({
2485
2521
  turnServerInfo: {
2486
2522
  url: FAKE_TURN_URL,
@@ -3401,9 +3437,7 @@ describe('plugin-meetings', () => {
3401
3437
  Media.createMediaConnection,
3402
3438
  false,
3403
3439
  meeting.getMediaConnectionDebugId(),
3404
- webex,
3405
3440
  meeting.id,
3406
- meeting.correlationId,
3407
3441
  sinon.match({
3408
3442
  turnServerInfo: {
3409
3443
  url: FAKE_TURN_URL,
@@ -8454,6 +8488,9 @@ describe('plugin-meetings', () => {
8454
8488
  it('listens to the self admitted guest event', (done) => {
8455
8489
  meeting.stopKeepAlive = sinon.stub();
8456
8490
  meeting.updateLLMConnection = sinon.stub();
8491
+ meeting.rtcMetrics = {
8492
+ sendNextMetrics: sinon.stub(),
8493
+ };
8457
8494
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
8458
8495
  assert.calledOnceWithExactly(meeting.stopKeepAlive);
8459
8496
  assert.calledThrice(TriggerProxy.trigger);
@@ -8465,6 +8502,8 @@ describe('plugin-meetings', () => {
8465
8502
  {payload: test1}
8466
8503
  );
8467
8504
  assert.calledOnce(meeting.updateLLMConnection);
8505
+ assert.calledOnceWithExactly(meeting.rtcMetrics.sendNextMetrics);
8506
+
8468
8507
  done();
8469
8508
  });
8470
8509