@webex/plugin-meetings 3.0.0-beta.37 → 3.0.0-beta.39
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/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +11 -32
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +19 -16
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/remoteMedia.js +42 -18
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/multistream/mediaRequestManager.d.ts +2 -2
- package/dist/types/multistream/receiveSlot.d.ts +6 -8
- package/dist/types/multistream/remoteMedia.d.ts +7 -0
- package/package.json +19 -19
- package/src/index.js +2 -0
- package/src/multistream/mediaRequestManager.ts +14 -31
- package/src/multistream/receiveSlot.ts +20 -13
- package/src/multistream/remoteMedia.ts +23 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +38 -73
- package/test/unit/spec/multistream/receiveSlot.ts +19 -19
- package/test/unit/spec/multistream/remoteMedia.ts +28 -0
package/dist/breakouts/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -9,6 +9,12 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
9
9
|
value: true
|
|
10
10
|
});
|
|
11
11
|
exports.REACTIONS = exports.CONSTANTS = void 0;
|
|
12
|
+
_Object$defineProperty(exports, "RemoteMedia", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function get() {
|
|
15
|
+
return _remoteMedia.RemoteMedia;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
12
18
|
_Object$defineProperty(exports, "TriggerProxy", {
|
|
13
19
|
enumerable: true,
|
|
14
20
|
get: function get() {
|
|
@@ -23,6 +29,7 @@ var _CONSTANTS = _interopRequireWildcard(require("./constants"));
|
|
|
23
29
|
exports.CONSTANTS = _CONSTANTS;
|
|
24
30
|
var _REACTIONS = _interopRequireWildcard(require("./reactions/reactions"));
|
|
25
31
|
exports.REACTIONS = _REACTIONS;
|
|
32
|
+
var _remoteMedia = require("./multistream/remoteMedia");
|
|
26
33
|
var _triggerProxy = _interopRequireDefault(require("./common/events/trigger-proxy"));
|
|
27
34
|
function _getRequireWildcardCache(nodeInterop) { if (typeof _WeakMap !== "function") return null; var cacheBabelInterop = new _WeakMap(); var cacheNodeInterop = new _WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
28
35
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = _Object$defineProperty && _Object$getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? _Object$getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { _Object$defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["registerPlugin","Meetings","config"],"sources":["index.js"],"sourcesContent":["/* eslint-env browser */\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Meetings from './meetings';\nimport config from './config';\n\nregisterPlugin('meetings', Meetings, {\n config,\n});\n\nexport default Meetings;\n\nexport * as CONSTANTS from './constants';\nexport * as REACTIONS from './reactions/reactions';\n\nexport {default as TriggerProxy} from './common/events/trigger-proxy';\n"],"mappings":"
|
|
1
|
+
{"version":3,"names":["registerPlugin","Meetings","config"],"sources":["index.js"],"sourcesContent":["/* eslint-env browser */\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Meetings from './meetings';\nimport config from './config';\n\nregisterPlugin('meetings', Meetings, {\n config,\n});\n\nexport default Meetings;\n\nexport * as CONSTANTS from './constants';\nexport * as REACTIONS from './reactions/reactions';\n\nexport {RemoteMedia} from './multistream/remoteMedia';\n\nexport {default as TriggerProxy} from './common/events/trigger-proxy';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AACA;AAEA;AACA;AAA8B;AAAA;AAAA;AAAA;AAW9B;AAEA;AAAsE;AAAA;AAjBtE;;AAMA,IAAAA,yBAAc,EAAC,UAAU,EAAEC,iBAAQ,EAAE;EACnCC,MAAM,EAANA;AACF,CAAC,CAAC;AAAC,eAEYD,iBAAQ;AAAA"}
|
|
@@ -6,12 +6,13 @@ _Object$defineProperty(exports, "__esModule", {
|
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
8
|
exports.MediaRequestManager = void 0;
|
|
9
|
-
var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
|
|
10
9
|
var _entries = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/entries"));
|
|
10
|
+
var _values = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/values"));
|
|
11
11
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/slicedToArray"));
|
|
12
12
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
|
|
13
13
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
|
|
14
14
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
15
|
+
var _debounce2 = _interopRequireDefault(require("lodash/debounce"));
|
|
15
16
|
var _cloneDeep2 = _interopRequireDefault(require("lodash/cloneDeep"));
|
|
16
17
|
var _internalMediaCore = require("@webex/internal-media-core");
|
|
17
18
|
var _loggerProxy = _interopRequireDefault(require("../common/logs/logger-proxy"));
|
|
@@ -26,49 +27,24 @@ var CODEC_DEFAULTS = {
|
|
|
26
27
|
maxMbps: 245760
|
|
27
28
|
}
|
|
28
29
|
};
|
|
30
|
+
var DEBOUNCED_SOURCE_UPDATE_TIME = 1000;
|
|
29
31
|
var MediaRequestManager = /*#__PURE__*/function () {
|
|
30
32
|
function MediaRequestManager(degradationPreferences, sendMediaRequestsCallback) {
|
|
31
33
|
(0, _classCallCheck2.default)(this, MediaRequestManager);
|
|
32
34
|
(0, _defineProperty2.default)(this, "sendMediaRequestsCallback", void 0);
|
|
33
35
|
(0, _defineProperty2.default)(this, "counter", void 0);
|
|
34
36
|
(0, _defineProperty2.default)(this, "clientRequests", void 0);
|
|
35
|
-
(0, _defineProperty2.default)(this, "slotsActiveInLastMediaRequest", void 0);
|
|
36
37
|
(0, _defineProperty2.default)(this, "degradationPreferences", void 0);
|
|
37
38
|
(0, _defineProperty2.default)(this, "sourceUpdateListener", void 0);
|
|
39
|
+
(0, _defineProperty2.default)(this, "debouncedSourceUpdateListener", void 0);
|
|
38
40
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
39
41
|
this.counter = 0;
|
|
40
42
|
this.clientRequests = {};
|
|
41
|
-
this.slotsActiveInLastMediaRequest = {};
|
|
42
43
|
this.degradationPreferences = degradationPreferences;
|
|
43
44
|
this.sourceUpdateListener = this.commit.bind(this);
|
|
45
|
+
this.debouncedSourceUpdateListener = (0, _debounce2.default)(this.sourceUpdateListener, DEBOUNCED_SOURCE_UPDATE_TIME);
|
|
44
46
|
}
|
|
45
47
|
(0, _createClass2.default)(MediaRequestManager, [{
|
|
46
|
-
key: "resetInactiveReceiveSlots",
|
|
47
|
-
value: function resetInactiveReceiveSlots() {
|
|
48
|
-
var activeSlots = {};
|
|
49
|
-
|
|
50
|
-
// create a map of all currently used slot ids
|
|
51
|
-
(0, _values.default)(this.clientRequests).forEach(function (request) {
|
|
52
|
-
return request.receiveSlots.forEach(function (slot) {
|
|
53
|
-
activeSlots[slot.id] = slot;
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// when we stop using some receive slots and they are not included in the new media request,
|
|
58
|
-
// we will never get a 'no source' notification for them, so we reset their state,
|
|
59
|
-
// so that the client doesn't try to display their video anymore
|
|
60
|
-
for (var _i = 0, _Object$entries = (0, _entries.default)(this.slotsActiveInLastMediaRequest); _i < _Object$entries.length; _i++) {
|
|
61
|
-
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
|
|
62
|
-
slotId = _Object$entries$_i[0],
|
|
63
|
-
slot = _Object$entries$_i[1];
|
|
64
|
-
if (!(slotId in activeSlots)) {
|
|
65
|
-
_loggerProxy.default.logger.info("multistream:mediaRequestManager --> resetting sourceState to \"no source\" for slot ".concat(slot.id));
|
|
66
|
-
slot.resetSourceState();
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
this.slotsActiveInLastMediaRequest = activeSlots;
|
|
70
|
-
}
|
|
71
|
-
}, {
|
|
72
48
|
key: "setDegradationPreferences",
|
|
73
49
|
value: function setDegradationPreferences(degradationPreferences) {
|
|
74
50
|
this.degradationPreferences = degradationPreferences;
|
|
@@ -89,7 +65,7 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
89
65
|
id = _ref2[0],
|
|
90
66
|
mr = _ref2[1];
|
|
91
67
|
if (mr.codecInfo) {
|
|
92
|
-
mr.codecInfo.maxFs = Math.min(mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs, maxFsLimits[i]);
|
|
68
|
+
mr.codecInfo.maxFs = Math.min(mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs, mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs, maxFsLimits[i]);
|
|
93
69
|
// we only consider sources with "live" state
|
|
94
70
|
var slotsWithLiveSource = _this.clientRequests[id].receiveSlots.filter(function (rs) {
|
|
95
71
|
return rs.sourceState === 'live';
|
|
@@ -126,7 +102,6 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
126
102
|
}), 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))]));
|
|
127
103
|
});
|
|
128
104
|
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
129
|
-
this.resetInactiveReceiveSlots();
|
|
130
105
|
}
|
|
131
106
|
}, {
|
|
132
107
|
key: "addRequest",
|
|
@@ -138,6 +113,11 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
138
113
|
this.clientRequests[newId] = mediaRequest;
|
|
139
114
|
mediaRequest.receiveSlots.forEach(function (rs) {
|
|
140
115
|
rs.on(_receiveSlot.ReceiveSlotEvents.SourceUpdate, _this2.sourceUpdateListener);
|
|
116
|
+
rs.on(_receiveSlot.ReceiveSlotEvents.MaxFsUpdate, function (_ref3) {
|
|
117
|
+
var maxFs = _ref3.maxFs;
|
|
118
|
+
mediaRequest.preferredMaxFs = maxFs;
|
|
119
|
+
_this2.debouncedSourceUpdateListener();
|
|
120
|
+
});
|
|
141
121
|
});
|
|
142
122
|
if (commit) {
|
|
143
123
|
this.commit();
|
|
@@ -167,7 +147,6 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
167
147
|
key: "reset",
|
|
168
148
|
value: function reset() {
|
|
169
149
|
this.clientRequests = {};
|
|
170
|
-
this.slotsActiveInLastMediaRequest = {};
|
|
171
150
|
}
|
|
172
151
|
}]);
|
|
173
152
|
return MediaRequestManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","MediaRequestManager","degradationPreferences","sendMediaRequestsCallback","counter","clientRequests","slotsActiveInLastMediaRequest","sourceUpdateListener","commit","bind","activeSlots","forEach","request","receiveSlots","slot","id","slotId","LoggerProxy","logger","info","resetSourceState","sendRequests","maxFsLimits","getMaxFs","totalMacroblocksRequested","mr","codecInfo","Math","min","i","slotsWithLiveSource","filter","rs","sourceState","length","maxMacroblocksLimit","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","resetInactiveReceiveSlots","mediaRequest","newId","on","ReceiveSlotEvents","SourceUpdate","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} 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}\n\nexport type MediaRequestId = string;\n\nconst CODEC_DEFAULTS = {\n h264: {\n maxFs: 8192,\n maxFps: 3000,\n maxMbps: 245760,\n },\n};\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 slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};\n\n private degradationPreferences: DegradationPreferences;\n\n private sourceUpdateListener: () => void;\n\n constructor(\n degradationPreferences: DegradationPreferences,\n sendMediaRequestsCallback: SendMediaRequestsCallback\n ) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.slotsActiveInLastMediaRequest = {};\n this.degradationPreferences = degradationPreferences;\n this.sourceUpdateListener = this.commit.bind(this);\n }\n\n private resetInactiveReceiveSlots() {\n const activeSlots: {[key: ReceiveSlotId]: ReceiveSlot} = {};\n\n // create a map of all currently used slot ids\n Object.values(this.clientRequests).forEach((request) =>\n request.receiveSlots.forEach((slot) => {\n activeSlots[slot.id] = slot;\n })\n );\n\n // when we stop using some receive slots and they are not included in the new media request,\n // we will never get a 'no source' notification for them, so we reset their state,\n // so that the client doesn't try to display their video anymore\n for (const [slotId, slot] of Object.entries(this.slotsActiveInLastMediaRequest)) {\n if (!(slotId in activeSlots)) {\n LoggerProxy.logger.info(\n `multistream:mediaRequestManager --> resetting sourceState to \"no source\" for slot ${slot.id}`\n );\n slot.resetSourceState();\n }\n }\n\n this.slotsActiveInLastMediaRequest = activeSlots;\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.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 this.resetInactiveReceiveSlots();\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 });\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 this.slotsActiveInLastMediaRequest = {};\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","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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"}
|
|
@@ -25,7 +25,8 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !_R
|
|
|
25
25
|
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
26
26
|
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
27
27
|
var ReceiveSlotEvents = {
|
|
28
|
-
SourceUpdate: 'sourceUpdate'
|
|
28
|
+
SourceUpdate: 'sourceUpdate',
|
|
29
|
+
MaxFsUpdate: 'maxFsUpdate'
|
|
29
30
|
};
|
|
30
31
|
exports.ReceiveSlotEvents = ReceiveSlotEvents;
|
|
31
32
|
var receiveSlotCounter = 0;
|
|
@@ -95,6 +96,23 @@ var ReceiveSlot = /*#__PURE__*/function (_EventsScope) {
|
|
|
95
96
|
return (0, _classPrivateFieldGet2.default)(this, _csi);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Set the max frame size for this slot
|
|
101
|
+
* @param newFs frame size
|
|
102
|
+
*/
|
|
103
|
+
}, {
|
|
104
|
+
key: "setMaxFs",
|
|
105
|
+
value: function setMaxFs(newFs) {
|
|
106
|
+
// emit event for media request manager to listen to
|
|
107
|
+
|
|
108
|
+
this.emit({
|
|
109
|
+
file: 'meeting/receiveSlot',
|
|
110
|
+
function: 'findMemberId'
|
|
111
|
+
}, ReceiveSlotEvents.MaxFsUpdate, {
|
|
112
|
+
maxFs: newFs
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
98
116
|
/**
|
|
99
117
|
* Getter for sourceState
|
|
100
118
|
*/
|
|
@@ -167,21 +185,6 @@ var ReceiveSlot = /*#__PURE__*/function (_EventsScope) {
|
|
|
167
185
|
get: function get() {
|
|
168
186
|
return this.mcReceiveSlot;
|
|
169
187
|
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Resets the source state to the default 'no source' value.
|
|
173
|
-
* This function should be called on receive slots that are
|
|
174
|
-
* no longer part of a media request. It's needed because WCME
|
|
175
|
-
* does not send any more events on such slots, so the sourceState
|
|
176
|
-
* value would not represent the truth anymore.
|
|
177
|
-
*/
|
|
178
|
-
}, {
|
|
179
|
-
key: "resetSourceState",
|
|
180
|
-
value: function resetSourceState() {
|
|
181
|
-
(0, _classPrivateFieldSet2.default)(this, _sourceState, 'no source');
|
|
182
|
-
(0, _classPrivateFieldSet2.default)(this, _csi, undefined);
|
|
183
|
-
(0, _classPrivateFieldSet2.default)(this, _memberId, undefined);
|
|
184
|
-
}
|
|
185
188
|
}]);
|
|
186
189
|
return ReceiveSlot;
|
|
187
190
|
}(_eventsScope.default);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ReceiveSlotEvents","SourceUpdate","receiveSlotCounter","ReceiveSlot","mediaType","mcReceiveSlot","findMemberIdCallback","id","setupEventListeners","
|
|
1
|
+
{"version":3,"names":["ReceiveSlotEvents","SourceUpdate","MaxFsUpdate","receiveSlotCounter","ReceiveSlot","mediaType","mcReceiveSlot","findMemberIdCallback","id","setupEventListeners","newFs","emit","file","function","maxFs","scope","on","WcmeReceiveSlotEvents","state","csi","LoggerProxy","logger","log","undefined","memberId","stream","EventsScope"],"sources":["receiveSlot.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {\n MediaType,\n ReceiveSlot as WcmeReceiveSlot,\n ReceiveSlotEvents as WcmeReceiveSlotEvents,\n SourceState,\n} from '@webex/internal-media-core';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport EventsScope from '../common/events/events-scope';\n\nexport const ReceiveSlotEvents = {\n SourceUpdate: 'sourceUpdate',\n MaxFsUpdate: 'maxFsUpdate',\n};\n\nexport type {SourceState} from '@webex/internal-media-core';\nexport type CSI = number;\nexport type MemberId = string;\nexport type ReceiveSlotId = string;\n\nlet receiveSlotCounter = 0;\n\nexport type FindMemberIdCallback = (csi: CSI) => MemberId | undefined;\n\n/**\n * Class representing a receive slot. A single receive slot is able to receive a single track\n * for example some participant's main video or audio\n */\nexport class ReceiveSlot extends EventsScope {\n private readonly mcReceiveSlot: WcmeReceiveSlot;\n\n private readonly findMemberIdCallback: FindMemberIdCallback;\n\n public readonly id: ReceiveSlotId;\n\n public readonly mediaType: MediaType;\n\n #memberId?: MemberId;\n\n #csi?: CSI;\n\n #sourceState: SourceState;\n\n /**\n * constructor - don't use it directly, you should always use meeting.receiveSlotManager.allocateSlot()\n * to create any receive slots\n *\n * @param {MediaType} mediaType\n * @param {ReceiveSlot} mcReceiveSlot\n * @param {FindMemberIdCallback} findMemberIdCallback callback for finding memberId for given CSI\n */\n constructor(\n mediaType: MediaType,\n mcReceiveSlot: WcmeReceiveSlot,\n findMemberIdCallback: FindMemberIdCallback\n ) {\n super();\n\n receiveSlotCounter += 1;\n\n this.findMemberIdCallback = findMemberIdCallback;\n this.mediaType = mediaType;\n this.mcReceiveSlot = mcReceiveSlot;\n this.#sourceState = 'no source';\n this.id = `r${receiveSlotCounter}`;\n\n this.setupEventListeners();\n }\n\n /**\n * Getter for memberId\n */\n public get memberId() {\n return this.#memberId;\n }\n\n /**\n * Getter for csi\n */\n public get csi() {\n return this.#csi;\n }\n\n /**\n * Set the max frame size for this slot\n * @param newFs frame size\n */\n public setMaxFs(newFs) {\n // emit event for media request manager to listen to\n\n this.emit(\n {\n file: 'meeting/receiveSlot',\n function: 'findMemberId',\n },\n ReceiveSlotEvents.MaxFsUpdate,\n {\n maxFs: newFs,\n }\n );\n }\n\n /**\n * Getter for sourceState\n */\n public get sourceState() {\n return this.#sourceState;\n }\n\n /**\n * registers event handlers with the underlying ReceiveSlot\n */\n private setupEventListeners() {\n const scope = {\n file: 'meeting/receiveSlot',\n function: 'setupEventListeners',\n };\n\n this.mcReceiveSlot.on(\n WcmeReceiveSlotEvents.SourceUpdate,\n (state: SourceState, csi?: number) => {\n LoggerProxy.logger.log(\n `ReceiveSlot#setupEventListeners --> got source update on receive slot ${this.id}, mediaType=${this.mediaType}, csi=${csi}, state=${state}`\n );\n this.#memberId = csi ? this.findMemberIdCallback(csi) : undefined;\n this.#csi = csi;\n this.#sourceState = state;\n\n this.emit(scope, ReceiveSlotEvents.SourceUpdate, {\n state: this.#sourceState,\n csi: this.#csi,\n memberId: this.#memberId,\n });\n }\n );\n }\n\n /** Tries to find the member id for this receive slot if it hasn't got one */\n public findMemberId() {\n if (this.#memberId === undefined && this.#csi) {\n this.#memberId = this.findMemberIdCallback(this.#csi);\n\n if (this.#memberId) {\n // if we found the memberId, simulate source update so that the client app knows that something's changed\n this.emit(\n {\n file: 'meeting/receiveSlot',\n function: 'findMemberId',\n },\n ReceiveSlotEvents.SourceUpdate,\n {\n state: this.#sourceState,\n csi: this.#csi,\n memberId: this.#memberId,\n }\n );\n }\n }\n }\n\n /**\n * The MediaStream object associated with this slot.\n *\n * @returns {MediaStream} The MediaStreamTrack.\n */\n get stream(): MediaStream {\n return this.mcReceiveSlot.stream;\n }\n\n /**\n * The underlying WCME receive slot\n */\n get wcmeReceiveSlot(): WcmeReceiveSlot {\n return this.mcReceiveSlot;\n }\n}\n"],"mappings":"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"}
|
|
@@ -96,20 +96,44 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @param {boolean} commit - whether to commit the cancellation of the media request
|
|
104
|
-
* @internal
|
|
99
|
+
* Supply the width and height of the video element
|
|
100
|
+
* to restrict the requested resolution to this size
|
|
101
|
+
* @param width width of the video element
|
|
102
|
+
* @param height height of the video element
|
|
105
103
|
*/
|
|
106
104
|
(0, _createClass2.default)(RemoteMedia, [{
|
|
105
|
+
key: "setSizeHint",
|
|
106
|
+
value: function setSizeHint(width, height) {
|
|
107
|
+
var _this$receiveSlot;
|
|
108
|
+
// only base on height for now
|
|
109
|
+
var fs;
|
|
110
|
+
if (height < 135) {
|
|
111
|
+
fs = 60;
|
|
112
|
+
} else if (height < 270) {
|
|
113
|
+
fs = 240;
|
|
114
|
+
} else if (height < 540) {
|
|
115
|
+
fs = 920;
|
|
116
|
+
} else {
|
|
117
|
+
fs = 3600;
|
|
118
|
+
}
|
|
119
|
+
(_this$receiveSlot = this.receiveSlot) === null || _this$receiveSlot === void 0 ? void 0 : _this$receiveSlot.setMaxFs(fs);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Invalidates the remote media by clearing the reference to a receive slot and
|
|
124
|
+
* cancelling the media request.
|
|
125
|
+
* After this call the remote media is unusable.
|
|
126
|
+
*
|
|
127
|
+
* @param {boolean} commit - whether to commit the cancellation of the media request
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
}, {
|
|
107
131
|
key: "stop",
|
|
108
132
|
value: function stop() {
|
|
109
|
-
var _this$
|
|
133
|
+
var _this$receiveSlot2;
|
|
110
134
|
var commit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
111
135
|
this.cancelMediaRequest(commit);
|
|
112
|
-
(_this$
|
|
136
|
+
(_this$receiveSlot2 = this.receiveSlot) === null || _this$receiveSlot2 === void 0 ? void 0 : _this$receiveSlot2.removeAllListeners();
|
|
113
137
|
this.receiveSlot = undefined;
|
|
114
138
|
this.emit({
|
|
115
139
|
file: 'multistream/remoteMedia',
|
|
@@ -184,8 +208,8 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
184
208
|
}, {
|
|
185
209
|
key: "mediaType",
|
|
186
210
|
get: function get() {
|
|
187
|
-
var _this$
|
|
188
|
-
return (_this$
|
|
211
|
+
var _this$receiveSlot3;
|
|
212
|
+
return (_this$receiveSlot3 = this.receiveSlot) === null || _this$receiveSlot3 === void 0 ? void 0 : _this$receiveSlot3.mediaType;
|
|
189
213
|
}
|
|
190
214
|
|
|
191
215
|
/**
|
|
@@ -194,8 +218,8 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
194
218
|
}, {
|
|
195
219
|
key: "memberId",
|
|
196
220
|
get: function get() {
|
|
197
|
-
var _this$
|
|
198
|
-
return (_this$
|
|
221
|
+
var _this$receiveSlot4;
|
|
222
|
+
return (_this$receiveSlot4 = this.receiveSlot) === null || _this$receiveSlot4 === void 0 ? void 0 : _this$receiveSlot4.memberId;
|
|
199
223
|
}
|
|
200
224
|
|
|
201
225
|
/**
|
|
@@ -204,8 +228,8 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
204
228
|
}, {
|
|
205
229
|
key: "csi",
|
|
206
230
|
get: function get() {
|
|
207
|
-
var _this$
|
|
208
|
-
return (_this$
|
|
231
|
+
var _this$receiveSlot5;
|
|
232
|
+
return (_this$receiveSlot5 = this.receiveSlot) === null || _this$receiveSlot5 === void 0 ? void 0 : _this$receiveSlot5.csi;
|
|
209
233
|
}
|
|
210
234
|
|
|
211
235
|
/**
|
|
@@ -214,8 +238,8 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
214
238
|
}, {
|
|
215
239
|
key: "sourceState",
|
|
216
240
|
get: function get() {
|
|
217
|
-
var _this$
|
|
218
|
-
return (_this$
|
|
241
|
+
var _this$receiveSlot6;
|
|
242
|
+
return (_this$receiveSlot6 = this.receiveSlot) === null || _this$receiveSlot6 === void 0 ? void 0 : _this$receiveSlot6.sourceState;
|
|
219
243
|
}
|
|
220
244
|
|
|
221
245
|
/**
|
|
@@ -224,8 +248,8 @@ var RemoteMedia = /*#__PURE__*/function (_EventsScope) {
|
|
|
224
248
|
}, {
|
|
225
249
|
key: "stream",
|
|
226
250
|
get: function get() {
|
|
227
|
-
var _this$
|
|
228
|
-
return (_this$
|
|
251
|
+
var _this$receiveSlot7;
|
|
252
|
+
return (_this$receiveSlot7 = this.receiveSlot) === null || _this$receiveSlot7 === void 0 ? void 0 : _this$receiveSlot7.stream;
|
|
229
253
|
}
|
|
230
254
|
|
|
231
255
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["RemoteMediaEvents","SourceUpdate","ReceiveSlotEvents","Stopped","getMaxFs","paneSize","maxFs","LoggerProxy","logger","warn","remoteMediaCounter","RemoteMedia","receiveSlot","mediaRequestManager","options","setupEventListeners","id","commit","cancelMediaRequest","removeAllListeners","undefined","emit","file","function","csi","mediaRequestId","Error","addRequest","policyInfo","policy","receiveSlots","codecInfo","resolution","codec","cancelRequest","scope","on","data","mediaType","memberId","sourceState","stream","EventsScope"],"sources":["remoteMedia.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {MediaType, SourceState} from '@webex/internal-media-core';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport EventsScope from '../common/events/events-scope';\n\nimport {MediaRequestId, MediaRequestManager} from './mediaRequestManager';\nimport {CSI, ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\n\nexport const RemoteMediaEvents = {\n SourceUpdate: ReceiveSlotEvents.SourceUpdate,\n Stopped: 'stopped',\n};\n\nexport type RemoteVideoResolution =\n | 'thumbnail' // the smallest possible resolution, 90p or less\n | 'very small' // 180p or less\n | 'small' // 360p or less\n | 'medium' // 720p or less\n | 'large' // 1080p or less\n | 'best'; // highest possible resolution\n\n/**\n * Converts pane size into h264 maxFs\n * @param {PaneSize} paneSize\n * @returns {number}\n */\nexport function getMaxFs(paneSize: RemoteVideoResolution): number {\n let maxFs;\n\n switch (paneSize) {\n case 'thumbnail':\n maxFs = 60;\n break;\n case 'very small':\n maxFs = 240;\n break;\n case 'small':\n maxFs = 920;\n break;\n case 'medium':\n maxFs = 3600;\n break;\n case 'large':\n maxFs = 8192;\n break;\n case 'best':\n maxFs = 8192; // for now 'best' is 1080p, so same as 'large'\n break;\n default:\n LoggerProxy.logger.warn(\n `RemoteMedia#getMaxFs --> unsupported paneSize: ${paneSize}, using \"medium\" instead`\n );\n maxFs = 3600;\n }\n\n return maxFs;\n}\n\ntype Options = {\n resolution?: RemoteVideoResolution; // applies only to groups of type MediaType.VideoMain and MediaType.VideoSlides\n};\n\nexport type RemoteMediaId = string;\n\nlet remoteMediaCounter = 0;\n\n/**\n * Class representing a remote audio/video stream.\n *\n * Internally it is associated with a specific receive slot\n * and a media request for it.\n */\nexport class RemoteMedia extends EventsScope {\n private receiveSlot?: ReceiveSlot;\n\n private readonly mediaRequestManager: MediaRequestManager;\n\n private readonly options: Options;\n\n private mediaRequestId?: MediaRequestId;\n\n public readonly id: RemoteMediaId;\n\n /**\n * Constructs RemoteMedia instance\n *\n * @param receiveSlot\n * @param mediaRequestManager\n * @param options\n */\n constructor(\n receiveSlot: ReceiveSlot,\n mediaRequestManager: MediaRequestManager,\n options?: Options\n ) {\n super();\n remoteMediaCounter += 1;\n this.receiveSlot = receiveSlot;\n this.mediaRequestManager = mediaRequestManager;\n this.options = options || {};\n this.setupEventListeners();\n this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;\n }\n\n /**\n * Invalidates the remote media by clearing the reference to a receive slot and\n * cancelling the media request.\n * After this call the remote media is unusable.\n *\n * @param {boolean} commit - whether to commit the cancellation of the media request\n * @internal\n */\n public stop(commit = true) {\n this.cancelMediaRequest(commit);\n this.receiveSlot?.removeAllListeners();\n this.receiveSlot = undefined;\n this.emit(\n {\n file: 'multistream/remoteMedia',\n function: 'stop',\n },\n RemoteMediaEvents.Stopped,\n {}\n );\n }\n\n /**\n * Sends a new media request. This method can only be used for receiver-selected policy,\n * because only in that policy we have a 1-1 relationship between RemoteMedia and MediaRequest\n * and the request id is then stored in this RemoteMedia instance.\n * For active-speaker policy, the same request is shared among many RemoteMedia instances,\n * so it's managed through RemoteMediaGroup\n *\n * @internal\n */\n public sendMediaRequest(csi: CSI, commit: boolean) {\n if (this.mediaRequestId) {\n this.cancelMediaRequest(false);\n }\n\n if (!this.receiveSlot) {\n throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');\n }\n\n this.mediaRequestId = this.mediaRequestManager.addRequest(\n {\n policyInfo: {\n policy: 'receiver-selected',\n csi,\n },\n receiveSlots: [this.receiveSlot],\n codecInfo: this.options.resolution && {\n codec: 'h264',\n maxFs: getMaxFs(this.options.resolution),\n },\n },\n commit\n );\n }\n\n /**\n * @internal\n */\n public cancelMediaRequest(commit: boolean) {\n if (this.mediaRequestId) {\n this.mediaRequestManager.cancelRequest(this.mediaRequestId, commit);\n this.mediaRequestId = undefined;\n }\n }\n\n /**\n * registers event listeners on the receive slot and forwards all the events\n */\n private setupEventListeners() {\n if (this.receiveSlot) {\n const scope = {\n file: 'multistream/remoteMedia',\n function: 'setupEventListeners',\n };\n\n this.receiveSlot.on(ReceiveSlotEvents.SourceUpdate, (data) => {\n this.emit(scope, RemoteMediaEvents.SourceUpdate, data);\n });\n }\n }\n\n /**\n * Getter for mediaType\n */\n public get mediaType(): MediaType {\n return this.receiveSlot?.mediaType;\n }\n\n /**\n * Getter for memberId\n */\n public get memberId() {\n return this.receiveSlot?.memberId;\n }\n\n /**\n * Getter for csi\n */\n public get csi() {\n return this.receiveSlot?.csi;\n }\n\n /**\n * Getter for source state\n */\n public get sourceState(): SourceState {\n return this.receiveSlot?.sourceState;\n }\n\n /**\n * Getter for remote media stream\n */\n public get stream() {\n return this.receiveSlot?.stream;\n }\n\n /**\n * @internal\n * @returns {ReceiveSlot}\n */\n public getUnderlyingReceiveSlot() {\n return this.receiveSlot;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["RemoteMediaEvents","SourceUpdate","ReceiveSlotEvents","Stopped","getMaxFs","paneSize","maxFs","LoggerProxy","logger","warn","remoteMediaCounter","RemoteMedia","receiveSlot","mediaRequestManager","options","setupEventListeners","id","width","height","fs","setMaxFs","commit","cancelMediaRequest","removeAllListeners","undefined","emit","file","function","csi","mediaRequestId","Error","addRequest","policyInfo","policy","receiveSlots","codecInfo","resolution","codec","cancelRequest","scope","on","data","mediaType","memberId","sourceState","stream","EventsScope"],"sources":["remoteMedia.ts"],"sourcesContent":["/* eslint-disable valid-jsdoc */\nimport {MediaType, SourceState} from '@webex/internal-media-core';\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport EventsScope from '../common/events/events-scope';\n\nimport {MediaRequestId, MediaRequestManager} from './mediaRequestManager';\nimport {CSI, ReceiveSlot, ReceiveSlotEvents} from './receiveSlot';\n\nexport const RemoteMediaEvents = {\n SourceUpdate: ReceiveSlotEvents.SourceUpdate,\n Stopped: 'stopped',\n};\n\nexport type RemoteVideoResolution =\n | 'thumbnail' // the smallest possible resolution, 90p or less\n | 'very small' // 180p or less\n | 'small' // 360p or less\n | 'medium' // 720p or less\n | 'large' // 1080p or less\n | 'best'; // highest possible resolution\n\n/**\n * Converts pane size into h264 maxFs\n * @param {PaneSize} paneSize\n * @returns {number}\n */\nexport function getMaxFs(paneSize: RemoteVideoResolution): number {\n let maxFs;\n\n switch (paneSize) {\n case 'thumbnail':\n maxFs = 60;\n break;\n case 'very small':\n maxFs = 240;\n break;\n case 'small':\n maxFs = 920;\n break;\n case 'medium':\n maxFs = 3600;\n break;\n case 'large':\n maxFs = 8192;\n break;\n case 'best':\n maxFs = 8192; // for now 'best' is 1080p, so same as 'large'\n break;\n default:\n LoggerProxy.logger.warn(\n `RemoteMedia#getMaxFs --> unsupported paneSize: ${paneSize}, using \"medium\" instead`\n );\n maxFs = 3600;\n }\n\n return maxFs;\n}\n\ntype Options = {\n resolution?: RemoteVideoResolution; // applies only to groups of type MediaType.VideoMain and MediaType.VideoSlides\n};\n\nexport type RemoteMediaId = string;\n\nlet remoteMediaCounter = 0;\n\n/**\n * Class representing a remote audio/video stream.\n *\n * Internally it is associated with a specific receive slot\n * and a media request for it.\n */\nexport class RemoteMedia extends EventsScope {\n private receiveSlot?: ReceiveSlot;\n\n private readonly mediaRequestManager: MediaRequestManager;\n\n private readonly options: Options;\n\n private mediaRequestId?: MediaRequestId;\n\n public readonly id: RemoteMediaId;\n\n /**\n * Constructs RemoteMedia instance\n *\n * @param receiveSlot\n * @param mediaRequestManager\n * @param options\n */\n constructor(\n receiveSlot: ReceiveSlot,\n mediaRequestManager: MediaRequestManager,\n options?: Options\n ) {\n super();\n remoteMediaCounter += 1;\n this.receiveSlot = receiveSlot;\n this.mediaRequestManager = mediaRequestManager;\n this.options = options || {};\n this.setupEventListeners();\n this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;\n }\n\n /**\n * Supply the width and height of the video element\n * to restrict the requested resolution to this size\n * @param width width of the video element\n * @param height height of the video element\n */\n public setSizeHint(width, height) {\n // only base on height for now\n let fs: number;\n\n if (height < 135) {\n fs = 60;\n } else if (height < 270) {\n fs = 240;\n } else if (height < 540) {\n fs = 920;\n } else {\n fs = 3600;\n }\n\n this.receiveSlot?.setMaxFs(fs);\n }\n\n /**\n * Invalidates the remote media by clearing the reference to a receive slot and\n * cancelling the media request.\n * After this call the remote media is unusable.\n *\n * @param {boolean} commit - whether to commit the cancellation of the media request\n * @internal\n */\n public stop(commit = true) {\n this.cancelMediaRequest(commit);\n this.receiveSlot?.removeAllListeners();\n this.receiveSlot = undefined;\n this.emit(\n {\n file: 'multistream/remoteMedia',\n function: 'stop',\n },\n RemoteMediaEvents.Stopped,\n {}\n );\n }\n\n /**\n * Sends a new media request. This method can only be used for receiver-selected policy,\n * because only in that policy we have a 1-1 relationship between RemoteMedia and MediaRequest\n * and the request id is then stored in this RemoteMedia instance.\n * For active-speaker policy, the same request is shared among many RemoteMedia instances,\n * so it's managed through RemoteMediaGroup\n *\n * @internal\n */\n public sendMediaRequest(csi: CSI, commit: boolean) {\n if (this.mediaRequestId) {\n this.cancelMediaRequest(false);\n }\n\n if (!this.receiveSlot) {\n throw new Error('sendMediaRequest() called on an invalidated RemoteMedia instance');\n }\n\n this.mediaRequestId = this.mediaRequestManager.addRequest(\n {\n policyInfo: {\n policy: 'receiver-selected',\n csi,\n },\n receiveSlots: [this.receiveSlot],\n codecInfo: this.options.resolution && {\n codec: 'h264',\n maxFs: getMaxFs(this.options.resolution),\n },\n },\n commit\n );\n }\n\n /**\n * @internal\n */\n public cancelMediaRequest(commit: boolean) {\n if (this.mediaRequestId) {\n this.mediaRequestManager.cancelRequest(this.mediaRequestId, commit);\n this.mediaRequestId = undefined;\n }\n }\n\n /**\n * registers event listeners on the receive slot and forwards all the events\n */\n private setupEventListeners() {\n if (this.receiveSlot) {\n const scope = {\n file: 'multistream/remoteMedia',\n function: 'setupEventListeners',\n };\n\n this.receiveSlot.on(ReceiveSlotEvents.SourceUpdate, (data) => {\n this.emit(scope, RemoteMediaEvents.SourceUpdate, data);\n });\n }\n }\n\n /**\n * Getter for mediaType\n */\n public get mediaType(): MediaType {\n return this.receiveSlot?.mediaType;\n }\n\n /**\n * Getter for memberId\n */\n public get memberId() {\n return this.receiveSlot?.memberId;\n }\n\n /**\n * Getter for csi\n */\n public get csi() {\n return this.receiveSlot?.csi;\n }\n\n /**\n * Getter for source state\n */\n public get sourceState(): SourceState {\n return this.receiveSlot?.sourceState;\n }\n\n /**\n * Getter for remote media stream\n */\n public get stream() {\n return this.receiveSlot?.stream;\n }\n\n /**\n * @internal\n * @returns {ReceiveSlot}\n */\n public getUnderlyingReceiveSlot() {\n return this.receiveSlot;\n }\n}\n"],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export default Meetings;
|
|
2
2
|
export * as CONSTANTS from "./constants";
|
|
3
3
|
export * as REACTIONS from "./reactions/reactions";
|
|
4
|
+
export { RemoteMedia } from "./multistream/remoteMedia";
|
|
4
5
|
export { default as TriggerProxy } from "./common/events/trigger-proxy";
|
|
5
6
|
import Meetings from "./meetings";
|
|
@@ -25,6 +25,7 @@ export interface MediaRequest {
|
|
|
25
25
|
policyInfo: PolicyInfo;
|
|
26
26
|
receiveSlots: Array<ReceiveSlot>;
|
|
27
27
|
codecInfo?: CodecInfo;
|
|
28
|
+
preferredMaxFs?: number;
|
|
28
29
|
}
|
|
29
30
|
export type MediaRequestId = string;
|
|
30
31
|
type DegradationPreferences = {
|
|
@@ -35,11 +36,10 @@ export declare class MediaRequestManager {
|
|
|
35
36
|
private sendMediaRequestsCallback;
|
|
36
37
|
private counter;
|
|
37
38
|
private clientRequests;
|
|
38
|
-
private slotsActiveInLastMediaRequest;
|
|
39
39
|
private degradationPreferences;
|
|
40
40
|
private sourceUpdateListener;
|
|
41
|
+
private debouncedSourceUpdateListener;
|
|
41
42
|
constructor(degradationPreferences: DegradationPreferences, sendMediaRequestsCallback: SendMediaRequestsCallback);
|
|
42
|
-
private resetInactiveReceiveSlots;
|
|
43
43
|
setDegradationPreferences(degradationPreferences: DegradationPreferences): void;
|
|
44
44
|
private getDegradedClientRequests;
|
|
45
45
|
private sendRequests;
|
|
@@ -2,6 +2,7 @@ import { MediaType, ReceiveSlot as WcmeReceiveSlot, SourceState } from '@webex/i
|
|
|
2
2
|
import EventsScope from '../common/events/events-scope';
|
|
3
3
|
export declare const ReceiveSlotEvents: {
|
|
4
4
|
SourceUpdate: string;
|
|
5
|
+
MaxFsUpdate: string;
|
|
5
6
|
};
|
|
6
7
|
export type { SourceState } from '@webex/internal-media-core';
|
|
7
8
|
export type CSI = number;
|
|
@@ -35,6 +36,11 @@ export declare class ReceiveSlot extends EventsScope {
|
|
|
35
36
|
* Getter for csi
|
|
36
37
|
*/
|
|
37
38
|
get csi(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Set the max frame size for this slot
|
|
41
|
+
* @param newFs frame size
|
|
42
|
+
*/
|
|
43
|
+
setMaxFs(newFs: any): void;
|
|
38
44
|
/**
|
|
39
45
|
* Getter for sourceState
|
|
40
46
|
*/
|
|
@@ -55,12 +61,4 @@ export declare class ReceiveSlot extends EventsScope {
|
|
|
55
61
|
* The underlying WCME receive slot
|
|
56
62
|
*/
|
|
57
63
|
get wcmeReceiveSlot(): WcmeReceiveSlot;
|
|
58
|
-
/**
|
|
59
|
-
* Resets the source state to the default 'no source' value.
|
|
60
|
-
* This function should be called on receive slots that are
|
|
61
|
-
* no longer part of a media request. It's needed because WCME
|
|
62
|
-
* does not send any more events on such slots, so the sourceState
|
|
63
|
-
* value would not represent the truth anymore.
|
|
64
|
-
*/
|
|
65
|
-
resetSourceState(): void;
|
|
66
64
|
}
|
|
@@ -37,6 +37,13 @@ export declare class RemoteMedia extends EventsScope {
|
|
|
37
37
|
* @param options
|
|
38
38
|
*/
|
|
39
39
|
constructor(receiveSlot: ReceiveSlot, mediaRequestManager: MediaRequestManager, options?: Options);
|
|
40
|
+
/**
|
|
41
|
+
* Supply the width and height of the video element
|
|
42
|
+
* to restrict the requested resolution to this size
|
|
43
|
+
* @param width width of the video element
|
|
44
|
+
* @param height height of the video element
|
|
45
|
+
*/
|
|
46
|
+
setSizeHint(width: any, height: any): void;
|
|
40
47
|
/**
|
|
41
48
|
* Invalidates the remote media by clearing the reference to a receive slot and
|
|
42
49
|
* cancelling the media request.
|
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.39",
|
|
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.39",
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-beta.39",
|
|
37
|
+
"@webex/test-helper-mocha": "3.0.0-beta.39",
|
|
38
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.39",
|
|
39
|
+
"@webex/test-helper-retry": "3.0.0-beta.39",
|
|
40
|
+
"@webex/test-helper-test-users": "3.0.0-beta.39",
|
|
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.
|
|
50
|
-
"@webex/internal-media-core": "1.35.
|
|
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.
|
|
49
|
+
"@webex/common": "3.0.0-beta.39",
|
|
50
|
+
"@webex/internal-media-core": "1.35.2",
|
|
51
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.39",
|
|
52
|
+
"@webex/internal-plugin-device": "3.0.0-beta.39",
|
|
53
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.39",
|
|
54
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.39",
|
|
55
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.39",
|
|
56
|
+
"@webex/internal-plugin-support": "3.0.0-beta.39",
|
|
57
|
+
"@webex/internal-plugin-user": "3.0.0-beta.39",
|
|
58
|
+
"@webex/plugin-people": "3.0.0-beta.39",
|
|
59
|
+
"@webex/plugin-rooms": "3.0.0-beta.39",
|
|
60
|
+
"@webex/webex-core": "3.0.0-beta.39",
|
|
61
61
|
"ampersand-collection": "^2.0.2",
|
|
62
62
|
"bowser": "^2.11.0",
|
|
63
63
|
"btoa": "^1.2.1",
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
CodecInfo as WcmeCodecInfo,
|
|
8
8
|
H264Codec,
|
|
9
9
|
} from '@webex/internal-media-core';
|
|
10
|
-
import {cloneDeep} from 'lodash';
|
|
10
|
+
import {cloneDeep, debounce} from 'lodash';
|
|
11
11
|
|
|
12
12
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
13
13
|
|
|
@@ -44,6 +44,7 @@ export interface MediaRequest {
|
|
|
44
44
|
policyInfo: PolicyInfo;
|
|
45
45
|
receiveSlots: Array<ReceiveSlot>;
|
|
46
46
|
codecInfo?: CodecInfo;
|
|
47
|
+
preferredMaxFs?: number;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export type MediaRequestId = string;
|
|
@@ -56,6 +57,8 @@ const CODEC_DEFAULTS = {
|
|
|
56
57
|
},
|
|
57
58
|
};
|
|
58
59
|
|
|
60
|
+
const DEBOUNCED_SOURCE_UPDATE_TIME = 1000;
|
|
61
|
+
|
|
59
62
|
type DegradationPreferences = {
|
|
60
63
|
maxMacroblocksLimit: number;
|
|
61
64
|
};
|
|
@@ -69,12 +72,12 @@ export class MediaRequestManager {
|
|
|
69
72
|
|
|
70
73
|
private clientRequests: {[key: MediaRequestId]: MediaRequest};
|
|
71
74
|
|
|
72
|
-
private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};
|
|
73
|
-
|
|
74
75
|
private degradationPreferences: DegradationPreferences;
|
|
75
76
|
|
|
76
77
|
private sourceUpdateListener: () => void;
|
|
77
78
|
|
|
79
|
+
private debouncedSourceUpdateListener: () => void;
|
|
80
|
+
|
|
78
81
|
constructor(
|
|
79
82
|
degradationPreferences: DegradationPreferences,
|
|
80
83
|
sendMediaRequestsCallback: SendMediaRequestsCallback
|
|
@@ -82,34 +85,12 @@ export class MediaRequestManager {
|
|
|
82
85
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
83
86
|
this.counter = 0;
|
|
84
87
|
this.clientRequests = {};
|
|
85
|
-
this.slotsActiveInLastMediaRequest = {};
|
|
86
88
|
this.degradationPreferences = degradationPreferences;
|
|
87
89
|
this.sourceUpdateListener = this.commit.bind(this);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const activeSlots: {[key: ReceiveSlotId]: ReceiveSlot} = {};
|
|
92
|
-
|
|
93
|
-
// create a map of all currently used slot ids
|
|
94
|
-
Object.values(this.clientRequests).forEach((request) =>
|
|
95
|
-
request.receiveSlots.forEach((slot) => {
|
|
96
|
-
activeSlots[slot.id] = slot;
|
|
97
|
-
})
|
|
90
|
+
this.debouncedSourceUpdateListener = debounce(
|
|
91
|
+
this.sourceUpdateListener,
|
|
92
|
+
DEBOUNCED_SOURCE_UPDATE_TIME
|
|
98
93
|
);
|
|
99
|
-
|
|
100
|
-
// when we stop using some receive slots and they are not included in the new media request,
|
|
101
|
-
// we will never get a 'no source' notification for them, so we reset their state,
|
|
102
|
-
// so that the client doesn't try to display their video anymore
|
|
103
|
-
for (const [slotId, slot] of Object.entries(this.slotsActiveInLastMediaRequest)) {
|
|
104
|
-
if (!(slotId in activeSlots)) {
|
|
105
|
-
LoggerProxy.logger.info(
|
|
106
|
-
`multistream:mediaRequestManager --> resetting sourceState to "no source" for slot ${slot.id}`
|
|
107
|
-
);
|
|
108
|
-
slot.resetSourceState();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
this.slotsActiveInLastMediaRequest = activeSlots;
|
|
113
94
|
}
|
|
114
95
|
|
|
115
96
|
public setDegradationPreferences(degradationPreferences: DegradationPreferences) {
|
|
@@ -134,6 +115,7 @@ export class MediaRequestManager {
|
|
|
134
115
|
Object.entries(clientRequests).forEach(([id, mr]) => {
|
|
135
116
|
if (mr.codecInfo) {
|
|
136
117
|
mr.codecInfo.maxFs = Math.min(
|
|
118
|
+
mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs,
|
|
137
119
|
mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
|
|
138
120
|
maxFsLimits[i]
|
|
139
121
|
);
|
|
@@ -201,8 +183,6 @@ export class MediaRequestManager {
|
|
|
201
183
|
});
|
|
202
184
|
|
|
203
185
|
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
204
|
-
|
|
205
|
-
this.resetInactiveReceiveSlots();
|
|
206
186
|
}
|
|
207
187
|
|
|
208
188
|
public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
|
|
@@ -213,6 +193,10 @@ export class MediaRequestManager {
|
|
|
213
193
|
|
|
214
194
|
mediaRequest.receiveSlots.forEach((rs) => {
|
|
215
195
|
rs.on(ReceiveSlotEvents.SourceUpdate, this.sourceUpdateListener);
|
|
196
|
+
rs.on(ReceiveSlotEvents.MaxFsUpdate, ({maxFs}) => {
|
|
197
|
+
mediaRequest.preferredMaxFs = maxFs;
|
|
198
|
+
this.debouncedSourceUpdateListener();
|
|
199
|
+
});
|
|
216
200
|
});
|
|
217
201
|
|
|
218
202
|
if (commit) {
|
|
@@ -240,6 +224,5 @@ export class MediaRequestManager {
|
|
|
240
224
|
|
|
241
225
|
public reset() {
|
|
242
226
|
this.clientRequests = {};
|
|
243
|
-
this.slotsActiveInLastMediaRequest = {};
|
|
244
227
|
}
|
|
245
228
|
}
|
|
@@ -11,6 +11,7 @@ import EventsScope from '../common/events/events-scope';
|
|
|
11
11
|
|
|
12
12
|
export const ReceiveSlotEvents = {
|
|
13
13
|
SourceUpdate: 'sourceUpdate',
|
|
14
|
+
MaxFsUpdate: 'maxFsUpdate',
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export type {SourceState} from '@webex/internal-media-core';
|
|
@@ -81,6 +82,25 @@ export class ReceiveSlot extends EventsScope {
|
|
|
81
82
|
return this.#csi;
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Set the max frame size for this slot
|
|
87
|
+
* @param newFs frame size
|
|
88
|
+
*/
|
|
89
|
+
public setMaxFs(newFs) {
|
|
90
|
+
// emit event for media request manager to listen to
|
|
91
|
+
|
|
92
|
+
this.emit(
|
|
93
|
+
{
|
|
94
|
+
file: 'meeting/receiveSlot',
|
|
95
|
+
function: 'findMemberId',
|
|
96
|
+
},
|
|
97
|
+
ReceiveSlotEvents.MaxFsUpdate,
|
|
98
|
+
{
|
|
99
|
+
maxFs: newFs,
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
/**
|
|
85
105
|
* Getter for sourceState
|
|
86
106
|
*/
|
|
@@ -154,17 +174,4 @@ export class ReceiveSlot extends EventsScope {
|
|
|
154
174
|
get wcmeReceiveSlot(): WcmeReceiveSlot {
|
|
155
175
|
return this.mcReceiveSlot;
|
|
156
176
|
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Resets the source state to the default 'no source' value.
|
|
160
|
-
* This function should be called on receive slots that are
|
|
161
|
-
* no longer part of a media request. It's needed because WCME
|
|
162
|
-
* does not send any more events on such slots, so the sourceState
|
|
163
|
-
* value would not represent the truth anymore.
|
|
164
|
-
*/
|
|
165
|
-
public resetSourceState() {
|
|
166
|
-
this.#sourceState = 'no source';
|
|
167
|
-
this.#csi = undefined;
|
|
168
|
-
this.#memberId = undefined;
|
|
169
|
-
}
|
|
170
177
|
}
|
|
@@ -102,6 +102,29 @@ export class RemoteMedia extends EventsScope {
|
|
|
102
102
|
this.id = `RM${remoteMediaCounter}-${this.receiveSlot.id}`;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Supply the width and height of the video element
|
|
107
|
+
* to restrict the requested resolution to this size
|
|
108
|
+
* @param width width of the video element
|
|
109
|
+
* @param height height of the video element
|
|
110
|
+
*/
|
|
111
|
+
public setSizeHint(width, height) {
|
|
112
|
+
// only base on height for now
|
|
113
|
+
let fs: number;
|
|
114
|
+
|
|
115
|
+
if (height < 135) {
|
|
116
|
+
fs = 60;
|
|
117
|
+
} else if (height < 270) {
|
|
118
|
+
fs = 240;
|
|
119
|
+
} else if (height < 540) {
|
|
120
|
+
fs = 920;
|
|
121
|
+
} else {
|
|
122
|
+
fs = 3600;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.receiveSlot?.setMaxFs(fs);
|
|
126
|
+
}
|
|
127
|
+
|
|
105
128
|
/**
|
|
106
129
|
* Invalidates the remote media by clearing the reference to a receive slot and
|
|
107
130
|
* cancelling the media request.
|
|
@@ -3,6 +3,7 @@ import {ReceiveSlot} from '@webex/plugin-meetings/src/multistream/receiveSlot';
|
|
|
3
3
|
import sinon from 'sinon';
|
|
4
4
|
import {assert} from '@webex/test-helper-chai';
|
|
5
5
|
import {getMaxFs} from '@webex/plugin-meetings/src/multistream/remoteMedia';
|
|
6
|
+
import FakeTimers from '@sinonjs/fake-timers';
|
|
6
7
|
|
|
7
8
|
type ExpectedActiveSpeaker = {
|
|
8
9
|
policy: 'active-speaker';
|
|
@@ -62,7 +63,6 @@ describe('MediaRequestManager', () => {
|
|
|
62
63
|
off: sinon.stub(),
|
|
63
64
|
sourceState: 'live',
|
|
64
65
|
wcmeReceiveSlot: fakeWcmeSlots[index],
|
|
65
|
-
resetSourceState: sinon.stub(),
|
|
66
66
|
} as unknown as ReceiveSlot)
|
|
67
67
|
);
|
|
68
68
|
});
|
|
@@ -609,78 +609,6 @@ describe('MediaRequestManager', () => {
|
|
|
609
609
|
checkMediaRequestsSent([]);
|
|
610
610
|
});
|
|
611
611
|
|
|
612
|
-
it('calls resetSourceState() on slots that are stopped being used', () => {
|
|
613
|
-
const requestIds = [
|
|
614
|
-
addActiveSpeakerRequest(
|
|
615
|
-
255,
|
|
616
|
-
[fakeReceiveSlots[0], fakeReceiveSlots[1]],
|
|
617
|
-
ACTIVE_SPEAKER_MAX_FS
|
|
618
|
-
),
|
|
619
|
-
addActiveSpeakerRequest(
|
|
620
|
-
255,
|
|
621
|
-
[fakeReceiveSlots[2], fakeReceiveSlots[3]],
|
|
622
|
-
ACTIVE_SPEAKER_MAX_FS
|
|
623
|
-
),
|
|
624
|
-
addReceiverSelectedRequest(100, fakeReceiveSlots[4], RECEIVER_SELECTED_MAX_FS),
|
|
625
|
-
addReceiverSelectedRequest(200, fakeReceiveSlots[5], RECEIVER_SELECTED_MAX_FS),
|
|
626
|
-
];
|
|
627
|
-
|
|
628
|
-
mediaRequestManager.commit();
|
|
629
|
-
checkMediaRequestsSent([
|
|
630
|
-
{
|
|
631
|
-
policy: 'active-speaker',
|
|
632
|
-
priority: 255,
|
|
633
|
-
receiveSlots: [fakeWcmeSlots[0], fakeWcmeSlots[1]],
|
|
634
|
-
maxFs: ACTIVE_SPEAKER_MAX_FS,
|
|
635
|
-
},
|
|
636
|
-
{
|
|
637
|
-
policy: 'active-speaker',
|
|
638
|
-
priority: 255,
|
|
639
|
-
receiveSlots: [fakeWcmeSlots[2], fakeWcmeSlots[3]],
|
|
640
|
-
maxFs: ACTIVE_SPEAKER_MAX_FS,
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
policy: 'receiver-selected',
|
|
644
|
-
csi: 100,
|
|
645
|
-
receiveSlot: fakeWcmeSlots[4],
|
|
646
|
-
maxFs: RECEIVER_SELECTED_MAX_FS,
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
policy: 'receiver-selected',
|
|
650
|
-
csi: 200,
|
|
651
|
-
receiveSlot: fakeWcmeSlots[5],
|
|
652
|
-
maxFs: RECEIVER_SELECTED_MAX_FS,
|
|
653
|
-
},
|
|
654
|
-
]);
|
|
655
|
-
|
|
656
|
-
// cancel 2 of the requests
|
|
657
|
-
mediaRequestManager.cancelRequest(requestIds[1], false);
|
|
658
|
-
mediaRequestManager.cancelRequest(requestIds[3], false);
|
|
659
|
-
|
|
660
|
-
mediaRequestManager.commit();
|
|
661
|
-
|
|
662
|
-
// expect only the 2 remaining requests to be sent out
|
|
663
|
-
checkMediaRequestsSent([
|
|
664
|
-
{
|
|
665
|
-
policy: 'active-speaker',
|
|
666
|
-
priority: 255,
|
|
667
|
-
receiveSlots: [fakeWcmeSlots[0], fakeWcmeSlots[1]],
|
|
668
|
-
maxFs: ACTIVE_SPEAKER_MAX_FS,
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
policy: 'receiver-selected',
|
|
672
|
-
csi: 100,
|
|
673
|
-
receiveSlot: fakeWcmeSlots[4],
|
|
674
|
-
maxFs: RECEIVER_SELECTED_MAX_FS,
|
|
675
|
-
},
|
|
676
|
-
]);
|
|
677
|
-
|
|
678
|
-
// and that the receive slots of the 2 cancelled ones had resetSourceState() called
|
|
679
|
-
assert.calledOnce(fakeReceiveSlots[2].resetSourceState);
|
|
680
|
-
assert.calledOnce(fakeReceiveSlots[3].resetSourceState);
|
|
681
|
-
assert.calledOnce(fakeReceiveSlots[5].resetSourceState);
|
|
682
|
-
});
|
|
683
|
-
|
|
684
612
|
it('re-sends media requests after degradation preferences are set', () => {
|
|
685
613
|
// set max macroblocks limit
|
|
686
614
|
mediaRequestManager.setDegradationPreferences({maxMacroblocksLimit: 32400});
|
|
@@ -801,4 +729,41 @@ describe('MediaRequestManager', () => {
|
|
|
801
729
|
},
|
|
802
730
|
]);
|
|
803
731
|
});
|
|
732
|
+
|
|
733
|
+
it('respects the preferredMaxFs if set', () => {
|
|
734
|
+
sendMediaRequestsCallback.resetHistory();
|
|
735
|
+
const clock = FakeTimers.install({now: Date.now()});
|
|
736
|
+
|
|
737
|
+
addActiveSpeakerRequest(255, fakeReceiveSlots.slice(0, 10), getMaxFs('large'), true);
|
|
738
|
+
|
|
739
|
+
sendMediaRequestsCallback.resetHistory();
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
const maxFsHandlerCall = fakeReceiveSlots[0].on.getCall(1);
|
|
743
|
+
|
|
744
|
+
const maxFsHandler = maxFsHandlerCall.args[1];
|
|
745
|
+
const eventName = maxFsHandlerCall.args[0];
|
|
746
|
+
|
|
747
|
+
assert.equal(eventName, 'maxFsUpdate');
|
|
748
|
+
|
|
749
|
+
const preferredFrameSize = 100;
|
|
750
|
+
|
|
751
|
+
maxFsHandler({maxFs: preferredFrameSize});
|
|
752
|
+
|
|
753
|
+
clock.tick(999);
|
|
754
|
+
|
|
755
|
+
assert.notCalled(sendMediaRequestsCallback);
|
|
756
|
+
|
|
757
|
+
clock.tick(1);
|
|
758
|
+
|
|
759
|
+
checkMediaRequestsSent([
|
|
760
|
+
{
|
|
761
|
+
policy: 'active-speaker',
|
|
762
|
+
priority: 255,
|
|
763
|
+
receiveSlots: fakeWcmeSlots.slice(0, 10),
|
|
764
|
+
maxFs: preferredFrameSize,
|
|
765
|
+
},
|
|
766
|
+
]);
|
|
767
|
+
});
|
|
768
|
+
|
|
804
769
|
});
|
|
@@ -83,25 +83,6 @@ describe('ReceiveSlot', () => {
|
|
|
83
83
|
assert.strictEqual(receiveSlot.sourceState, 'live');
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
it('resets source related properties when resetSourceState() is called', () => {
|
|
87
|
-
const csi = 123456;
|
|
88
|
-
const fakeMemberId = '00000001-5555-6666-9012-345678901234';
|
|
89
|
-
|
|
90
|
-
findMemberIdCallbackStub.returns(fakeMemberId);
|
|
91
|
-
|
|
92
|
-
fakeWcmeSlot.emit(WcmeReceiveSlotEvents.SourceUpdate, 'live', csi);
|
|
93
|
-
|
|
94
|
-
assert.strictEqual(receiveSlot.memberId, fakeMemberId);
|
|
95
|
-
assert.strictEqual(receiveSlot.csi, csi);
|
|
96
|
-
assert.strictEqual(receiveSlot.sourceState, 'live');
|
|
97
|
-
|
|
98
|
-
receiveSlot.resetSourceState();
|
|
99
|
-
|
|
100
|
-
assert.strictEqual(receiveSlot.memberId, undefined);
|
|
101
|
-
assert.strictEqual(receiveSlot.csi, undefined);
|
|
102
|
-
assert.strictEqual(receiveSlot.sourceState, 'no source');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
86
|
describe('findMemberId()', () => {
|
|
106
87
|
it('doesn\'t do anything if csi is not set', () => {
|
|
107
88
|
// by default the receiveSlot does not have any csi or member id
|
|
@@ -152,4 +133,23 @@ describe('ReceiveSlot', () => {
|
|
|
152
133
|
assert.notCalled(findMemberIdCallbackStub);
|
|
153
134
|
});
|
|
154
135
|
});
|
|
136
|
+
|
|
137
|
+
describe('setMaxFs()', () => {
|
|
138
|
+
it('emits the correct event', () => {
|
|
139
|
+
sinon.stub(receiveSlot, 'emit');
|
|
140
|
+
receiveSlot.setMaxFs(100);
|
|
141
|
+
|
|
142
|
+
assert.calledOnceWithExactly(
|
|
143
|
+
receiveSlot.emit,
|
|
144
|
+
{
|
|
145
|
+
file: 'meeting/receiveSlot',
|
|
146
|
+
function: 'findMemberId',
|
|
147
|
+
},
|
|
148
|
+
ReceiveSlotEvents.MaxFsUpdate,
|
|
149
|
+
{
|
|
150
|
+
maxFs: 100,
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
155
|
});
|
|
@@ -6,6 +6,7 @@ import {RemoteMedia, RemoteMediaEvents} from '@webex/plugin-meetings/src/multist
|
|
|
6
6
|
import {ReceiveSlotEvents} from '@webex/plugin-meetings/src/multistream/receiveSlot';
|
|
7
7
|
import sinon from 'sinon';
|
|
8
8
|
import {assert} from '@webex/test-helper-chai';
|
|
9
|
+
import { forEach } from 'lodash';
|
|
9
10
|
|
|
10
11
|
describe('RemoteMedia', () => {
|
|
11
12
|
let remoteMedia;
|
|
@@ -21,6 +22,7 @@ describe('RemoteMedia', () => {
|
|
|
21
22
|
fakeReceiveSlot.csi = 999;
|
|
22
23
|
fakeReceiveSlot.sourceState = 'avatar';
|
|
23
24
|
fakeReceiveSlot.stream = fakeStream;
|
|
25
|
+
fakeReceiveSlot.setMaxFs = sinon.stub();
|
|
24
26
|
|
|
25
27
|
fakeMediaRequestManager = {
|
|
26
28
|
addRequest: sinon.stub(),
|
|
@@ -222,4 +224,30 @@ describe('RemoteMedia', () => {
|
|
|
222
224
|
});
|
|
223
225
|
});
|
|
224
226
|
});
|
|
227
|
+
|
|
228
|
+
describe('setSizeHint()', () => {
|
|
229
|
+
|
|
230
|
+
it('works if the receive slot is undefined', () => {
|
|
231
|
+
remoteMedia.receiveSlot = undefined;
|
|
232
|
+
remoteMedia.setSizeHint(100, 100);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
forEach(
|
|
236
|
+
[
|
|
237
|
+
{height: 134, fs: 60},
|
|
238
|
+
{height: 135, fs: 240},
|
|
239
|
+
{height: 269, fs: 240},
|
|
240
|
+
{height: 270, fs: 920},
|
|
241
|
+
{height: 539, fs: 920},
|
|
242
|
+
{height: 540, fs: 3600},
|
|
243
|
+
],
|
|
244
|
+
({height, fs}) => {
|
|
245
|
+
it(`sets the max fs to ${fs} correctly when height is ${height}`, () => {
|
|
246
|
+
remoteMedia.setSizeHint(100, height);
|
|
247
|
+
|
|
248
|
+
assert.calledOnceWithExactly(fakeReceiveSlot.setMaxFs, fs);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
});
|
|
225
253
|
});
|