@webex/plugin-meetings 3.0.0-beta.65 → 3.0.0-beta.66
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/multistream/mediaRequestManager.js +48 -6
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/types/multistream/mediaRequestManager.d.ts +17 -0
- package/package.json +18 -18
- package/src/multistream/mediaRequestManager.ts +45 -3
- package/test/unit/spec/multistream/mediaRequestManager.ts +55 -8
package/dist/breakouts/index.js
CHANGED
|
@@ -7,11 +7,13 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
7
7
|
});
|
|
8
8
|
exports.MediaRequestManager = void 0;
|
|
9
9
|
var _entries = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/entries"));
|
|
10
|
+
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
|
|
10
11
|
var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
|
|
11
12
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
|
|
12
13
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
|
|
13
14
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
|
|
14
15
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
16
|
+
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
|
|
15
17
|
var _debounce2 = _interopRequireDefault(require("lodash/debounce"));
|
|
16
18
|
var _cloneDeep2 = _interopRequireDefault(require("lodash/cloneDeep"));
|
|
17
19
|
var _internalMediaCore = require("@webex/internal-media-core");
|
|
@@ -37,6 +39,7 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
37
39
|
(0, _defineProperty2.default)(this, "degradationPreferences", void 0);
|
|
38
40
|
(0, _defineProperty2.default)(this, "sourceUpdateListener", void 0);
|
|
39
41
|
(0, _defineProperty2.default)(this, "debouncedSourceUpdateListener", void 0);
|
|
42
|
+
(0, _defineProperty2.default)(this, "previousWCMEMediaRequests", []);
|
|
40
43
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
41
44
|
this.counter = 0;
|
|
42
45
|
this.clientRequests = {};
|
|
@@ -88,6 +91,36 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
88
91
|
}
|
|
89
92
|
return clientRequests;
|
|
90
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Returns true if two media requests are the same, false otherwise.
|
|
97
|
+
*
|
|
98
|
+
* @param {WcmeMediaRequest} mediaRequestA - Media request A for comparison.
|
|
99
|
+
* @param {WcmeMediaRequest} mediaRequestB - Media request B for comparison.
|
|
100
|
+
* @returns {boolean} - Whether they are equal.
|
|
101
|
+
*/
|
|
102
|
+
// eslint-disable-next-line class-methods-use-this
|
|
103
|
+
}, {
|
|
104
|
+
key: "isEqual",
|
|
105
|
+
value: function isEqual(mediaRequestA, mediaRequestB) {
|
|
106
|
+
return (0, _stringify.default)(mediaRequestA._toJmpScrRequest()) === (0, _stringify.default)(mediaRequestB._toJmpScrRequest());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Compares new media requests to previous ones and determines
|
|
111
|
+
* if they are the same.
|
|
112
|
+
*
|
|
113
|
+
* @param {WcmeMediaRequest[]} newRequests - Array with new requests.
|
|
114
|
+
* @returns {boolean} - True if they are equal, false otherwise.
|
|
115
|
+
*/
|
|
116
|
+
}, {
|
|
117
|
+
key: "checkIsNewRequestsEqualToPrev",
|
|
118
|
+
value: function checkIsNewRequestsEqualToPrev(newRequests) {
|
|
119
|
+
var _this2 = this;
|
|
120
|
+
return !(0, _isEmpty2.default)(this.previousWCMEMediaRequests) && this.previousWCMEMediaRequests.length === newRequests.length && this.previousWCMEMediaRequests.every(function (req, idx) {
|
|
121
|
+
return _this2.isEqual(req, newRequests[idx]);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
91
124
|
}, {
|
|
92
125
|
key: "sendRequests",
|
|
93
126
|
value: function sendRequests() {
|
|
@@ -101,22 +134,31 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
101
134
|
return receiveSlot.wcmeReceiveSlot;
|
|
102
135
|
}), maxPayloadBitsPerSecond, mr.codecInfo && [new _internalMediaCore.CodecInfo(0x80, new _internalMediaCore.H264Codec(mr.codecInfo.maxFs, mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps, mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps, mr.codecInfo.maxWidth, mr.codecInfo.maxHeight))]));
|
|
103
136
|
});
|
|
104
|
-
|
|
137
|
+
|
|
138
|
+
//! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/jmp-multistream)
|
|
139
|
+
// https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713
|
|
140
|
+
if (!this.checkIsNewRequestsEqualToPrev(wcmeMediaRequests)) {
|
|
141
|
+
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
142
|
+
this.previousWCMEMediaRequests = wcmeMediaRequests;
|
|
143
|
+
_loggerProxy.default.logger.info("multistream:sendRequests --> media requests sent. ");
|
|
144
|
+
} else {
|
|
145
|
+
_loggerProxy.default.logger.info("multistream:sendRequests --> detected duplicate WCME requests, skipping them... ");
|
|
146
|
+
}
|
|
105
147
|
}
|
|
106
148
|
}, {
|
|
107
149
|
key: "addRequest",
|
|
108
150
|
value: function addRequest(mediaRequest) {
|
|
109
|
-
var
|
|
151
|
+
var _this3 = this;
|
|
110
152
|
var commit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
111
153
|
// eslint-disable-next-line no-plusplus
|
|
112
154
|
var newId = "".concat(this.counter++);
|
|
113
155
|
this.clientRequests[newId] = mediaRequest;
|
|
114
156
|
mediaRequest.receiveSlots.forEach(function (rs) {
|
|
115
|
-
rs.on(_receiveSlot.ReceiveSlotEvents.SourceUpdate,
|
|
157
|
+
rs.on(_receiveSlot.ReceiveSlotEvents.SourceUpdate, _this3.sourceUpdateListener);
|
|
116
158
|
rs.on(_receiveSlot.ReceiveSlotEvents.MaxFsUpdate, function (_ref3) {
|
|
117
159
|
var maxFs = _ref3.maxFs;
|
|
118
160
|
mediaRequest.preferredMaxFs = maxFs;
|
|
119
|
-
|
|
161
|
+
_this3.debouncedSourceUpdateListener();
|
|
120
162
|
});
|
|
121
163
|
});
|
|
122
164
|
if (commit) {
|
|
@@ -128,10 +170,10 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
128
170
|
key: "cancelRequest",
|
|
129
171
|
value: function cancelRequest(requestId) {
|
|
130
172
|
var _this$clientRequests$,
|
|
131
|
-
|
|
173
|
+
_this4 = this;
|
|
132
174
|
var commit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
133
175
|
(_this$clientRequests$ = this.clientRequests[requestId]) === null || _this$clientRequests$ === void 0 ? void 0 : _this$clientRequests$.receiveSlots.forEach(function (rs) {
|
|
134
|
-
rs.off(_receiveSlot.ReceiveSlotEvents.SourceUpdate,
|
|
176
|
+
rs.off(_receiveSlot.ReceiveSlotEvents.SourceUpdate, _this4.sourceUpdateListener);
|
|
135
177
|
});
|
|
136
178
|
delete this.clientRequests[requestId];
|
|
137
179
|
if (commit) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","DEBOUNCED_SOURCE_UPDATE_TIME","MediaRequestManager","degradationPreferences","sendMediaRequestsCallback","counter","clientRequests","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","wcmeMediaRequests","getDegradedClientRequests","maxPayloadBitsPerSecond","push","WcmeMediaRequest","policyInfo","policy","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","priority","crossPriorityDuplication","crossPolicyDuplication","preferLiveVideo","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","WcmeCodecInfo","H264Codec","maxWidth","maxHeight","mediaRequest","newId","on","ReceiveSlotEvents","SourceUpdate","MaxFsUpdate","requestId","off"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {\n MediaRequest as WcmeMediaRequest,\n Policy,\n ActiveSpeakerInfo,\n ReceiverSelectedInfo,\n CodecInfo as WcmeCodecInfo,\n H264Codec,\n} from '@webex/internal-media-core';\nimport {cloneDeep, debounce} from 'lodash';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport {ReceiveSlot, ReceiveSlotEvents, ReceiveSlotId} 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}\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 = (mediaRequests: WcmeMediaRequest[]) => void;\n\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\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 constructor(\n degradationPreferences: DegradationPreferences,\n sendMediaRequestsCallback: SendMediaRequestsCallback\n ) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.degradationPreferences = degradationPreferences;\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 private sendRequests() {\n const wcmeMediaRequests: WcmeMediaRequest[] = [];\n\n const clientRequests = this.getDegradedClientRequests();\n const maxPayloadBitsPerSecond = 10 * 1000 * 1000;\n\n // map all the client media requests to wcme media requests\n Object.values(clientRequests).forEach((mr) => {\n wcmeMediaRequests.push(\n new WcmeMediaRequest(\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 maxPayloadBitsPerSecond,\n mr.codecInfo && [\n new WcmeCodecInfo(\n 0x80,\n new H264Codec(\n mr.codecInfo.maxFs,\n mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,\n mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,\n mr.codecInfo.maxWidth,\n mr.codecInfo.maxHeight\n )\n ),\n ]\n )\n );\n });\n\n this.sendMediaRequestsCallback(wcmeMediaRequests);\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 mediaRequest.receiveSlots.forEach((rs) => {\n rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.on(ReceiveSlotEvents.MaxFsUpdate, ({maxFs}) => {\n mediaRequest.preferredMaxFs = maxFs;\n this.debouncedSourceUpdateListener();\n });\n });\n\n if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\n this.clientRequests[requestId]?.receiveSlots.forEach((rs) => {\n rs.off(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\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","degradationPreferences","sendMediaRequestsCallback","counter","clientRequests","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","mediaRequestA","mediaRequestB","_toJmpScrRequest","newRequests","previousWCMEMediaRequests","every","req","idx","isEqual","wcmeMediaRequests","getDegradedClientRequests","maxPayloadBitsPerSecond","push","WcmeMediaRequest","policyInfo","policy","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","priority","crossPriorityDuplication","crossPolicyDuplication","preferLiveVideo","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","WcmeCodecInfo","H264Codec","maxWidth","maxHeight","checkIsNewRequestsEqualToPrev","info","mediaRequest","newId","on","ReceiveSlotEvents","SourceUpdate","MaxFsUpdate","requestId","off"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {\n MediaRequest as WcmeMediaRequest,\n Policy,\n ActiveSpeakerInfo,\n ReceiverSelectedInfo,\n CodecInfo as WcmeCodecInfo,\n H264Codec,\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}\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 = (mediaRequests: WcmeMediaRequest[]) => void;\n\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\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 previousWCMEMediaRequests: Array<WcmeMediaRequest> = [];\n\n constructor(\n degradationPreferences: DegradationPreferences,\n sendMediaRequestsCallback: SendMediaRequestsCallback\n ) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.degradationPreferences = degradationPreferences;\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 media requests are the same, false otherwise.\n *\n * @param {WcmeMediaRequest} mediaRequestA - Media request A for comparison.\n * @param {WcmeMediaRequest} mediaRequestB - Media request B for comparison.\n * @returns {boolean} - Whether they are equal.\n */\n // eslint-disable-next-line class-methods-use-this\n public isEqual(mediaRequestA: WcmeMediaRequest, mediaRequestB: WcmeMediaRequest) {\n return (\n JSON.stringify(mediaRequestA._toJmpScrRequest()) ===\n JSON.stringify(mediaRequestB._toJmpScrRequest())\n );\n }\n\n /**\n * Compares new media requests to previous ones and determines\n * if they are the same.\n *\n * @param {WcmeMediaRequest[]} newRequests - Array with new requests.\n * @returns {boolean} - True if they are equal, false otherwise.\n */\n private checkIsNewRequestsEqualToPrev(newRequests: WcmeMediaRequest[]) {\n return (\n !isEmpty(this.previousWCMEMediaRequests) &&\n this.previousWCMEMediaRequests.length === newRequests.length &&\n this.previousWCMEMediaRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))\n );\n }\n\n private sendRequests() {\n const wcmeMediaRequests: WcmeMediaRequest[] = [];\n\n const clientRequests = this.getDegradedClientRequests();\n const maxPayloadBitsPerSecond = 10 * 1000 * 1000;\n\n // map all the client media requests to wcme media requests\n Object.values(clientRequests).forEach((mr) => {\n wcmeMediaRequests.push(\n new WcmeMediaRequest(\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 maxPayloadBitsPerSecond,\n mr.codecInfo && [\n new WcmeCodecInfo(\n 0x80,\n new H264Codec(\n mr.codecInfo.maxFs,\n mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,\n mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,\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/jmp-multistream)\n // https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713\n if (!this.checkIsNewRequestsEqualToPrev(wcmeMediaRequests)) {\n this.sendMediaRequestsCallback(wcmeMediaRequests);\n this.previousWCMEMediaRequests = wcmeMediaRequests;\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 mediaRequest.receiveSlots.forEach((rs) => {\n rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\n rs.on(ReceiveSlotEvents.MaxFsUpdate, ({maxFs}) => {\n mediaRequest.preferredMaxFs = maxFs;\n this.debouncedSourceUpdateListener();\n });\n });\n\n if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\n this.clientRequests[requestId]?.receiveSlots.forEach((rs) => {\n rs.off(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);\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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"}
|
|
@@ -39,9 +39,26 @@ export declare class MediaRequestManager {
|
|
|
39
39
|
private degradationPreferences;
|
|
40
40
|
private sourceUpdateListener;
|
|
41
41
|
private debouncedSourceUpdateListener;
|
|
42
|
+
private previousWCMEMediaRequests;
|
|
42
43
|
constructor(degradationPreferences: DegradationPreferences, sendMediaRequestsCallback: SendMediaRequestsCallback);
|
|
43
44
|
setDegradationPreferences(degradationPreferences: DegradationPreferences): void;
|
|
44
45
|
private getDegradedClientRequests;
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if two media requests are the same, false otherwise.
|
|
48
|
+
*
|
|
49
|
+
* @param {WcmeMediaRequest} mediaRequestA - Media request A for comparison.
|
|
50
|
+
* @param {WcmeMediaRequest} mediaRequestB - Media request B for comparison.
|
|
51
|
+
* @returns {boolean} - Whether they are equal.
|
|
52
|
+
*/
|
|
53
|
+
isEqual(mediaRequestA: WcmeMediaRequest, mediaRequestB: WcmeMediaRequest): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Compares new media requests to previous ones and determines
|
|
56
|
+
* if they are the same.
|
|
57
|
+
*
|
|
58
|
+
* @param {WcmeMediaRequest[]} newRequests - Array with new requests.
|
|
59
|
+
* @returns {boolean} - True if they are equal, false otherwise.
|
|
60
|
+
*/
|
|
61
|
+
private checkIsNewRequestsEqualToPrev;
|
|
45
62
|
private sendRequests;
|
|
46
63
|
addRequest(mediaRequest: MediaRequest, commit?: boolean): MediaRequestId;
|
|
47
64
|
cancelRequest(requestId: MediaRequestId, commit?: boolean): void;
|
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.66",
|
|
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.66",
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-beta.66",
|
|
37
|
+
"@webex/test-helper-mocha": "3.0.0-beta.66",
|
|
38
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.66",
|
|
39
|
+
"@webex/test-helper-retry": "3.0.0-beta.66",
|
|
40
|
+
"@webex/test-helper-test-users": "3.0.0-beta.66",
|
|
41
41
|
"chai": "^4.3.4",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"jsdom-global": "3.0.2",
|
|
@@ -46,18 +46,18 @@
|
|
|
46
46
|
"typescript": "^4.7.4"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@webex/common": "3.0.0-beta.
|
|
49
|
+
"@webex/common": "3.0.0-beta.66",
|
|
50
50
|
"@webex/internal-media-core": "1.35.7",
|
|
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/plugin-people": "3.0.0-beta.
|
|
59
|
-
"@webex/plugin-rooms": "3.0.0-beta.
|
|
60
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
51
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.66",
|
|
52
|
+
"@webex/internal-plugin-device": "3.0.0-beta.66",
|
|
53
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.66",
|
|
54
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.66",
|
|
55
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.66",
|
|
56
|
+
"@webex/internal-plugin-support": "3.0.0-beta.66",
|
|
57
|
+
"@webex/internal-plugin-user": "3.0.0-beta.66",
|
|
58
|
+
"@webex/plugin-people": "3.0.0-beta.66",
|
|
59
|
+
"@webex/plugin-rooms": "3.0.0-beta.66",
|
|
60
|
+
"@webex/webex-core": "3.0.0-beta.66",
|
|
61
61
|
"ampersand-collection": "^2.0.2",
|
|
62
62
|
"bowser": "^2.11.0",
|
|
63
63
|
"btoa": "^1.2.1",
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
CodecInfo as WcmeCodecInfo,
|
|
8
8
|
H264Codec,
|
|
9
9
|
} from '@webex/internal-media-core';
|
|
10
|
-
import {cloneDeep, debounce} from 'lodash';
|
|
10
|
+
import {cloneDeep, debounce, isEmpty} from 'lodash';
|
|
11
11
|
|
|
12
12
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
13
13
|
|
|
14
|
-
import {ReceiveSlot, ReceiveSlotEvents
|
|
14
|
+
import {ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';
|
|
15
15
|
import {getMaxFs} from './remoteMedia';
|
|
16
16
|
|
|
17
17
|
export interface ActiveSpeakerPolicyInfo {
|
|
@@ -78,6 +78,8 @@ export class MediaRequestManager {
|
|
|
78
78
|
|
|
79
79
|
private debouncedSourceUpdateListener: () => void;
|
|
80
80
|
|
|
81
|
+
private previousWCMEMediaRequests: Array<WcmeMediaRequest> = [];
|
|
82
|
+
|
|
81
83
|
constructor(
|
|
82
84
|
degradationPreferences: DegradationPreferences,
|
|
83
85
|
sendMediaRequestsCallback: SendMediaRequestsCallback
|
|
@@ -143,6 +145,36 @@ export class MediaRequestManager {
|
|
|
143
145
|
return clientRequests;
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Returns true if two media requests are the same, false otherwise.
|
|
150
|
+
*
|
|
151
|
+
* @param {WcmeMediaRequest} mediaRequestA - Media request A for comparison.
|
|
152
|
+
* @param {WcmeMediaRequest} mediaRequestB - Media request B for comparison.
|
|
153
|
+
* @returns {boolean} - Whether they are equal.
|
|
154
|
+
*/
|
|
155
|
+
// eslint-disable-next-line class-methods-use-this
|
|
156
|
+
public isEqual(mediaRequestA: WcmeMediaRequest, mediaRequestB: WcmeMediaRequest) {
|
|
157
|
+
return (
|
|
158
|
+
JSON.stringify(mediaRequestA._toJmpScrRequest()) ===
|
|
159
|
+
JSON.stringify(mediaRequestB._toJmpScrRequest())
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Compares new media requests to previous ones and determines
|
|
165
|
+
* if they are the same.
|
|
166
|
+
*
|
|
167
|
+
* @param {WcmeMediaRequest[]} newRequests - Array with new requests.
|
|
168
|
+
* @returns {boolean} - True if they are equal, false otherwise.
|
|
169
|
+
*/
|
|
170
|
+
private checkIsNewRequestsEqualToPrev(newRequests: WcmeMediaRequest[]) {
|
|
171
|
+
return (
|
|
172
|
+
!isEmpty(this.previousWCMEMediaRequests) &&
|
|
173
|
+
this.previousWCMEMediaRequests.length === newRequests.length &&
|
|
174
|
+
this.previousWCMEMediaRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
146
178
|
private sendRequests() {
|
|
147
179
|
const wcmeMediaRequests: WcmeMediaRequest[] = [];
|
|
148
180
|
|
|
@@ -182,7 +214,17 @@ export class MediaRequestManager {
|
|
|
182
214
|
);
|
|
183
215
|
});
|
|
184
216
|
|
|
185
|
-
this.
|
|
217
|
+
//! IMPORTANT: this is only a temporary fix. This will soon be done in the jmp layer (@webex/jmp-multistream)
|
|
218
|
+
// https://jira-eng-gpk2.cisco.com/jira/browse/WEBEX-326713
|
|
219
|
+
if (!this.checkIsNewRequestsEqualToPrev(wcmeMediaRequests)) {
|
|
220
|
+
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
221
|
+
this.previousWCMEMediaRequests = wcmeMediaRequests;
|
|
222
|
+
LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);
|
|
223
|
+
} else {
|
|
224
|
+
LoggerProxy.logger.info(
|
|
225
|
+
`multistream:sendRequests --> detected duplicate WCME requests, skipping them... `
|
|
226
|
+
);
|
|
227
|
+
}
|
|
186
228
|
}
|
|
187
229
|
|
|
188
230
|
public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
|
|
@@ -552,28 +552,28 @@ describe('MediaRequestManager', () => {
|
|
|
552
552
|
]);
|
|
553
553
|
});
|
|
554
554
|
|
|
555
|
-
it('clears all the requests on reset()', () => {
|
|
555
|
+
it('avoids sending duplicate requests and clears all the requests on reset()', () => {
|
|
556
556
|
// send some requests and commit them one by one
|
|
557
|
-
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], RECEIVER_SELECTED_MAX_FS,
|
|
558
|
-
addReceiverSelectedRequest(1501, fakeReceiveSlots[1], RECEIVER_SELECTED_MAX_FS,
|
|
557
|
+
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], RECEIVER_SELECTED_MAX_FS, false);
|
|
558
|
+
addReceiverSelectedRequest(1501, fakeReceiveSlots[1], RECEIVER_SELECTED_MAX_FS, false);
|
|
559
559
|
addActiveSpeakerRequest(
|
|
560
560
|
255,
|
|
561
561
|
[fakeReceiveSlots[2], fakeReceiveSlots[3], fakeReceiveSlots[4]],
|
|
562
562
|
ACTIVE_SPEAKER_MAX_FS,
|
|
563
|
-
|
|
563
|
+
false
|
|
564
564
|
);
|
|
565
565
|
addActiveSpeakerRequest(
|
|
566
566
|
254,
|
|
567
567
|
[fakeReceiveSlots[5], fakeReceiveSlots[6], fakeReceiveSlots[7]],
|
|
568
568
|
ACTIVE_SPEAKER_MAX_FS,
|
|
569
|
-
|
|
569
|
+
false
|
|
570
570
|
);
|
|
571
571
|
|
|
572
|
-
|
|
572
|
+
// nothing should be sent out as we didn't commit the requests
|
|
573
|
+
assert.notCalled(sendMediaRequestsCallback);
|
|
573
574
|
|
|
574
|
-
// check that when calling commit() all requests are re-sent again
|
|
575
|
-
mediaRequestManager.commit();
|
|
576
575
|
|
|
576
|
+
mediaRequestManager.commit();
|
|
577
577
|
checkMediaRequestsSent([
|
|
578
578
|
{
|
|
579
579
|
policy: 'receiver-selected',
|
|
@@ -601,6 +601,13 @@ describe('MediaRequestManager', () => {
|
|
|
601
601
|
},
|
|
602
602
|
]);
|
|
603
603
|
|
|
604
|
+
|
|
605
|
+
// check that when calling commit()
|
|
606
|
+
// all requests are not re-sent again (avoid duplicate requests)
|
|
607
|
+
mediaRequestManager.commit();
|
|
608
|
+
|
|
609
|
+
assert.notCalled(sendMediaRequestsCallback);
|
|
610
|
+
|
|
604
611
|
// now reset everything
|
|
605
612
|
mediaRequestManager.reset();
|
|
606
613
|
|
|
@@ -609,6 +616,46 @@ describe('MediaRequestManager', () => {
|
|
|
609
616
|
checkMediaRequestsSent([]);
|
|
610
617
|
});
|
|
611
618
|
|
|
619
|
+
it('makes sure to call requests correctly after reset was called and another request was added', () => {
|
|
620
|
+
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], RECEIVER_SELECTED_MAX_FS, false);
|
|
621
|
+
|
|
622
|
+
assert.notCalled(sendMediaRequestsCallback);
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
mediaRequestManager.commit();
|
|
626
|
+
checkMediaRequestsSent([
|
|
627
|
+
{
|
|
628
|
+
policy: 'receiver-selected',
|
|
629
|
+
csi: 1500,
|
|
630
|
+
receiveSlot: fakeWcmeSlots[0],
|
|
631
|
+
maxFs: RECEIVER_SELECTED_MAX_FS,
|
|
632
|
+
},
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
// now reset everything
|
|
636
|
+
mediaRequestManager.reset();
|
|
637
|
+
|
|
638
|
+
// calling commit now should not cause any requests to be sent out
|
|
639
|
+
mediaRequestManager.commit();
|
|
640
|
+
checkMediaRequestsSent([]);
|
|
641
|
+
|
|
642
|
+
//add new request
|
|
643
|
+
addReceiverSelectedRequest(1501, fakeReceiveSlots[1], RECEIVER_SELECTED_MAX_FS, false);
|
|
644
|
+
|
|
645
|
+
// commit
|
|
646
|
+
mediaRequestManager.commit();
|
|
647
|
+
|
|
648
|
+
// check the new request was sent
|
|
649
|
+
checkMediaRequestsSent([
|
|
650
|
+
{
|
|
651
|
+
policy: 'receiver-selected',
|
|
652
|
+
csi: 1501,
|
|
653
|
+
receiveSlot: fakeWcmeSlots[1],
|
|
654
|
+
maxFs: RECEIVER_SELECTED_MAX_FS,
|
|
655
|
+
},
|
|
656
|
+
])
|
|
657
|
+
})
|
|
658
|
+
|
|
612
659
|
it('re-sends media requests after degradation preferences are set', () => {
|
|
613
660
|
// set max macroblocks limit
|
|
614
661
|
mediaRequestManager.setDegradationPreferences({maxMacroblocksLimit: 32400});
|