@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.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/index.js +6 -9
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/index.js +22 -16
- package/dist/meeting/index.js.map +1 -1
- package/dist/rtcMetrics/index.js +18 -4
- package/dist/rtcMetrics/index.js.map +1 -1
- package/dist/types/meeting/index.d.ts +1 -0
- package/dist/types/rtcMetrics/index.d.ts +10 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -21
- package/src/media/index.ts +5 -9
- package/src/meeting/index.ts +9 -3
- package/src/rtcMetrics/index.ts +16 -4
- package/test/unit/spec/media/index.ts +33 -7
- package/test/unit/spec/meeting/index.js +45 -6
package/dist/rtcMetrics/index.js
CHANGED
|
@@ -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, "
|
|
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 (
|
|
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.
|
|
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.
|
|
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","
|
|
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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"}
|
|
@@ -11,7 +11,7 @@ export default class RtcMetrics {
|
|
|
11
11
|
meetingId: string;
|
|
12
12
|
correlationId: string;
|
|
13
13
|
connectionId: string;
|
|
14
|
-
|
|
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
|
*
|
package/dist/webinar/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
47
|
-
"@webex/plugin-rooms": "3.
|
|
48
|
-
"@webex/test-helper-chai": "3.
|
|
49
|
-
"@webex/test-helper-mocha": "3.
|
|
50
|
-
"@webex/test-helper-mock-webex": "3.
|
|
51
|
-
"@webex/test-helper-retry": "3.
|
|
52
|
-
"@webex/test-helper-test-users": "3.
|
|
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.
|
|
64
|
+
"@webex/common": "3.4.0-next.1",
|
|
65
65
|
"@webex/internal-media-core": "2.10.2",
|
|
66
|
-
"@webex/internal-plugin-conversation": "3.
|
|
67
|
-
"@webex/internal-plugin-device": "3.
|
|
68
|
-
"@webex/internal-plugin-llm": "3.
|
|
69
|
-
"@webex/internal-plugin-mercury": "3.
|
|
70
|
-
"@webex/internal-plugin-metrics": "3.
|
|
71
|
-
"@webex/internal-plugin-support": "3.
|
|
72
|
-
"@webex/internal-plugin-user": "3.
|
|
73
|
-
"@webex/internal-plugin-voicea": "3.4.0-next.
|
|
74
|
-
"@webex/media-helpers": "3.4.0-next.
|
|
75
|
-
"@webex/plugin-people": "3.
|
|
76
|
-
"@webex/plugin-rooms": "3.
|
|
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.
|
|
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.
|
|
94
|
+
"version": "3.4.0-next.5"
|
|
95
95
|
}
|
package/src/media/index.ts
CHANGED
|
@@ -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
|
|
202
|
-
() => rtcMetrics
|
|
203
|
-
() => rtcMetrics
|
|
197
|
+
(data) => rtcMetrics?.addMetrics(data),
|
|
198
|
+
() => rtcMetrics?.closeMetrics(),
|
|
199
|
+
() => rtcMetrics?.sendMetricsInQueue()
|
|
204
200
|
);
|
|
205
201
|
}
|
|
206
202
|
|
package/src/meeting/index.ts
CHANGED
|
@@ -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
|
package/src/rtcMetrics/index.ts
CHANGED
|
@@ -34,7 +34,7 @@ export default class RtcMetrics {
|
|
|
34
34
|
|
|
35
35
|
connectionId: string;
|
|
36
36
|
|
|
37
|
-
|
|
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 (
|
|
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.
|
|
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.
|
|
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',
|
|
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
|
-
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
|