@webex/plugin-meetings 1.148.0 → 1.150.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.
- package/dist/constants.js +12 -4
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js +88 -11
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/meeting/index.js +247 -38
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +17 -41
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting-info/index.js +1 -1
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/util.js +14 -0
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meetings/index.js +12 -13
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +0 -16
- package/dist/meetings/util.js.map +1 -1
- package/dist/members/index.js +72 -11
- package/dist/members/index.js.map +1 -1
- package/dist/metrics/config.js +6 -0
- package/dist/metrics/config.js.map +1 -1
- package/dist/metrics/index.js +6 -0
- package/dist/metrics/index.js.map +1 -1
- package/package.json +5 -5
- package/src/constants.js +9 -2
- package/src/locus-info/mediaSharesUtils.js +84 -12
- package/src/meeting/index.js +225 -19
- package/src/meeting/request.js +50 -29
- package/src/meeting-info/index.js +4 -1
- package/src/meeting-info/util.js +13 -0
- package/src/meetings/index.js +10 -11
- package/src/meetings/util.js +0 -14
- package/src/members/index.js +80 -11
- package/src/metrics/config.js +6 -0
- package/src/metrics/index.js +5 -1
- package/test/integration/spec/journey.js +141 -5
- package/test/integration/spec/space-meeting.js +10 -1
- package/test/unit/spec/meeting/index.js +547 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {CONTENT} from '../constants';
|
|
1
|
+
import {CONTENT, WHITEBOARD} from '../constants';
|
|
2
2
|
|
|
3
3
|
const MediaSharesUtils = {};
|
|
4
4
|
|
|
@@ -10,8 +10,15 @@ const MediaSharesUtils = {};
|
|
|
10
10
|
MediaSharesUtils.parse = (mediaShares) => {
|
|
11
11
|
if (mediaShares) {
|
|
12
12
|
return {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
content: {
|
|
14
|
+
beneficiaryId: MediaSharesUtils.getContentBeneficiaryId(mediaShares),
|
|
15
|
+
disposition: MediaSharesUtils.getContentDisposition(mediaShares)
|
|
16
|
+
},
|
|
17
|
+
whiteboard: {
|
|
18
|
+
beneficiaryId: MediaSharesUtils.getWhiteboardBeneficiaryId(mediaShares),
|
|
19
|
+
disposition: MediaSharesUtils.getWhiteboardDisposition(mediaShares),
|
|
20
|
+
resourceUrl: MediaSharesUtils.getWhiteboardResourceUrl(mediaShares)
|
|
21
|
+
}
|
|
15
22
|
};
|
|
16
23
|
}
|
|
17
24
|
|
|
@@ -36,16 +43,27 @@ MediaSharesUtils.getMediaShares = (oldShare, newShare) => {
|
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
/**
|
|
39
|
-
* get the floor disposition (released, granted)
|
|
46
|
+
* get the content floor disposition (released, granted)
|
|
40
47
|
* @param {Object} mediaShares
|
|
41
48
|
* @returns {Boolean} disposition
|
|
42
49
|
*/
|
|
43
|
-
MediaSharesUtils.
|
|
50
|
+
MediaSharesUtils.getContentDisposition = (mediaShares) => {
|
|
44
51
|
const contentFloor = MediaSharesUtils.extractContentFloor(mediaShares);
|
|
45
52
|
|
|
46
53
|
return contentFloor ? contentFloor.disposition : null;
|
|
47
54
|
};
|
|
48
55
|
|
|
56
|
+
/**
|
|
57
|
+
* get the whiteboard floor disposition (released, granted)
|
|
58
|
+
* @param {Object} mediaShares
|
|
59
|
+
* @returns {Boolean} disposition
|
|
60
|
+
*/
|
|
61
|
+
MediaSharesUtils.getWhiteboardDisposition = (mediaShares) => {
|
|
62
|
+
const whiteboardFloor = MediaSharesUtils.extractWhiteboardFloor(mediaShares);
|
|
63
|
+
|
|
64
|
+
return whiteboardFloor ? whiteboardFloor.disposition : null;
|
|
65
|
+
};
|
|
66
|
+
|
|
49
67
|
/**
|
|
50
68
|
* extract the content property from media shares
|
|
51
69
|
* @param {Object} mediaShares
|
|
@@ -60,16 +78,29 @@ MediaSharesUtils.extractContent = (mediaShares) => {
|
|
|
60
78
|
};
|
|
61
79
|
|
|
62
80
|
/**
|
|
63
|
-
* extract the
|
|
64
|
-
* @param {Object}
|
|
81
|
+
* extract the whiteboard property from media shares
|
|
82
|
+
* @param {Object} mediaShares
|
|
83
|
+
* @returns {Object}
|
|
84
|
+
*/
|
|
85
|
+
MediaSharesUtils.extractWhiteboard = (mediaShares) => {
|
|
86
|
+
if (!mediaShares || !mediaShares.length) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return mediaShares.find((share) => share.name === WHITEBOARD) || null;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* extract the media stream floor property from content object
|
|
95
|
+
* @param {Object} mediaStream
|
|
65
96
|
* @returns {Object}
|
|
66
97
|
*/
|
|
67
|
-
MediaSharesUtils.extractFloor = (
|
|
68
|
-
if (!
|
|
98
|
+
MediaSharesUtils.extractFloor = (mediaStream) => {
|
|
99
|
+
if (!mediaStream) {
|
|
69
100
|
return null;
|
|
70
101
|
}
|
|
71
102
|
|
|
72
|
-
return
|
|
103
|
+
return mediaStream.floor;
|
|
73
104
|
};
|
|
74
105
|
|
|
75
106
|
/**
|
|
@@ -84,11 +115,22 @@ MediaSharesUtils.extractContentFloor = (mediaShares) => {
|
|
|
84
115
|
};
|
|
85
116
|
|
|
86
117
|
/**
|
|
87
|
-
*
|
|
118
|
+
* extract the whiteboard's floor from media shares
|
|
119
|
+
* @param {Object} mediaShares
|
|
120
|
+
* @returns {Object}
|
|
121
|
+
*/
|
|
122
|
+
MediaSharesUtils.extractWhiteboardFloor = (mediaShares) => {
|
|
123
|
+
const whiteboard = MediaSharesUtils.extractWhiteboard(mediaShares);
|
|
124
|
+
|
|
125
|
+
return MediaSharesUtils.extractFloor(whiteboard);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* get who is sharing from media shares (content)
|
|
88
130
|
* @param {Object} mediaShares
|
|
89
131
|
* @returns {Object}
|
|
90
132
|
*/
|
|
91
|
-
MediaSharesUtils.
|
|
133
|
+
MediaSharesUtils.getContentBeneficiaryId = (mediaShares) => {
|
|
92
134
|
const contentFloor = MediaSharesUtils.extractContentFloor(mediaShares);
|
|
93
135
|
|
|
94
136
|
if (!contentFloor || !contentFloor.beneficiary) {
|
|
@@ -98,4 +140,34 @@ MediaSharesUtils.getContentId = (mediaShares) => {
|
|
|
98
140
|
return contentFloor.beneficiary.id;
|
|
99
141
|
};
|
|
100
142
|
|
|
143
|
+
/**
|
|
144
|
+
* get who is sharing from media shares (whiteboard)
|
|
145
|
+
* @param {Object} mediaShares
|
|
146
|
+
* @returns {Object}
|
|
147
|
+
*/
|
|
148
|
+
MediaSharesUtils.getWhiteboardBeneficiaryId = (mediaShares) => {
|
|
149
|
+
const whiteboardFloor = MediaSharesUtils.extractWhiteboardFloor(mediaShares);
|
|
150
|
+
|
|
151
|
+
if (!whiteboardFloor || !whiteboardFloor.beneficiary) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return whiteboardFloor.beneficiary.id;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* get the which whiteboard is being shared via resource url
|
|
160
|
+
* @param {Object} mediaShares
|
|
161
|
+
* @returns {Object}
|
|
162
|
+
*/
|
|
163
|
+
MediaSharesUtils.getWhiteboardResourceUrl = (mediaShares) => {
|
|
164
|
+
const whiteboard = MediaSharesUtils.extractWhiteboard(mediaShares);
|
|
165
|
+
|
|
166
|
+
if (!whiteboard || !whiteboard.resourceUrl) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return whiteboard.resourceUrl;
|
|
171
|
+
};
|
|
172
|
+
|
|
101
173
|
export default MediaSharesUtils;
|
package/src/meeting/index.js
CHANGED
|
@@ -1047,6 +1047,24 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1047
1047
|
};
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
|
+
const joinRespTxStartAudio = this.getSendingMediaDelayDuration('audio');
|
|
1051
|
+
|
|
1052
|
+
if (joinRespTxStartAudio) {
|
|
1053
|
+
options.audioSetupDelay = {
|
|
1054
|
+
...options.audioSetupDelay,
|
|
1055
|
+
joinRespTxStart: joinRespTxStartAudio
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const joinRespTxStartVideo = this.getSendingMediaDelayDuration('video');
|
|
1060
|
+
|
|
1061
|
+
if (joinRespTxStartVideo) {
|
|
1062
|
+
options.videoSetupDelay = {
|
|
1063
|
+
...options.videoSetupDelay,
|
|
1064
|
+
joinRespTxStart: joinRespTxStartVideo
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1050
1068
|
if (options.type === MQA_STATS.CA_TYPE) {
|
|
1051
1069
|
payload = Metrics.initMediaPayload(options.event, identifiers, options);
|
|
1052
1070
|
}
|
|
@@ -1292,7 +1310,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1292
1310
|
|
|
1293
1311
|
/**
|
|
1294
1312
|
* Set up the locus info media shares listener
|
|
1295
|
-
* update content sharing id value for members, and updates the member
|
|
1313
|
+
* update content and whiteboard sharing id value for members, and updates the member
|
|
1296
1314
|
* notifies consumer with members:content:update {activeContentSharingId, endedContentSharingId}
|
|
1297
1315
|
* @returns {undefined}
|
|
1298
1316
|
* @private
|
|
@@ -1301,11 +1319,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1301
1319
|
setUpLocusMediaSharesListener() {
|
|
1302
1320
|
// Will get triggered on local and remote share
|
|
1303
1321
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, (payload) => {
|
|
1304
|
-
const {
|
|
1322
|
+
const {content: contentShare, whiteboard: whiteboardShare} = payload.current;
|
|
1323
|
+
const previousContentShare = payload.previous?.content;
|
|
1324
|
+
const previousWhiteboardShare = payload.previous?.whiteboard;
|
|
1305
1325
|
|
|
1306
1326
|
if (
|
|
1307
|
-
|
|
1308
|
-
|
|
1327
|
+
(contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
|
|
1328
|
+
contentShare.disposition === previousContentShare?.disposition) &&
|
|
1329
|
+
(whiteboardShare.beneficiaryId === previousWhiteboardShare?.beneficiaryId &&
|
|
1330
|
+
whiteboardShare.disposition === previousWhiteboardShare?.disposition &&
|
|
1331
|
+
whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl)
|
|
1309
1332
|
) {
|
|
1310
1333
|
// nothing changed, so ignore
|
|
1311
1334
|
// (this happens when we steal presentation from remote)
|
|
@@ -1316,15 +1339,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1316
1339
|
|
|
1317
1340
|
// REMOTE - check if remote started sharing
|
|
1318
1341
|
if (
|
|
1319
|
-
this.selfId !==
|
|
1320
|
-
disposition === FLOOR_ACTION.GRANTED
|
|
1342
|
+
this.selfId !== contentShare.beneficiaryId &&
|
|
1343
|
+
contentShare.disposition === FLOOR_ACTION.GRANTED
|
|
1321
1344
|
) {
|
|
1345
|
+
// CONTENT - sharing content remote
|
|
1322
1346
|
newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
|
|
1323
1347
|
}
|
|
1324
|
-
// LOCAL - check if we started sharing
|
|
1348
|
+
// LOCAL - check if we started sharing content
|
|
1325
1349
|
else if (
|
|
1326
|
-
this.selfId ===
|
|
1327
|
-
disposition === FLOOR_ACTION.GRANTED
|
|
1350
|
+
this.selfId === contentShare.beneficiaryId &&
|
|
1351
|
+
contentShare.disposition === FLOOR_ACTION.GRANTED
|
|
1328
1352
|
) {
|
|
1329
1353
|
if (this.mediaProperties.shareTrack?.readyState === 'ended') {
|
|
1330
1354
|
this.stopShare({
|
|
@@ -1335,13 +1359,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1335
1359
|
});
|
|
1336
1360
|
}
|
|
1337
1361
|
else {
|
|
1362
|
+
// CONTENT - sharing content local
|
|
1338
1363
|
newShareStatus = SHARE_STATUS.LOCAL_SHARE_ACTIVE;
|
|
1339
1364
|
}
|
|
1340
1365
|
}
|
|
1341
|
-
//
|
|
1366
|
+
// If we did not hit the cases above, no one is sharng content, so we check if we are sharing whiteboard
|
|
1367
|
+
// There is no concept of local/remote share for whiteboard
|
|
1368
|
+
// It does not matter who requested to share the whiteboard, everyone gets the same view
|
|
1369
|
+
else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
|
|
1370
|
+
// WHITEBOARD - sharing whiteboard
|
|
1371
|
+
newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
1372
|
+
}
|
|
1373
|
+
// or if content share is either released or null and whiteboard share is either released or null, no one is sharing
|
|
1342
1374
|
else if (
|
|
1343
|
-
|
|
1344
|
-
|
|
1375
|
+
(previousContentShare &&
|
|
1376
|
+
(contentShare.disposition === FLOOR_ACTION.RELEASED) || (contentShare.disposition === null)) &&
|
|
1377
|
+
(previousWhiteboardShare &&
|
|
1378
|
+
(whiteboardShare.disposition === FLOOR_ACTION.RELEASED) || (whiteboardShare.disposition === null))
|
|
1345
1379
|
) {
|
|
1346
1380
|
newShareStatus = SHARE_STATUS.NO_SHARE;
|
|
1347
1381
|
}
|
|
@@ -1379,6 +1413,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1379
1413
|
);
|
|
1380
1414
|
break;
|
|
1381
1415
|
|
|
1416
|
+
case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
|
|
1417
|
+
Trigger.trigger(
|
|
1418
|
+
this,
|
|
1419
|
+
{
|
|
1420
|
+
file: 'meeting/index',
|
|
1421
|
+
function: 'stopWhiteboardShare'
|
|
1422
|
+
},
|
|
1423
|
+
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
|
|
1424
|
+
);
|
|
1425
|
+
break;
|
|
1426
|
+
|
|
1382
1427
|
case SHARE_STATUS.NO_SHARE:
|
|
1383
1428
|
// nothing to do
|
|
1384
1429
|
break;
|
|
@@ -1399,13 +1444,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1399
1444
|
},
|
|
1400
1445
|
EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
1401
1446
|
{
|
|
1402
|
-
memberId:
|
|
1447
|
+
memberId: contentShare.beneficiaryId
|
|
1403
1448
|
}
|
|
1404
1449
|
);
|
|
1405
1450
|
};
|
|
1406
1451
|
|
|
1407
1452
|
// if a remote participant is stealing the presentation from us
|
|
1408
|
-
if (this.mediaProperties.mediaDirection?.sendShare) {
|
|
1453
|
+
if (!this.mediaProperties.mediaDirection?.sendShare || oldShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
|
|
1454
|
+
sendStartedSharingRemote();
|
|
1455
|
+
}
|
|
1456
|
+
else {
|
|
1409
1457
|
this.updateShare({
|
|
1410
1458
|
sendShare: false,
|
|
1411
1459
|
receiveShare: this.mediaProperties.mediaDirection.receiveShare
|
|
@@ -1414,9 +1462,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1414
1462
|
sendStartedSharingRemote();
|
|
1415
1463
|
});
|
|
1416
1464
|
}
|
|
1417
|
-
else {
|
|
1418
|
-
sendStartedSharingRemote();
|
|
1419
|
-
}
|
|
1420
1465
|
break;
|
|
1421
1466
|
}
|
|
1422
1467
|
|
|
@@ -1432,6 +1477,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1432
1477
|
Metrics.postEvent({event: eventType.LOCAL_SHARE_FLOOR_GRANTED, meeting: this});
|
|
1433
1478
|
break;
|
|
1434
1479
|
|
|
1480
|
+
case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
|
|
1481
|
+
Trigger.trigger(
|
|
1482
|
+
this,
|
|
1483
|
+
{
|
|
1484
|
+
file: 'meeting/index',
|
|
1485
|
+
function: 'startWhiteboardShare'
|
|
1486
|
+
},
|
|
1487
|
+
EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
|
1488
|
+
{
|
|
1489
|
+
resourceUrl: whiteboardShare.resourceUrl,
|
|
1490
|
+
memberId: whiteboardShare.beneficiaryId
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
|
|
1494
|
+
break;
|
|
1495
|
+
|
|
1435
1496
|
case SHARE_STATUS.NO_SHARE:
|
|
1436
1497
|
// nothing to do
|
|
1437
1498
|
break;
|
|
@@ -1453,11 +1514,29 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1453
1514
|
},
|
|
1454
1515
|
EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
1455
1516
|
{
|
|
1456
|
-
memberId:
|
|
1517
|
+
memberId: contentShare.beneficiaryId
|
|
1457
1518
|
}
|
|
1458
1519
|
);
|
|
1459
1520
|
this.members.locusMediaSharesUpdate(payload);
|
|
1460
1521
|
}
|
|
1522
|
+
else if (newShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
|
|
1523
|
+
// if we got here, then some remote participant has stolen
|
|
1524
|
+
// the presentation from another remote participant
|
|
1525
|
+
Trigger.trigger(
|
|
1526
|
+
this,
|
|
1527
|
+
{
|
|
1528
|
+
file: 'meeting/index',
|
|
1529
|
+
function: 'startWhiteboardShare'
|
|
1530
|
+
},
|
|
1531
|
+
EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
|
1532
|
+
{
|
|
1533
|
+
resourceUrl: whiteboardShare.resourceUrl,
|
|
1534
|
+
memberId: whiteboardShare.beneficiaryId
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
|
|
1538
|
+
this.members.locusMediaSharesUpdate(payload);
|
|
1539
|
+
}
|
|
1461
1540
|
});
|
|
1462
1541
|
}
|
|
1463
1542
|
|
|
@@ -4473,6 +4552,105 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4473
4552
|
});
|
|
4474
4553
|
}
|
|
4475
4554
|
|
|
4555
|
+
/**
|
|
4556
|
+
* Start sharing whiteboard given channelUrl
|
|
4557
|
+
* @param {string} channelUrl whiteboard url
|
|
4558
|
+
* @param {String} resourceToken token created by authorize media injector
|
|
4559
|
+
* @returns {Promise}
|
|
4560
|
+
* @public
|
|
4561
|
+
* @memberof Meeting
|
|
4562
|
+
*/
|
|
4563
|
+
startWhiteboardShare(channelUrl, resourceToken) {
|
|
4564
|
+
const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
|
|
4565
|
+
|
|
4566
|
+
if (!channelUrl) {
|
|
4567
|
+
return Promise.reject(new ParameterError('Cannot share without channelUrl.'));
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
if (whiteboard) {
|
|
4571
|
+
Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_INITIATED, meeting: this});
|
|
4572
|
+
|
|
4573
|
+
const body = {
|
|
4574
|
+
disposition: FLOOR_ACTION.GRANTED,
|
|
4575
|
+
personUrl: this.locusInfo.self.url,
|
|
4576
|
+
deviceUrl: this.deviceUrl,
|
|
4577
|
+
uri: whiteboard.url,
|
|
4578
|
+
resourceUrl: channelUrl
|
|
4579
|
+
};
|
|
4580
|
+
|
|
4581
|
+
if (resourceToken) {
|
|
4582
|
+
body.resourceToken = resourceToken;
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4585
|
+
return this.meetingRequest.changeMeetingFloor(body)
|
|
4586
|
+
.then(() => {
|
|
4587
|
+
this.isSharing = false;
|
|
4588
|
+
|
|
4589
|
+
return Promise.resolve();
|
|
4590
|
+
})
|
|
4591
|
+
.catch((error) => {
|
|
4592
|
+
LoggerProxy.logger.error('Meeting:index#startWhiteboardShare --> Error ', error);
|
|
4593
|
+
|
|
4594
|
+
Metrics.sendOperationalMetric(
|
|
4595
|
+
METRICS_OPERATIONAL_MEASURES.MEETING_START_WHITEBOARD_SHARE_FAILURE,
|
|
4596
|
+
{
|
|
4597
|
+
correlation_id: this.correlationId,
|
|
4598
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
4599
|
+
reason: error.message,
|
|
4600
|
+
stack: error.stack,
|
|
4601
|
+
board: {channelUrl}
|
|
4602
|
+
}
|
|
4603
|
+
);
|
|
4604
|
+
|
|
4605
|
+
return Promise.reject(error);
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4609
|
+
return Promise.reject(new ParameterError('Cannot share without whiteboard.'));
|
|
4610
|
+
}
|
|
4611
|
+
|
|
4612
|
+
/**
|
|
4613
|
+
* Stop sharing whiteboard given channelUrl
|
|
4614
|
+
* @param {string} channelUrl whiteboard url
|
|
4615
|
+
* @returns {Promise}
|
|
4616
|
+
* @public
|
|
4617
|
+
* @memberof Meeting
|
|
4618
|
+
*/
|
|
4619
|
+
stopWhiteboardShare(channelUrl) {
|
|
4620
|
+
const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
|
|
4621
|
+
|
|
4622
|
+
if (whiteboard) {
|
|
4623
|
+
Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_STOPPED, meeting: this});
|
|
4624
|
+
|
|
4625
|
+
return this.meetingRequest.changeMeetingFloor({
|
|
4626
|
+
disposition: FLOOR_ACTION.RELEASED,
|
|
4627
|
+
personUrl: this.locusInfo.self.url,
|
|
4628
|
+
deviceUrl: this.deviceUrl,
|
|
4629
|
+
uri: whiteboard.url
|
|
4630
|
+
})
|
|
4631
|
+
.catch((error) => {
|
|
4632
|
+
LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
|
|
4633
|
+
|
|
4634
|
+
Metrics.sendOperationalMetric(
|
|
4635
|
+
METRICS_OPERATIONAL_MEASURES.STOP_WHITEBOARD_SHARE_FAILURE,
|
|
4636
|
+
{
|
|
4637
|
+
correlation_id: this.correlationId,
|
|
4638
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
4639
|
+
reason: error.message,
|
|
4640
|
+
stack: error.stack,
|
|
4641
|
+
board: {channelUrl}
|
|
4642
|
+
}
|
|
4643
|
+
);
|
|
4644
|
+
|
|
4645
|
+
return Promise.reject(error);
|
|
4646
|
+
})
|
|
4647
|
+
.finally(() => {
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
return Promise.reject(new ParameterError('Cannot stop share without whiteboard.'));
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4476
4654
|
/**
|
|
4477
4655
|
* Start sharing content with server
|
|
4478
4656
|
* @returns {Promise} see #meetingRequest.changeMeetingFloor
|
|
@@ -4546,7 +4724,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4546
4724
|
Metrics.postEvent({event: eventType.SHARE_STOPPED, meeting: this});
|
|
4547
4725
|
Media.stopTracks(this.mediaProperties.shareTrack);
|
|
4548
4726
|
|
|
4549
|
-
if (
|
|
4727
|
+
if (content.floor.beneficiary.id !== this.selfId) {
|
|
4550
4728
|
// remote participant started sharing and caused our sharing to stop, we don't want to send any floor action request in that case
|
|
4551
4729
|
this.isSharing = false;
|
|
4552
4730
|
|
|
@@ -5049,4 +5227,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5049
5227
|
|
|
5050
5228
|
return (start && end) ? end - start : undefined;
|
|
5051
5229
|
}
|
|
5230
|
+
|
|
5231
|
+
/**
|
|
5232
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5233
|
+
* @returns {undefined}
|
|
5234
|
+
*/
|
|
5235
|
+
setStartSendingMediaDelay(typeMedia) {
|
|
5236
|
+
this[`startSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5237
|
+
this[`endSendingMediaDelay${typeMedia}`] = undefined;
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5240
|
+
/**
|
|
5241
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5242
|
+
* @returns {undefined}
|
|
5243
|
+
*/
|
|
5244
|
+
setEndSendingMediaDelay(typeMedia) {
|
|
5245
|
+
this[`endSendingMediaDelay${typeMedia}`] = performance.now();
|
|
5246
|
+
}
|
|
5247
|
+
|
|
5248
|
+
/**
|
|
5249
|
+
* @param {string} typeMedia 'audio' or 'video'
|
|
5250
|
+
* @returns {string} duration between join response and first media tx
|
|
5251
|
+
*/
|
|
5252
|
+
getSendingMediaDelayDuration(typeMedia) {
|
|
5253
|
+
const start = this[`startSendingMediaDelay${typeMedia}`];
|
|
5254
|
+
const end = this[`endSendingMediaDelay${typeMedia}`];
|
|
5255
|
+
|
|
5256
|
+
return (start && end) ? end - start : undefined;
|
|
5257
|
+
}
|
|
5052
5258
|
}
|
package/src/meeting/request.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import uuid from 'uuid';
|
|
2
2
|
import {debounce} from 'lodash';
|
|
3
3
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
4
|
-
import {
|
|
5
|
-
deviceType
|
|
6
|
-
} from '@webex/common';
|
|
4
|
+
import {deviceType} from '@webex/common';
|
|
7
5
|
|
|
8
6
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
9
7
|
import {
|
|
@@ -51,7 +49,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
51
49
|
*/
|
|
52
50
|
async joinMeeting(options) {
|
|
53
51
|
const {
|
|
54
|
-
asResourceOccupant,
|
|
52
|
+
asResourceOccupant,
|
|
53
|
+
sipUri,
|
|
54
|
+
meetingNumber,
|
|
55
|
+
deviceUrl,
|
|
56
|
+
locusUrl,
|
|
57
|
+
resourceId,
|
|
58
|
+
correlationId,
|
|
59
|
+
ensureConversation,
|
|
60
|
+
moderator,
|
|
61
|
+
pin,
|
|
62
|
+
moveToResource,
|
|
63
|
+
roapMessage,
|
|
64
|
+
preferTranscoding
|
|
55
65
|
} = options;
|
|
56
66
|
|
|
57
67
|
LoggerProxy.logger.info(
|
|
@@ -95,29 +105,16 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
95
105
|
if (locusUrl) {
|
|
96
106
|
url = `${locusUrl}/${PARTICIPANT}`;
|
|
97
107
|
}
|
|
98
|
-
else if (meetingNumber) {
|
|
108
|
+
else if (sipUri || meetingNumber) {
|
|
99
109
|
try {
|
|
100
110
|
await this.webex.internal.services.waitForCatalog('postauth');
|
|
101
111
|
url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
|
|
102
112
|
body.invitee = {
|
|
103
|
-
address: `wbxmn:${meetingNumber}`
|
|
113
|
+
address: sipUri || `wbxmn:${meetingNumber}`
|
|
104
114
|
};
|
|
105
115
|
}
|
|
106
116
|
catch (e) {
|
|
107
|
-
LoggerProxy.logger.error(`Meeting:request#joinMeeting
|
|
108
|
-
throw (e);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else if (sipUri) {
|
|
112
|
-
try {
|
|
113
|
-
await this.webex.internal.services.waitForCatalog('postauth');
|
|
114
|
-
url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
|
|
115
|
-
body.invitee = {
|
|
116
|
-
address: sipUri
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
catch (e) {
|
|
120
|
-
LoggerProxy.logger.error(`Meeting:request#joinMeeting sipUrl --> ${e}`);
|
|
117
|
+
LoggerProxy.logger.error(`Meeting:request#joinMeeting ${sipUri ? 'sipUri' : 'meetingNumber'} --> ${e}`);
|
|
121
118
|
throw (e);
|
|
122
119
|
}
|
|
123
120
|
}
|
|
@@ -154,7 +151,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
154
151
|
* @private
|
|
155
152
|
*/
|
|
156
153
|
dialIn({
|
|
157
|
-
locusUrl,
|
|
154
|
+
locusUrl,
|
|
155
|
+
dialInUrl,
|
|
156
|
+
clientUrl,
|
|
157
|
+
correlationId
|
|
158
158
|
}) {
|
|
159
159
|
LoggerProxy.logger.info(
|
|
160
160
|
'Meeting:request#dialIn --> Provisioning a dial in device',
|
|
@@ -195,7 +195,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
195
195
|
* @private
|
|
196
196
|
*/
|
|
197
197
|
dialOut({
|
|
198
|
-
locusUrl,
|
|
198
|
+
locusUrl,
|
|
199
|
+
dialOutUrl,
|
|
200
|
+
phoneNumber,
|
|
201
|
+
clientUrl,
|
|
202
|
+
correlationId
|
|
199
203
|
}) {
|
|
200
204
|
LoggerProxy.logger.info(
|
|
201
205
|
'Meeting:request#dialOut --> Provisioning a dial out device',
|
|
@@ -294,7 +298,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
294
298
|
* @private
|
|
295
299
|
*/
|
|
296
300
|
disconnectPhoneAudio({
|
|
297
|
-
locusUrl,
|
|
301
|
+
locusUrl,
|
|
302
|
+
phoneUrl,
|
|
303
|
+
correlationId,
|
|
304
|
+
selfId
|
|
298
305
|
}) {
|
|
299
306
|
LoggerProxy.logger.info(
|
|
300
307
|
`Meeting:request#disconnectPhoneAudio --> request phone ${phoneUrl} to leave`,
|
|
@@ -334,7 +341,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
334
341
|
* @returns {Promise}
|
|
335
342
|
*/
|
|
336
343
|
leaveMeeting({
|
|
337
|
-
locusUrl,
|
|
344
|
+
locusUrl,
|
|
345
|
+
selfId,
|
|
346
|
+
deviceUrl: url,
|
|
347
|
+
resourceId,
|
|
348
|
+
correlationId
|
|
338
349
|
}) {
|
|
339
350
|
LoggerProxy.logger.info(
|
|
340
351
|
'Meeting:request#leaveMeeting --> Leaving a meeting',
|
|
@@ -512,13 +523,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
512
523
|
};
|
|
513
524
|
}
|
|
514
525
|
|
|
526
|
+
const body = {
|
|
527
|
+
floor: floorReq,
|
|
528
|
+
resourceUrl: options.resourceUrl
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
if (options?.resourceToken) {
|
|
532
|
+
body.resourceToken = options?.resourceToken;
|
|
533
|
+
}
|
|
534
|
+
|
|
515
535
|
return this.request({
|
|
516
536
|
uri: options.uri,
|
|
517
537
|
method: HTTP_VERBS.PUT,
|
|
518
|
-
body
|
|
519
|
-
floor: floorReq,
|
|
520
|
-
resourceUrl: options.resourceUrl
|
|
521
|
-
}
|
|
538
|
+
body
|
|
522
539
|
});
|
|
523
540
|
}
|
|
524
541
|
|
|
@@ -559,7 +576,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
559
576
|
* @returns {Promise}
|
|
560
577
|
*/
|
|
561
578
|
changeVideoLayout({
|
|
562
|
-
locusUrl,
|
|
579
|
+
locusUrl,
|
|
580
|
+
deviceUrl,
|
|
581
|
+
layoutType,
|
|
582
|
+
main,
|
|
583
|
+
content
|
|
563
584
|
}) {
|
|
564
585
|
// send main/content renderInfo only if both width and height are specified
|
|
565
586
|
if (main && (!main.width || !main.height)) {
|
|
@@ -108,7 +108,10 @@ export default class MeetingInfo {
|
|
|
108
108
|
* @memberof MeetingInfo
|
|
109
109
|
*/
|
|
110
110
|
fetchMeetingInfo(destination, type = null) {
|
|
111
|
-
return this.fetchInfoOptions(
|
|
111
|
+
return this.fetchInfoOptions(
|
|
112
|
+
MeetingInfoUtil.extractDestination(destination, type),
|
|
113
|
+
type
|
|
114
|
+
).then((options) =>
|
|
112
115
|
// fetch meeting info
|
|
113
116
|
this.requestFetchInfo(options).catch((error) => {
|
|
114
117
|
// if it failed the first time as meeting link
|
package/src/meeting-info/util.js
CHANGED
|
@@ -35,6 +35,19 @@ import {
|
|
|
35
35
|
|
|
36
36
|
const MeetingInfoUtil = {};
|
|
37
37
|
|
|
38
|
+
MeetingInfoUtil.extractDestination = (destination, type) => {
|
|
39
|
+
let dest = destination;
|
|
40
|
+
|
|
41
|
+
if (type === _LOCUS_ID_) {
|
|
42
|
+
if (!(destination && destination.url)) {
|
|
43
|
+
throw new ParameterError('You cannot create a meeting by locus without a locus.url defined');
|
|
44
|
+
}
|
|
45
|
+
dest = destination.url;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return dest;
|
|
49
|
+
};
|
|
50
|
+
|
|
38
51
|
MeetingInfoUtil.getParsedUrl = (link) => {
|
|
39
52
|
try {
|
|
40
53
|
let parsedUrl = url.parse(link);
|