@webex/plugin-meetings 3.0.0-beta.154 → 3.0.0-beta.156
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 +20 -1
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/meeting/index.js +14 -6
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +111 -18
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/types/meetings/meetings.types.d.ts +1 -1
- package/dist/types/multistream/mediaRequestManager.d.ts +14 -0
- package/package.json +20 -20
- package/src/breakouts/breakout.ts +19 -2
- package/src/meeting/index.ts +16 -4
- package/src/meetings/meetings.types.ts +4 -1
- package/src/multistream/mediaRequestManager.ts +149 -40
- package/test/unit/spec/breakouts/breakout.ts +31 -1
- package/test/unit/spec/meeting/index.js +85 -0
- package/test/unit/spec/meetings/index.js +1 -3
- package/test/unit/spec/multistream/mediaRequestManager.ts +341 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["meetings.types.ts"],"sourcesContent":["import type {\n NoiseReductionEffectOptions,\n VirtualBackgroundEffectOptions,\n} from '@webex/media-helpers';\n\ntype INoiseReductionEffect = Omit
|
|
1
|
+
{"version":3,"names":[],"sources":["meetings.types.ts"],"sourcesContent":["import type {\n NoiseReductionEffectOptions,\n VirtualBackgroundEffectOptions,\n} from '@webex/media-helpers';\n\ntype INoiseReductionEffect = Omit<\n NoiseReductionEffectOptions,\n 'authToken' | 'workletProcessorUrl' | 'legacyProcessorUrl'\n>;\ntype IVirtualBackgroundEffect = Omit<VirtualBackgroundEffectOptions, 'authToken'>;\n\nexport type {INoiseReductionEffect, IVirtualBackgroundEffect};\n"],"mappings":""}
|
|
@@ -6,16 +6,15 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
8
|
exports.MediaRequestManager = void 0;
|
|
9
|
-
var _entries = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/entries"));
|
|
10
|
-
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
11
9
|
var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
|
|
12
|
-
var
|
|
10
|
+
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
11
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray"));
|
|
13
12
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
|
|
14
13
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
|
|
15
14
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
16
15
|
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
|
|
17
16
|
var _debounce2 = _interopRequireDefault(require("lodash/debounce"));
|
|
18
|
-
var
|
|
17
|
+
var _cloneDeepWith2 = _interopRequireDefault(require("lodash/cloneDeepWith"));
|
|
19
18
|
var _internalMediaCore = require("@webex/internal-media-core");
|
|
20
19
|
var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
|
|
21
20
|
var _receiveSlot = require("./receiveSlot");
|
|
@@ -41,11 +40,17 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
41
40
|
(0, _defineProperty2.default)(this, "sourceUpdateListener", void 0);
|
|
42
41
|
(0, _defineProperty2.default)(this, "debouncedSourceUpdateListener", void 0);
|
|
43
42
|
(0, _defineProperty2.default)(this, "previousStreamRequests", []);
|
|
43
|
+
(0, _defineProperty2.default)(this, "trimRequestsToNumOfSources", void 0);
|
|
44
|
+
(0, _defineProperty2.default)(this, "numTotalSources", void 0);
|
|
45
|
+
(0, _defineProperty2.default)(this, "numLiveSources", void 0);
|
|
44
46
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
45
47
|
this.counter = 0;
|
|
48
|
+
this.numLiveSources = 0;
|
|
49
|
+
this.numTotalSources = 0;
|
|
46
50
|
this.clientRequests = {};
|
|
47
51
|
this.degradationPreferences = options.degradationPreferences;
|
|
48
52
|
this.kind = options.kind;
|
|
53
|
+
this.trimRequestsToNumOfSources = options.trimRequestsToNumOfSources;
|
|
49
54
|
this.sourceUpdateListener = this.commit.bind(this);
|
|
50
55
|
this.debouncedSourceUpdateListener = (0, _debounce2.default)(this.sourceUpdateListener, DEBOUNCED_SOURCE_UPDATE_TIME);
|
|
51
56
|
}
|
|
@@ -57,22 +62,18 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
57
62
|
}
|
|
58
63
|
}, {
|
|
59
64
|
key: "getDegradedClientRequests",
|
|
60
|
-
value: function getDegradedClientRequests() {
|
|
65
|
+
value: function getDegradedClientRequests(clientRequests) {
|
|
61
66
|
var _this = this;
|
|
62
|
-
var clientRequests = (0, _cloneDeep2.default)(this.clientRequests);
|
|
63
67
|
var maxFsLimits = [(0, _remoteMedia.getMaxFs)('best'), (0, _remoteMedia.getMaxFs)('large'), (0, _remoteMedia.getMaxFs)('medium'), (0, _remoteMedia.getMaxFs)('small'), (0, _remoteMedia.getMaxFs)('very small'), (0, _remoteMedia.getMaxFs)('thumbnail')];
|
|
64
68
|
|
|
65
69
|
// reduce max-fs until total macroblocks is below limit
|
|
66
70
|
var _loop = function _loop(i) {
|
|
67
71
|
var totalMacroblocksRequested = 0;
|
|
68
|
-
(0,
|
|
69
|
-
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
70
|
-
id = _ref2[0],
|
|
71
|
-
mr = _ref2[1];
|
|
72
|
+
(0, _values.default)(clientRequests).forEach(function (mr) {
|
|
72
73
|
if (mr.codecInfo) {
|
|
73
74
|
mr.codecInfo.maxFs = Math.min(mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs, mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs, maxFsLimits[i]);
|
|
74
75
|
// we only consider sources with "live" state
|
|
75
|
-
var slotsWithLiveSource =
|
|
76
|
+
var slotsWithLiveSource = mr.receiveSlots.filter(function (rs) {
|
|
76
77
|
return rs.sourceState === 'live';
|
|
77
78
|
});
|
|
78
79
|
totalMacroblocksRequested += mr.codecInfo.maxFs * slotsWithLiveSource.length;
|
|
@@ -91,7 +92,6 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
91
92
|
var _ret = _loop(i);
|
|
92
93
|
if (_ret === "break") break;
|
|
93
94
|
}
|
|
94
|
-
return clientRequests;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -174,18 +174,102 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
174
174
|
value: function clearPreviousRequests() {
|
|
175
175
|
this.previousStreamRequests = [];
|
|
176
176
|
}
|
|
177
|
+
|
|
178
|
+
/** Modifies the passed in clientRequests and makes sure that in total they don't ask
|
|
179
|
+
* for more streams than there are available.
|
|
180
|
+
*
|
|
181
|
+
* @param {Object} clientRequests
|
|
182
|
+
* @returns {void}
|
|
183
|
+
*/
|
|
184
|
+
}, {
|
|
185
|
+
key: "trimRequests",
|
|
186
|
+
value: function trimRequests(clientRequests) {
|
|
187
|
+
var preferLiveVideo = this.getPreferLiveVideo();
|
|
188
|
+
if (!this.trimRequestsToNumOfSources) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// preferLiveVideo being undefined means that there are no active-speaker requests so we don't need to do any trimming
|
|
193
|
+
if (preferLiveVideo === undefined) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
var numStreamsAvailable = preferLiveVideo ? this.numLiveSources : this.numTotalSources;
|
|
197
|
+
(0, _values.default)(clientRequests).sort(function (a, b) {
|
|
198
|
+
// we have to count how many streams we're asking for
|
|
199
|
+
// and should not ask for more than numStreamsAvailable in total,
|
|
200
|
+
// so we might need to trim active-speaker requests and first ones to trim should be
|
|
201
|
+
// the ones with lowest priority
|
|
202
|
+
|
|
203
|
+
// receiver-selected requests have priority over active-speakers
|
|
204
|
+
if (a.policyInfo.policy === 'receiver-selected') {
|
|
205
|
+
return -1;
|
|
206
|
+
}
|
|
207
|
+
if (b.policyInfo.policy === 'receiver-selected') {
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// and active-speakers are sorted by descending priority
|
|
212
|
+
return b.policyInfo.priority - a.policyInfo.priority;
|
|
213
|
+
}).forEach(function (request) {
|
|
214
|
+
// we only trim active-speaker requests
|
|
215
|
+
if (request.policyInfo.policy === 'active-speaker') {
|
|
216
|
+
var trimmedCount = Math.min(numStreamsAvailable, request.receiveSlots.length);
|
|
217
|
+
request.receiveSlots.length = trimmedCount;
|
|
218
|
+
numStreamsAvailable -= trimmedCount;
|
|
219
|
+
} else {
|
|
220
|
+
numStreamsAvailable -= request.receiveSlots.length;
|
|
221
|
+
}
|
|
222
|
+
if (numStreamsAvailable < 0) {
|
|
223
|
+
numStreamsAvailable = 0;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}, {
|
|
228
|
+
key: "getPreferLiveVideo",
|
|
229
|
+
value: function getPreferLiveVideo() {
|
|
230
|
+
var preferLiveVideo;
|
|
231
|
+
(0, _values.default)(this.clientRequests).forEach(function (mr) {
|
|
232
|
+
if (mr.policyInfo.policy === 'active-speaker') {
|
|
233
|
+
// take the value from first encountered active speaker request
|
|
234
|
+
if (preferLiveVideo === undefined) {
|
|
235
|
+
preferLiveVideo = mr.policyInfo.preferLiveVideo;
|
|
236
|
+
}
|
|
237
|
+
if (mr.policyInfo.preferLiveVideo !== preferLiveVideo) {
|
|
238
|
+
throw new Error('a mix of active-speaker groups with different values for preferLiveVideo is not supported');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return preferLiveVideo;
|
|
243
|
+
}
|
|
244
|
+
}, {
|
|
245
|
+
key: "cloneClientRequests",
|
|
246
|
+
value: function cloneClientRequests() {
|
|
247
|
+
// we clone the client requests but without cloning the ReceiveSlots that they reference
|
|
248
|
+
return (0, _cloneDeepWith2.default)(this.clientRequests, function (value, key) {
|
|
249
|
+
if (key === 'receiveSlots') {
|
|
250
|
+
return (0, _toConsumableArray2.default)(value);
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
177
255
|
}, {
|
|
178
256
|
key: "sendRequests",
|
|
179
257
|
value: function sendRequests() {
|
|
180
258
|
var _this3 = this;
|
|
181
259
|
var streamRequests = [];
|
|
182
|
-
|
|
260
|
+
|
|
261
|
+
// clone the requests so that any modifications we do to them don't affect the original ones
|
|
262
|
+
var clientRequests = this.cloneClientRequests();
|
|
263
|
+
this.trimRequests(clientRequests);
|
|
264
|
+
this.getDegradedClientRequests(clientRequests);
|
|
183
265
|
|
|
184
266
|
// map all the client media requests to wcme stream requests
|
|
185
267
|
(0, _values.default)(clientRequests).forEach(function (mr) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
268
|
+
if (mr.receiveSlots.length > 0) {
|
|
269
|
+
streamRequests.push(new _internalMediaCore.StreamRequest(mr.policyInfo.policy === 'active-speaker' ? _internalMediaCore.Policy.ActiveSpeaker : _internalMediaCore.Policy.ReceiverSelected, mr.policyInfo.policy === 'active-speaker' ? new _internalMediaCore.ActiveSpeakerInfo(mr.policyInfo.priority, mr.policyInfo.crossPriorityDuplication, mr.policyInfo.crossPolicyDuplication, mr.policyInfo.preferLiveVideo) : new _internalMediaCore.ReceiverSelectedInfo(mr.policyInfo.csi), mr.receiveSlots.map(function (receiveSlot) {
|
|
270
|
+
return receiveSlot.wcmeReceiveSlot;
|
|
271
|
+
}), _this3.getMaxPayloadBitsPerSecond(mr), mr.codecInfo && [new _internalMediaCore.CodecInfo(0x80, new _internalMediaCore.H264Codec(mr.codecInfo.maxFs, mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps, _this3.getH264MaxMbps(mr), mr.codecInfo.maxWidth, mr.codecInfo.maxHeight))]));
|
|
272
|
+
}
|
|
189
273
|
});
|
|
190
274
|
|
|
191
275
|
//! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/json-multistream)
|
|
@@ -206,8 +290,8 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
206
290
|
// eslint-disable-next-line no-plusplus
|
|
207
291
|
var newId = "".concat(this.counter++);
|
|
208
292
|
this.clientRequests[newId] = mediaRequest;
|
|
209
|
-
var eventHandler = function eventHandler(
|
|
210
|
-
var maxFs =
|
|
293
|
+
var eventHandler = function eventHandler(_ref) {
|
|
294
|
+
var maxFs = _ref.maxFs;
|
|
211
295
|
mediaRequest.preferredMaxFs = maxFs;
|
|
212
296
|
_this4.debouncedSourceUpdateListener();
|
|
213
297
|
};
|
|
@@ -245,6 +329,15 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
245
329
|
key: "reset",
|
|
246
330
|
value: function reset() {
|
|
247
331
|
this.clientRequests = {};
|
|
332
|
+
this.numTotalSources = 0;
|
|
333
|
+
this.numLiveSources = 0;
|
|
334
|
+
}
|
|
335
|
+
}, {
|
|
336
|
+
key: "setNumCurrentSources",
|
|
337
|
+
value: function setNumCurrentSources(numTotalSources, numLiveSources) {
|
|
338
|
+
this.numTotalSources = numTotalSources;
|
|
339
|
+
this.numLiveSources = numLiveSources;
|
|
340
|
+
this.sendRequests();
|
|
248
341
|
}
|
|
249
342
|
}]);
|
|
250
343
|
return MediaRequestManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","DEBOUNCED_SOURCE_UPDATE_TIME","MediaRequestManager","sendMediaRequestsCallback","options","counter","clientRequests","degradationPreferences","kind","sourceUpdateListener","commit","bind","debouncedSourceUpdateListener","sendRequests","maxFsLimits","getMaxFs","totalMacroblocksRequested","forEach","id","mr","codecInfo","Math","min","preferredMaxFs","i","slotsWithLiveSource","receiveSlots","filter","rs","sourceState","length","maxMacroblocksLimit","LoggerProxy","logger","warn","streamRequestA","streamRequestB","_toJmpStreamRequest","newRequests","previousStreamRequests","every","req","idx","isEqual","mediaRequest","RecommendedOpusBitrates","FB_MONO_MUSIC","getRecommendedMaxBitrateForFrameSize","streamRequests","getDegradedClientRequests","push","StreamRequest","policyInfo","policy","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","priority","crossPriorityDuplication","crossPolicyDuplication","preferLiveVideo","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","getMaxPayloadBitsPerSecond","WcmeCodecInfo","H264Codec","getH264MaxMbps","maxWidth","maxHeight","checkIsNewRequestsEqualToPrev","info","newId","eventHandler","handleMaxFs","on","ReceiveSlotEvents","SourceUpdate","MaxFsUpdate","requestId","off"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {\n StreamRequest,\n Policy,\n ActiveSpeakerInfo,\n ReceiverSelectedInfo,\n CodecInfo as WcmeCodecInfo,\n H264Codec,\n getRecommendedMaxBitrateForFrameSize,\n RecommendedOpusBitrates,\n} from '@webex/internal-media-core';\nimport {cloneDeep, debounce, isEmpty} from 'lodash';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\nimport {getMaxFs} from './remoteMedia';\n\nexport interface ActiveSpeakerPolicyInfo {\n policy: 'active-speaker';\n priority: number;\n crossPriorityDuplication: boolean;\n crossPolicyDuplication: boolean;\n preferLiveVideo: boolean;\n}\n\nexport interface ReceiverSelectedPolicyInfo {\n policy: 'receiver-selected';\n csi: number;\n}\n\nexport type PolicyInfo = ActiveSpeakerPolicyInfo | ReceiverSelectedPolicyInfo;\n\nexport interface H264CodecInfo {\n codec: 'h264';\n maxFs?: number;\n maxFps?: number;\n maxMbps?: number;\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport type CodecInfo = H264CodecInfo; // we'll add AV1 here in the future when it's available\n\nexport interface MediaRequest {\n policyInfo: PolicyInfo;\n receiveSlots: Array<ReceiveSlot>;\n codecInfo?: CodecInfo;\n preferredMaxFs?: number;\n handleMaxFs?: ({maxFs}: {maxFs: number}) => void;\n}\n\nexport type MediaRequestId = string;\n\nconst CODEC_DEFAULTS = {\n h264: {\n maxFs: 8192,\n maxFps: 3000,\n maxMbps: 245760,\n },\n};\n\nconst DEBOUNCED_SOURCE_UPDATE_TIME = 1000;\n\ntype DegradationPreferences = {\n maxMacroblocksLimit: number;\n};\n\ntype SendMediaRequestsCallback = (streamRequests: StreamRequest[]) => void;\ntype Kind = 'audio' | 'video';\n\ntype Options = {\n degradationPreferences: DegradationPreferences;\n kind: Kind;\n};\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\n\n private kind: Kind;\n\n private counter: number;\n\n private clientRequests: {[key: MediaRequestId]: MediaRequest};\n\n private degradationPreferences: DegradationPreferences;\n\n private sourceUpdateListener: () => void;\n\n private debouncedSourceUpdateListener: () => void;\n\n private previousStreamRequests: Array<StreamRequest> = [];\n\n constructor(sendMediaRequestsCallback: SendMediaRequestsCallback, options: Options) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.degradationPreferences = options.degradationPreferences;\n this.kind = options.kind;\n this.sourceUpdateListener = this.commit.bind(this);\n this.debouncedSourceUpdateListener = debounce(\n this.sourceUpdateListener,\n DEBOUNCED_SOURCE_UPDATE_TIME\n );\n }\n\n public setDegradationPreferences(degradationPreferences: DegradationPreferences) {\n this.degradationPreferences = degradationPreferences;\n this.sendRequests(); // re-send requests after preferences are set\n }\n\n private getDegradedClientRequests() {\n const clientRequests = cloneDeep(this.clientRequests);\n const maxFsLimits = [\n getMaxFs('best'),\n getMaxFs('large'),\n getMaxFs('medium'),\n getMaxFs('small'),\n getMaxFs('very small'),\n getMaxFs('thumbnail'),\n ];\n\n // reduce max-fs until total macroblocks is below limit\n for (let i = 0; i < maxFsLimits.length; i += 1) {\n let totalMacroblocksRequested = 0;\n Object.entries(clientRequests).forEach(([id, mr]) => {\n if (mr.codecInfo) {\n mr.codecInfo.maxFs = Math.min(\n mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs,\n mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,\n maxFsLimits[i]\n );\n // we only consider sources with \"live\" state\n const slotsWithLiveSource = this.clientRequests[id].receiveSlots.filter(\n (rs) => rs.sourceState === 'live'\n );\n totalMacroblocksRequested += mr.codecInfo.maxFs * slotsWithLiveSource.length;\n }\n });\n if (totalMacroblocksRequested <= this.degradationPreferences.maxMacroblocksLimit) {\n if (i !== 0) {\n LoggerProxy.logger.warn(\n `multistream:mediaRequestManager --> too many streams with high max-fs, frame size will be limited to ${maxFsLimits[i]}`\n );\n }\n break;\n } else if (i === maxFsLimits.length - 1) {\n LoggerProxy.logger.warn(\n `multistream:mediaRequestManager --> even with frame size limited to ${maxFsLimits[i]} you are still requesting too many streams, consider reducing the number of requests`\n );\n }\n }\n\n return clientRequests;\n }\n\n /**\n * Returns true if two stream requests are the same, false otherwise.\n *\n * @param {StreamRequest} streamRequestA - Stream request A for comparison.\n * @param {StreamRequest} streamRequestB - Stream request B for comparison.\n * @returns {boolean} - Whether they are equal.\n */\n // eslint-disable-next-line class-methods-use-this\n public isEqual(streamRequestA: StreamRequest, streamRequestB: StreamRequest) {\n return (\n JSON.stringify(streamRequestA._toJmpStreamRequest()) ===\n JSON.stringify(streamRequestB._toJmpStreamRequest())\n );\n }\n\n /**\n * Compares new stream requests to previous ones and determines\n * if they are the same.\n *\n * @param {StreamRequest[]} newRequests - Array with new requests.\n * @returns {boolean} - True if they are equal, false otherwise.\n */\n private checkIsNewRequestsEqualToPrev(newRequests: StreamRequest[]) {\n return (\n !isEmpty(this.previousStreamRequests) &&\n this.previousStreamRequests.length === newRequests.length &&\n this.previousStreamRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))\n );\n }\n\n /**\n * Returns the maxPayloadBitsPerSecond per Stream\n *\n * If MediaRequestManager kind is \"audio\", a constant bitrate will be returned.\n * If MediaRequestManager kind is \"video\", the bitrate will be calculated based\n * on maxFs (default h264 maxFs as fallback if maxFs is not defined)\n *\n * @param {MediaRequest} mediaRequest - mediaRequest to take data from\n * @returns {number} maxPayloadBitsPerSecond\n */\n private getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number {\n if (this.kind === 'audio') {\n // return mono_music bitrate default if the kind of mediarequest manager is audio:\n return RecommendedOpusBitrates.FB_MONO_MUSIC;\n }\n\n return getRecommendedMaxBitrateForFrameSize(\n mediaRequest.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs\n );\n }\n\n /**\n * Returns the max Macro Blocks per second (maxMbps) per H264 Stream\n *\n * The maxMbps will be calculated based on maxFs and maxFps\n * (default h264 maxFps as fallback if maxFps is not defined)\n *\n * @param {MediaRequest} mediaRequest - mediaRequest to take data from\n * @returns {number} maxMbps\n */\n // eslint-disable-next-line class-methods-use-this\n private getH264MaxMbps(mediaRequest: MediaRequest): number {\n // fallback for maxFps (not needed for maxFs, since there is a fallback already in getDegradedClientRequests)\n const maxFps = mediaRequest.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps;\n\n // divided by 100 since maxFps is 3000 (for 30 frames per seconds)\n return (mediaRequest.codecInfo.maxFs * maxFps) / 100;\n }\n\n /**\n * Clears the previous stream requests.\n *\n * @returns {void}\n */\n public clearPreviousRequests(): void {\n this.previousStreamRequests = [];\n }\n\n private sendRequests() {\n const streamRequests: StreamRequest[] = [];\n\n const clientRequests = this.getDegradedClientRequests();\n\n // map all the client media requests to wcme stream requests\n Object.values(clientRequests).forEach((mr) => {\n streamRequests.push(\n new StreamRequest(\n mr.policyInfo.policy === 'active-speaker'\n ? Policy.ActiveSpeaker\n : Policy.ReceiverSelected,\n mr.policyInfo.policy === 'active-speaker'\n ? new ActiveSpeakerInfo(\n mr.policyInfo.priority,\n mr.policyInfo.crossPriorityDuplication,\n mr.policyInfo.crossPolicyDuplication,\n mr.policyInfo.preferLiveVideo\n )\n : new ReceiverSelectedInfo(mr.policyInfo.csi),\n mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),\n this.getMaxPayloadBitsPerSecond(mr),\n mr.codecInfo && [\n new WcmeCodecInfo(\n 0x80,\n new H264Codec(\n mr.codecInfo.maxFs,\n mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,\n this.getH264MaxMbps(mr),\n mr.codecInfo.maxWidth,\n mr.codecInfo.maxHeight\n )\n ),\n ]\n )\n );\n });\n\n //! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/json-multistream)\n // https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713\n if (!this.checkIsNewRequestsEqualToPrev(streamRequests)) {\n this.sendMediaRequestsCallback(streamRequests);\n this.previousStreamRequests = streamRequests;\n LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);\n } else {\n LoggerProxy.logger.info(\n `multistream:sendRequests --> detected duplicate WCME requests, skipping them... `\n );\n }\n }\n\n public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {\n // eslint-disable-next-line no-plusplus\n const newId = `${this.counter++}`;\n\n this.clientRequests[newId] = mediaRequest;\n\n const eventHandler = ({maxFs}) => {\n mediaRequest.preferredMaxFs = maxFs;\n this.debouncedSourceUpdateListener();\n };\n mediaRequest.handleMaxFs = eventHandler;\n\n mediaRequest.receiveSlots.forEach((rs) => {\n rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.on(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);\n });\n\n if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\n const mediaRequest = this.clientRequests[requestId];\n\n mediaRequest?.receiveSlots.forEach((rs) => {\n rs.off(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.off(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);\n });\n\n delete this.clientRequests[requestId];\n\n if (commit) {\n this.commit();\n }\n }\n\n public commit() {\n return this.sendRequests();\n }\n\n public reset() {\n this.clientRequests = {};\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","DEBOUNCED_SOURCE_UPDATE_TIME","MediaRequestManager","sendMediaRequestsCallback","options","counter","numLiveSources","numTotalSources","clientRequests","degradationPreferences","kind","trimRequestsToNumOfSources","sourceUpdateListener","commit","bind","debouncedSourceUpdateListener","sendRequests","maxFsLimits","getMaxFs","totalMacroblocksRequested","forEach","mr","codecInfo","Math","min","preferredMaxFs","i","slotsWithLiveSource","receiveSlots","filter","rs","sourceState","length","maxMacroblocksLimit","LoggerProxy","logger","warn","streamRequestA","streamRequestB","_toJmpStreamRequest","newRequests","previousStreamRequests","every","req","idx","isEqual","mediaRequest","RecommendedOpusBitrates","FB_MONO_MUSIC","getRecommendedMaxBitrateForFrameSize","preferLiveVideo","getPreferLiveVideo","undefined","numStreamsAvailable","sort","a","b","policyInfo","policy","priority","request","trimmedCount","Error","value","key","streamRequests","cloneClientRequests","trimRequests","getDegradedClientRequests","push","StreamRequest","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","crossPriorityDuplication","crossPolicyDuplication","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","getMaxPayloadBitsPerSecond","WcmeCodecInfo","H264Codec","getH264MaxMbps","maxWidth","maxHeight","checkIsNewRequestsEqualToPrev","info","newId","eventHandler","handleMaxFs","on","ReceiveSlotEvents","SourceUpdate","MaxFsUpdate","requestId","off"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {\n StreamRequest,\n Policy,\n ActiveSpeakerInfo,\n ReceiverSelectedInfo,\n CodecInfo as WcmeCodecInfo,\n H264Codec,\n getRecommendedMaxBitrateForFrameSize,\n RecommendedOpusBitrates,\n} from '@webex/internal-media-core';\nimport {cloneDeepWith, debounce, isEmpty} from 'lodash';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\nimport {getMaxFs} from './remoteMedia';\n\nexport interface ActiveSpeakerPolicyInfo {\n policy: 'active-speaker';\n priority: number;\n crossPriorityDuplication: boolean;\n crossPolicyDuplication: boolean;\n preferLiveVideo: boolean;\n}\n\nexport interface ReceiverSelectedPolicyInfo {\n policy: 'receiver-selected';\n csi: number;\n}\n\nexport type PolicyInfo = ActiveSpeakerPolicyInfo | ReceiverSelectedPolicyInfo;\n\nexport interface H264CodecInfo {\n codec: 'h264';\n maxFs?: number;\n maxFps?: number;\n maxMbps?: number;\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport type CodecInfo = H264CodecInfo; // we'll add AV1 here in the future when it's available\n\nexport interface MediaRequest {\n policyInfo: PolicyInfo;\n receiveSlots: Array<ReceiveSlot>;\n codecInfo?: CodecInfo;\n preferredMaxFs?: number;\n handleMaxFs?: ({maxFs}: {maxFs: number}) => void;\n}\n\nexport type MediaRequestId = string;\n\nconst CODEC_DEFAULTS = {\n h264: {\n maxFs: 8192,\n maxFps: 3000,\n maxMbps: 245760,\n },\n};\n\nconst DEBOUNCED_SOURCE_UPDATE_TIME = 1000;\n\ntype DegradationPreferences = {\n maxMacroblocksLimit: number;\n};\n\ntype SendMediaRequestsCallback = (streamRequests: StreamRequest[]) => void;\ntype Kind = 'audio' | 'video';\n\ntype Options = {\n degradationPreferences: DegradationPreferences;\n kind: Kind;\n trimRequestsToNumOfSources: boolean; // if enabled, AS speaker requests will be trimmed based on the calls to setNumCurrentSources()\n};\n\ntype ClientRequestsMap = {[key: MediaRequestId]: MediaRequest};\n\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\n\n private kind: Kind;\n\n private counter: number;\n\n private clientRequests: ClientRequestsMap;\n\n private degradationPreferences: DegradationPreferences;\n\n private sourceUpdateListener: () => void;\n\n private debouncedSourceUpdateListener: () => void;\n\n private previousStreamRequests: Array<StreamRequest> = [];\n\n private trimRequestsToNumOfSources: boolean;\n private numTotalSources: number;\n private numLiveSources: number;\n\n constructor(sendMediaRequestsCallback: SendMediaRequestsCallback, options: Options) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.numLiveSources = 0;\n this.numTotalSources = 0;\n this.clientRequests = {};\n this.degradationPreferences = options.degradationPreferences;\n this.kind = options.kind;\n this.trimRequestsToNumOfSources = options.trimRequestsToNumOfSources;\n this.sourceUpdateListener = this.commit.bind(this);\n this.debouncedSourceUpdateListener = debounce(\n this.sourceUpdateListener,\n DEBOUNCED_SOURCE_UPDATE_TIME\n );\n }\n\n public setDegradationPreferences(degradationPreferences: DegradationPreferences) {\n this.degradationPreferences = degradationPreferences;\n this.sendRequests(); // re-send requests after preferences are set\n }\n\n private getDegradedClientRequests(clientRequests: ClientRequestsMap) {\n const maxFsLimits = [\n getMaxFs('best'),\n getMaxFs('large'),\n getMaxFs('medium'),\n getMaxFs('small'),\n getMaxFs('very small'),\n getMaxFs('thumbnail'),\n ];\n\n // reduce max-fs until total macroblocks is below limit\n for (let i = 0; i < maxFsLimits.length; i += 1) {\n let totalMacroblocksRequested = 0;\n Object.values(clientRequests).forEach((mr) => {\n if (mr.codecInfo) {\n mr.codecInfo.maxFs = Math.min(\n mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs,\n mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,\n maxFsLimits[i]\n );\n // we only consider sources with \"live\" state\n const slotsWithLiveSource = mr.receiveSlots.filter((rs) => rs.sourceState === 'live');\n totalMacroblocksRequested += mr.codecInfo.maxFs * slotsWithLiveSource.length;\n }\n });\n if (totalMacroblocksRequested <= this.degradationPreferences.maxMacroblocksLimit) {\n if (i !== 0) {\n LoggerProxy.logger.warn(\n `multistream:mediaRequestManager --> too many streams with high max-fs, frame size will be limited to ${maxFsLimits[i]}`\n );\n }\n break;\n } else if (i === maxFsLimits.length - 1) {\n LoggerProxy.logger.warn(\n `multistream:mediaRequestManager --> even with frame size limited to ${maxFsLimits[i]} you are still requesting too many streams, consider reducing the number of requests`\n );\n }\n }\n }\n\n /**\n * Returns true if two stream requests are the same, false otherwise.\n *\n * @param {StreamRequest} streamRequestA - Stream request A for comparison.\n * @param {StreamRequest} streamRequestB - Stream request B for comparison.\n * @returns {boolean} - Whether they are equal.\n */\n // eslint-disable-next-line class-methods-use-this\n public isEqual(streamRequestA: StreamRequest, streamRequestB: StreamRequest) {\n return (\n JSON.stringify(streamRequestA._toJmpStreamRequest()) ===\n JSON.stringify(streamRequestB._toJmpStreamRequest())\n );\n }\n\n /**\n * Compares new stream requests to previous ones and determines\n * if they are the same.\n *\n * @param {StreamRequest[]} newRequests - Array with new requests.\n * @returns {boolean} - True if they are equal, false otherwise.\n */\n private checkIsNewRequestsEqualToPrev(newRequests: StreamRequest[]) {\n return (\n !isEmpty(this.previousStreamRequests) &&\n this.previousStreamRequests.length === newRequests.length &&\n this.previousStreamRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))\n );\n }\n\n /**\n * Returns the maxPayloadBitsPerSecond per Stream\n *\n * If MediaRequestManager kind is \"audio\", a constant bitrate will be returned.\n * If MediaRequestManager kind is \"video\", the bitrate will be calculated based\n * on maxFs (default h264 maxFs as fallback if maxFs is not defined)\n *\n * @param {MediaRequest} mediaRequest - mediaRequest to take data from\n * @returns {number} maxPayloadBitsPerSecond\n */\n private getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number {\n if (this.kind === 'audio') {\n // return mono_music bitrate default if the kind of mediarequest manager is audio:\n return RecommendedOpusBitrates.FB_MONO_MUSIC;\n }\n\n return getRecommendedMaxBitrateForFrameSize(\n mediaRequest.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs\n );\n }\n\n /**\n * Returns the max Macro Blocks per second (maxMbps) per H264 Stream\n *\n * The maxMbps will be calculated based on maxFs and maxFps\n * (default h264 maxFps as fallback if maxFps is not defined)\n *\n * @param {MediaRequest} mediaRequest - mediaRequest to take data from\n * @returns {number} maxMbps\n */\n // eslint-disable-next-line class-methods-use-this\n private getH264MaxMbps(mediaRequest: MediaRequest): number {\n // fallback for maxFps (not needed for maxFs, since there is a fallback already in getDegradedClientRequests)\n const maxFps = mediaRequest.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps;\n\n // divided by 100 since maxFps is 3000 (for 30 frames per seconds)\n return (mediaRequest.codecInfo.maxFs * maxFps) / 100;\n }\n\n /**\n * Clears the previous stream requests.\n *\n * @returns {void}\n */\n public clearPreviousRequests(): void {\n this.previousStreamRequests = [];\n }\n\n /** Modifies the passed in clientRequests and makes sure that in total they don't ask\n * for more streams than there are available.\n *\n * @param {Object} clientRequests\n * @returns {void}\n */\n private trimRequests(clientRequests: ClientRequestsMap) {\n const preferLiveVideo = this.getPreferLiveVideo();\n\n if (!this.trimRequestsToNumOfSources) {\n return;\n }\n\n // preferLiveVideo being undefined means that there are no active-speaker requests so we don't need to do any trimming\n if (preferLiveVideo === undefined) {\n return;\n }\n\n let numStreamsAvailable = preferLiveVideo ? this.numLiveSources : this.numTotalSources;\n\n Object.values(clientRequests)\n .sort((a, b) => {\n // we have to count how many streams we're asking for\n // and should not ask for more than numStreamsAvailable in total,\n // so we might need to trim active-speaker requests and first ones to trim should be\n // the ones with lowest priority\n\n // receiver-selected requests have priority over active-speakers\n if (a.policyInfo.policy === 'receiver-selected') {\n return -1;\n }\n if (b.policyInfo.policy === 'receiver-selected') {\n return 1;\n }\n\n // and active-speakers are sorted by descending priority\n return b.policyInfo.priority - a.policyInfo.priority;\n })\n .forEach((request) => {\n // we only trim active-speaker requests\n if (request.policyInfo.policy === 'active-speaker') {\n const trimmedCount = Math.min(numStreamsAvailable, request.receiveSlots.length);\n\n request.receiveSlots.length = trimmedCount;\n\n numStreamsAvailable -= trimmedCount;\n } else {\n numStreamsAvailable -= request.receiveSlots.length;\n }\n\n if (numStreamsAvailable < 0) {\n numStreamsAvailable = 0;\n }\n });\n }\n\n private getPreferLiveVideo(): boolean | undefined {\n let preferLiveVideo;\n\n Object.values(this.clientRequests).forEach((mr) => {\n if (mr.policyInfo.policy === 'active-speaker') {\n // take the value from first encountered active speaker request\n if (preferLiveVideo === undefined) {\n preferLiveVideo = mr.policyInfo.preferLiveVideo;\n }\n\n if (mr.policyInfo.preferLiveVideo !== preferLiveVideo) {\n throw new Error(\n 'a mix of active-speaker groups with different values for preferLiveVideo is not supported'\n );\n }\n }\n });\n\n return preferLiveVideo;\n }\n\n private cloneClientRequests(): ClientRequestsMap {\n // we clone the client requests but without cloning the ReceiveSlots that they reference\n return cloneDeepWith(this.clientRequests, (value, key) => {\n if (key === 'receiveSlots') {\n return [...value];\n }\n\n return undefined;\n });\n }\n\n private sendRequests() {\n const streamRequests: StreamRequest[] = [];\n\n // clone the requests so that any modifications we do to them don't affect the original ones\n const clientRequests = this.cloneClientRequests();\n\n this.trimRequests(clientRequests);\n this.getDegradedClientRequests(clientRequests);\n\n // map all the client media requests to wcme stream requests\n Object.values(clientRequests).forEach((mr) => {\n if (mr.receiveSlots.length > 0) {\n streamRequests.push(\n new StreamRequest(\n mr.policyInfo.policy === 'active-speaker'\n ? Policy.ActiveSpeaker\n : Policy.ReceiverSelected,\n mr.policyInfo.policy === 'active-speaker'\n ? new ActiveSpeakerInfo(\n mr.policyInfo.priority,\n mr.policyInfo.crossPriorityDuplication,\n mr.policyInfo.crossPolicyDuplication,\n mr.policyInfo.preferLiveVideo\n )\n : new ReceiverSelectedInfo(mr.policyInfo.csi),\n mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),\n this.getMaxPayloadBitsPerSecond(mr),\n mr.codecInfo && [\n new WcmeCodecInfo(\n 0x80,\n new H264Codec(\n mr.codecInfo.maxFs,\n mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,\n this.getH264MaxMbps(mr),\n mr.codecInfo.maxWidth,\n mr.codecInfo.maxHeight\n )\n ),\n ]\n )\n );\n }\n });\n\n //! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/json-multistream)\n // https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713\n if (!this.checkIsNewRequestsEqualToPrev(streamRequests)) {\n this.sendMediaRequestsCallback(streamRequests);\n this.previousStreamRequests = streamRequests;\n LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);\n } else {\n LoggerProxy.logger.info(\n `multistream:sendRequests --> detected duplicate WCME requests, skipping them... `\n );\n }\n }\n\n public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {\n // eslint-disable-next-line no-plusplus\n const newId = `${this.counter++}`;\n\n this.clientRequests[newId] = mediaRequest;\n\n const eventHandler = ({maxFs}) => {\n mediaRequest.preferredMaxFs = maxFs;\n this.debouncedSourceUpdateListener();\n };\n mediaRequest.handleMaxFs = eventHandler;\n\n mediaRequest.receiveSlots.forEach((rs) => {\n rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.on(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);\n });\n\n if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\n const mediaRequest = this.clientRequests[requestId];\n\n mediaRequest?.receiveSlots.forEach((rs) => {\n rs.off(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.off(ReceiveSlotEvents.MaxFsUpdate, mediaRequest.handleMaxFs);\n });\n\n delete this.clientRequests[requestId];\n\n if (commit) {\n this.commit();\n }\n }\n\n public commit() {\n return this.sendRequests();\n }\n\n public reset() {\n this.clientRequests = {};\n this.numTotalSources = 0;\n this.numLiveSources = 0;\n }\n\n public setNumCurrentSources(numTotalSources: number, numLiveSources: number) {\n this.numTotalSources = numTotalSources;\n this.numLiveSources = numLiveSources;\n\n this.sendRequests();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { NoiseReductionEffectOptions, VirtualBackgroundEffectOptions } from '@webex/media-helpers';
|
|
2
|
-
type INoiseReductionEffect = Omit<NoiseReductionEffectOptions, 'authToken'>;
|
|
2
|
+
type INoiseReductionEffect = Omit<NoiseReductionEffectOptions, 'authToken' | 'workletProcessorUrl' | 'legacyProcessorUrl'>;
|
|
3
3
|
type IVirtualBackgroundEffect = Omit<VirtualBackgroundEffectOptions, 'authToken'>;
|
|
4
4
|
export type { INoiseReductionEffect, IVirtualBackgroundEffect };
|
|
@@ -39,6 +39,7 @@ type Kind = 'audio' | 'video';
|
|
|
39
39
|
type Options = {
|
|
40
40
|
degradationPreferences: DegradationPreferences;
|
|
41
41
|
kind: Kind;
|
|
42
|
+
trimRequestsToNumOfSources: boolean;
|
|
42
43
|
};
|
|
43
44
|
export declare class MediaRequestManager {
|
|
44
45
|
private sendMediaRequestsCallback;
|
|
@@ -49,6 +50,9 @@ export declare class MediaRequestManager {
|
|
|
49
50
|
private sourceUpdateListener;
|
|
50
51
|
private debouncedSourceUpdateListener;
|
|
51
52
|
private previousStreamRequests;
|
|
53
|
+
private trimRequestsToNumOfSources;
|
|
54
|
+
private numTotalSources;
|
|
55
|
+
private numLiveSources;
|
|
52
56
|
constructor(sendMediaRequestsCallback: SendMediaRequestsCallback, options: Options);
|
|
53
57
|
setDegradationPreferences(degradationPreferences: DegradationPreferences): void;
|
|
54
58
|
private getDegradedClientRequests;
|
|
@@ -95,10 +99,20 @@ export declare class MediaRequestManager {
|
|
|
95
99
|
* @returns {void}
|
|
96
100
|
*/
|
|
97
101
|
clearPreviousRequests(): void;
|
|
102
|
+
/** Modifies the passed in clientRequests and makes sure that in total they don't ask
|
|
103
|
+
* for more streams than there are available.
|
|
104
|
+
*
|
|
105
|
+
* @param {Object} clientRequests
|
|
106
|
+
* @returns {void}
|
|
107
|
+
*/
|
|
108
|
+
private trimRequests;
|
|
109
|
+
private getPreferLiveVideo;
|
|
110
|
+
private cloneClientRequests;
|
|
98
111
|
private sendRequests;
|
|
99
112
|
addRequest(mediaRequest: MediaRequest, commit?: boolean): MediaRequestId;
|
|
100
113
|
cancelRequest(requestId: MediaRequestId, commit?: boolean): void;
|
|
101
114
|
commit(): void;
|
|
102
115
|
reset(): void;
|
|
116
|
+
setNumCurrentSources(numTotalSources: number, numLiveSources: number): void;
|
|
103
117
|
}
|
|
104
118
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.156",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"build": "yarn run -T tsc --declaration true --declarationDir ./dist/types"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@webex/plugin-meetings": "3.0.0-beta.
|
|
36
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
37
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
38
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
39
|
-
"@webex/test-helper-retry": "3.0.0-beta.
|
|
40
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
35
|
+
"@webex/plugin-meetings": "3.0.0-beta.156",
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-beta.156",
|
|
37
|
+
"@webex/test-helper-mocha": "3.0.0-beta.156",
|
|
38
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.156",
|
|
39
|
+
"@webex/test-helper-retry": "3.0.0-beta.156",
|
|
40
|
+
"@webex/test-helper-test-users": "3.0.0-beta.156",
|
|
41
41
|
"chai": "^4.3.4",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"jsdom-global": "3.0.2",
|
|
@@ -46,19 +46,19 @@
|
|
|
46
46
|
"typescript": "^4.7.4"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@webex/common": "3.0.0-beta.
|
|
50
|
-
"@webex/internal-media-core": "1.38.
|
|
51
|
-
"@webex/internal-plugin-conversation": "3.0.0-beta.
|
|
52
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
53
|
-
"@webex/internal-plugin-llm": "3.0.0-beta.
|
|
54
|
-
"@webex/internal-plugin-mercury": "3.0.0-beta.
|
|
55
|
-
"@webex/internal-plugin-metrics": "3.0.0-beta.
|
|
56
|
-
"@webex/internal-plugin-support": "3.0.0-beta.
|
|
57
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
58
|
-
"@webex/media-helpers": "3.0.0-beta.
|
|
59
|
-
"@webex/plugin-people": "3.0.0-beta.
|
|
60
|
-
"@webex/plugin-rooms": "3.0.0-beta.
|
|
61
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
49
|
+
"@webex/common": "3.0.0-beta.156",
|
|
50
|
+
"@webex/internal-media-core": "1.38.5",
|
|
51
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.156",
|
|
52
|
+
"@webex/internal-plugin-device": "3.0.0-beta.156",
|
|
53
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.156",
|
|
54
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.156",
|
|
55
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.156",
|
|
56
|
+
"@webex/internal-plugin-support": "3.0.0-beta.156",
|
|
57
|
+
"@webex/internal-plugin-user": "3.0.0-beta.156",
|
|
58
|
+
"@webex/media-helpers": "3.0.0-beta.156",
|
|
59
|
+
"@webex/plugin-people": "3.0.0-beta.156",
|
|
60
|
+
"@webex/plugin-rooms": "3.0.0-beta.156",
|
|
61
|
+
"@webex/webex-core": "3.0.0-beta.156",
|
|
62
62
|
"ampersand-collection": "^2.0.2",
|
|
63
63
|
"bowser": "^2.11.0",
|
|
64
64
|
"btoa": "^1.2.1",
|
|
@@ -46,7 +46,6 @@ const Breakout = WebexPlugin.extend({
|
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
|
-
|
|
50
49
|
/**
|
|
51
50
|
* initializer for the Breakout class
|
|
52
51
|
* @returns {void}
|
|
@@ -54,6 +53,7 @@ const Breakout = WebexPlugin.extend({
|
|
|
54
53
|
initialize() {
|
|
55
54
|
// @ts-ignore
|
|
56
55
|
this.breakoutRequest = new BreakoutRequest({webex: this.webex});
|
|
56
|
+
this.breakoutRosterLocus = null;
|
|
57
57
|
},
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -130,6 +130,20 @@ const Breakout = WebexPlugin.extend({
|
|
|
130
130
|
);
|
|
131
131
|
},
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* check sequence and determine whether to update the new roster or not
|
|
135
|
+
* @param {Object} locus Locus object
|
|
136
|
+
* @returns {Boolean}
|
|
137
|
+
*/
|
|
138
|
+
isNeedHandleRoster(locus: any) {
|
|
139
|
+
if (!this.breakoutRosterLocus?.sequence?.entries?.length || !locus?.sequence?.entries?.length) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
const prevSequence = this.breakoutRosterLocus.sequence.entries[0];
|
|
143
|
+
const currentSequence = locus.sequence.entries[0];
|
|
144
|
+
|
|
145
|
+
return currentSequence > prevSequence;
|
|
146
|
+
},
|
|
133
147
|
/**
|
|
134
148
|
* Parses the participants from the locus object
|
|
135
149
|
* @param {Object} locus Locus object
|
|
@@ -139,7 +153,10 @@ const Breakout = WebexPlugin.extend({
|
|
|
139
153
|
if (!this.members) {
|
|
140
154
|
this.initMembers();
|
|
141
155
|
}
|
|
142
|
-
|
|
156
|
+
if (!this.isNeedHandleRoster(locus)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.breakoutRosterLocus = locus;
|
|
143
160
|
this.members.locusParticipantsUpdate(locus);
|
|
144
161
|
},
|
|
145
162
|
|
package/src/meeting/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Errors,
|
|
8
8
|
ErrorType,
|
|
9
9
|
Event,
|
|
10
|
+
MediaContent,
|
|
10
11
|
MediaType,
|
|
11
12
|
RemoteTrackType,
|
|
12
13
|
} from '@webex/internal-media-core';
|
|
@@ -669,6 +670,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
669
670
|
// @ts-ignore - config coming from registerPlugin
|
|
670
671
|
degradationPreferences: this.config.degradationPreferences,
|
|
671
672
|
kind: 'audio',
|
|
673
|
+
trimRequestsToNumOfSources: false,
|
|
672
674
|
}
|
|
673
675
|
),
|
|
674
676
|
video: new MediaRequestManager(
|
|
@@ -689,6 +691,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
689
691
|
// @ts-ignore - config coming from registerPlugin
|
|
690
692
|
degradationPreferences: this.config.degradationPreferences,
|
|
691
693
|
kind: 'video',
|
|
694
|
+
trimRequestsToNumOfSources: true,
|
|
692
695
|
}
|
|
693
696
|
),
|
|
694
697
|
screenShareAudio: new MediaRequestManager(
|
|
@@ -709,6 +712,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
709
712
|
// @ts-ignore - config coming from registerPlugin
|
|
710
713
|
degradationPreferences: this.config.degradationPreferences,
|
|
711
714
|
kind: 'audio',
|
|
715
|
+
trimRequestsToNumOfSources: false,
|
|
712
716
|
}
|
|
713
717
|
),
|
|
714
718
|
screenShareVideo: new MediaRequestManager(
|
|
@@ -729,6 +733,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
729
733
|
// @ts-ignore - config coming from registerPlugin
|
|
730
734
|
degradationPreferences: this.config.degradationPreferences,
|
|
731
735
|
kind: 'video',
|
|
736
|
+
trimRequestsToNumOfSources: false,
|
|
732
737
|
}
|
|
733
738
|
),
|
|
734
739
|
};
|
|
@@ -4804,6 +4809,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4804
4809
|
mediaContent,
|
|
4805
4810
|
}
|
|
4806
4811
|
);
|
|
4812
|
+
|
|
4813
|
+
if (mediaContent === MediaContent.Main) {
|
|
4814
|
+
this.mediaRequestManagers.video.setNumCurrentSources(numTotalSources, numLiveSources);
|
|
4815
|
+
}
|
|
4807
4816
|
}
|
|
4808
4817
|
);
|
|
4809
4818
|
|
|
@@ -5117,10 +5126,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5117
5126
|
.then(() => {
|
|
5118
5127
|
this.setMercuryListener();
|
|
5119
5128
|
})
|
|
5120
|
-
.then(
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5129
|
+
.then(
|
|
5130
|
+
() =>
|
|
5131
|
+
getDevices()
|
|
5132
|
+
.then((devices) => {
|
|
5133
|
+
MeetingUtil.handleDeviceLogging(devices);
|
|
5134
|
+
})
|
|
5135
|
+
.catch(() => {}) // getDevices may fail if we don't have browser permissions, that's ok, we still can have a media connection
|
|
5124
5136
|
)
|
|
5125
5137
|
.then(() => {
|
|
5126
5138
|
this.handleMediaLogging(this.mediaProperties);
|
|
@@ -3,7 +3,10 @@ import type {
|
|
|
3
3
|
VirtualBackgroundEffectOptions,
|
|
4
4
|
} from '@webex/media-helpers';
|
|
5
5
|
|
|
6
|
-
type INoiseReductionEffect = Omit<
|
|
6
|
+
type INoiseReductionEffect = Omit<
|
|
7
|
+
NoiseReductionEffectOptions,
|
|
8
|
+
'authToken' | 'workletProcessorUrl' | 'legacyProcessorUrl'
|
|
9
|
+
>;
|
|
7
10
|
type IVirtualBackgroundEffect = Omit<VirtualBackgroundEffectOptions, 'authToken'>;
|
|
8
11
|
|
|
9
12
|
export type {INoiseReductionEffect, IVirtualBackgroundEffect};
|