@webex/plugin-meetings 3.0.0-beta.2 → 3.0.0-beta.4
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/multistream/mediaRequestManager.js +3 -2
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/reconnection-manager/index.js +3 -3
- package/dist/reconnection-manager/index.js.map +1 -1
- package/package.json +18 -18
- package/src/multistream/mediaRequestManager.ts +2 -0
- package/src/reconnection-manager/index.js +2 -2
- package/test/unit/spec/multistream/mediaRequestManager.ts +4 -0
- package/test/unit/spec/reconnection-manager/index.js +67 -2
|
@@ -79,12 +79,13 @@ var MediaRequestManager = /*#__PURE__*/function () {
|
|
|
79
79
|
key: "sendRequests",
|
|
80
80
|
value: function sendRequests() {
|
|
81
81
|
var wcmeMediaRequests = []; // todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)
|
|
82
|
-
|
|
82
|
+
|
|
83
|
+
var maxPayloadBitsPerSecond = 10 * 1000 * 1000; // map all the client media requests to wcme media requests
|
|
83
84
|
|
|
84
85
|
(0, _values.default)(this.clientRequests).forEach(function (mr) {
|
|
85
86
|
wcmeMediaRequests.push(new _internalMediaCore.MediaConnection.MediaRequest(mr.policyInfo.policy === 'active-speaker' ? _internalMediaCore.MediaConnection.Policy.ActiveSpeaker : _internalMediaCore.MediaConnection.Policy.ReceiverSelected, mr.policyInfo.policy === 'active-speaker' ? new _internalMediaCore.MediaConnection.ActiveSpeakerInfo(mr.policyInfo.priority, mr.policyInfo.crossPriorityDuplication, mr.policyInfo.crossPolicyDuplication, mr.policyInfo.preferLiveVideo) : new _internalMediaCore.MediaConnection.ReceiverSelectedInfo(mr.policyInfo.csi), mr.receiveSlots.map(function (receiveSlot) {
|
|
86
87
|
return receiveSlot.wcmeReceiveSlot;
|
|
87
|
-
}), mr.codecInfo && [new _internalMediaCore.MediaConnection.CodecInfo(0x80, new _internalMediaCore.MediaConnection.H264Codec(mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs, mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps, mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps, mr.codecInfo.maxWidth, mr.codecInfo.maxHeight))]));
|
|
88
|
+
}), maxPayloadBitsPerSecond, mr.codecInfo && [new _internalMediaCore.MediaConnection.CodecInfo(0x80, new _internalMediaCore.MediaConnection.H264Codec(mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs, mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps, mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps, mr.codecInfo.maxWidth, mr.codecInfo.maxHeight))]));
|
|
88
89
|
});
|
|
89
90
|
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
90
91
|
this.resetInactiveReceiveSlots();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","MediaRequestManager","sendMediaRequestsCallback","counter","clientRequests","slotsActiveInLastMediaRequest","activeSlots","forEach","request","receiveSlots","slot","id","slotId","LoggerProxy","logger","info","resetSourceState","wcmeMediaRequests","mr","push","MC","MediaRequest","policyInfo","policy","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","priority","crossPriorityDuplication","crossPolicyDuplication","preferLiveVideo","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","codecInfo","CodecInfo","H264Codec","maxWidth","maxHeight","resetInactiveReceiveSlots","mediaRequest","commit","newId","requestId","sendRequests"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {MediaConnection as MC} from '@webex/internal-media-core';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport {ReceiveSlot, ReceiveSlotId} from './receiveSlot';\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 SendMediaRequestsCallback = (mediaRequests: MC.MediaRequest[]) => void;\n\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\n\n private counter;\n\n private clientRequests: {[key: MediaRequestId]: MediaRequest};\n\n private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};\n\n constructor(sendMediaRequestsCallback: SendMediaRequestsCallback) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.slotsActiveInLastMediaRequest = {};\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 private sendRequests() {\n const wcmeMediaRequests: MC.MediaRequest[] = [];\n\n // todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)\n\n // map all the client media requests to wcme media requests\n Object.values(this.clientRequests).forEach((mr) => {\n wcmeMediaRequests.push(\n new MC.MediaRequest(\n mr.policyInfo.policy === 'active-speaker'\n ? MC.Policy.ActiveSpeaker\n : MC.Policy.ReceiverSelected,\n mr.policyInfo.policy === 'active-speaker'\n ? new MC.ActiveSpeakerInfo(\n mr.policyInfo.priority,\n mr.policyInfo.crossPriorityDuplication,\n mr.policyInfo.crossPolicyDuplication,\n mr.policyInfo.preferLiveVideo\n )\n : new MC.ReceiverSelectedInfo(mr.policyInfo.csi),\n mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),\n mr.codecInfo && [\n new MC.CodecInfo(\n 0x80,\n new MC.H264Codec(\n mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.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 if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\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":";;;;;;;;;;;;;;;;;;;;;;;;AACA;;AAEA;;AAHA;AAyCA,IAAMA,cAAc,GAAG;EACrBC,IAAI,EAAE;IACJC,KAAK,EAAE,IADH;IAEJC,MAAM,EAAE,IAFJ;IAGJC,OAAO,EAAE;EAHL;AADe,CAAvB;;IAUaC,mB;EASX,6BAAYC,yBAAZ,EAAkE;IAAA;IAAA;IAAA;IAAA;IAAA;IAChE,KAAKA,yBAAL,GAAiCA,yBAAjC;IACA,KAAKC,OAAL,GAAe,CAAf;IACA,KAAKC,cAAL,GAAsB,EAAtB;IACA,KAAKC,6BAAL,GAAqC,EAArC;EACD;;;;WAED,qCAAoC;MAClC,IAAMC,WAAgD,GAAG,EAAzD,CADkC,CAGlC;;MACA,qBAAc,KAAKF,cAAnB,EAAmCG,OAAnC,CAA2C,UAACC,OAAD;QAAA,OACzCA,OAAO,CAACC,YAAR,CAAqBF,OAArB,CAA6B,UAACG,IAAD,EAAU;UACrCJ,WAAW,CAACI,IAAI,CAACC,EAAN,CAAX,GAAuBD,IAAvB;QACD,CAFD,CADyC;MAAA,CAA3C,EAJkC,CAUlC;MACA;MACA;;MACA,mCAA6B,sBAAe,KAAKL,6BAApB,CAA7B,qCAAiF;QAA5E;QAAA,IAAOO,MAAP;QAAA,IAAeF,IAAf;;QACH,IAAI,EAAEE,MAAM,IAAIN,WAAZ,CAAJ,EAA8B;UAC5BO,oBAAA,CAAYC,MAAZ,CAAmBC,IAAnB,+FACuFL,IAAI,CAACC,EAD5F;;UAGAD,IAAI,CAACM,gBAAL;QACD;MACF;;MAED,KAAKX,6BAAL,GAAqCC,WAArC;IACD;;;WAED,wBAAuB;MACrB,IAAMW,iBAAoC,GAAG,EAA7C,CADqB,CAGrB
|
|
1
|
+
{"version":3,"names":["CODEC_DEFAULTS","h264","maxFs","maxFps","maxMbps","MediaRequestManager","sendMediaRequestsCallback","counter","clientRequests","slotsActiveInLastMediaRequest","activeSlots","forEach","request","receiveSlots","slot","id","slotId","LoggerProxy","logger","info","resetSourceState","wcmeMediaRequests","maxPayloadBitsPerSecond","mr","push","MC","MediaRequest","policyInfo","policy","Policy","ActiveSpeaker","ReceiverSelected","ActiveSpeakerInfo","priority","crossPriorityDuplication","crossPolicyDuplication","preferLiveVideo","ReceiverSelectedInfo","csi","map","receiveSlot","wcmeReceiveSlot","codecInfo","CodecInfo","H264Codec","maxWidth","maxHeight","resetInactiveReceiveSlots","mediaRequest","commit","newId","requestId","sendRequests"],"sources":["mediaRequestManager.ts"],"sourcesContent":["/* eslint-disable require-jsdoc */\nimport {MediaConnection as MC} from '@webex/internal-media-core';\n\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport {ReceiveSlot, ReceiveSlotId} from './receiveSlot';\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 SendMediaRequestsCallback = (mediaRequests: MC.MediaRequest[]) => void;\n\nexport class MediaRequestManager {\n private sendMediaRequestsCallback: SendMediaRequestsCallback;\n\n private counter;\n\n private clientRequests: {[key: MediaRequestId]: MediaRequest};\n\n private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};\n\n constructor(sendMediaRequestsCallback: SendMediaRequestsCallback) {\n this.sendMediaRequestsCallback = sendMediaRequestsCallback;\n this.counter = 0;\n this.clientRequests = {};\n this.slotsActiveInLastMediaRequest = {};\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 private sendRequests() {\n const wcmeMediaRequests: MC.MediaRequest[] = [];\n\n // todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)\n const maxPayloadBitsPerSecond = 10 * 1000 * 1000;\n\n // map all the client media requests to wcme media requests\n Object.values(this.clientRequests).forEach((mr) => {\n wcmeMediaRequests.push(\n new MC.MediaRequest(\n mr.policyInfo.policy === 'active-speaker'\n ? MC.Policy.ActiveSpeaker\n : MC.Policy.ReceiverSelected,\n mr.policyInfo.policy === 'active-speaker'\n ? new MC.ActiveSpeakerInfo(\n mr.policyInfo.priority,\n mr.policyInfo.crossPriorityDuplication,\n mr.policyInfo.crossPolicyDuplication,\n mr.policyInfo.preferLiveVideo\n )\n : new MC.ReceiverSelectedInfo(mr.policyInfo.csi),\n mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),\n maxPayloadBitsPerSecond,\n mr.codecInfo && [\n new MC.CodecInfo(\n 0x80,\n new MC.H264Codec(\n mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.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 if (commit) {\n this.commit();\n }\n\n return newId;\n }\n\n public cancelRequest(requestId: MediaRequestId, commit = true) {\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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"}
|
|
@@ -694,7 +694,7 @@ var ReconnectionManager = /*#__PURE__*/function () {
|
|
|
694
694
|
key: "reconnectMedia",
|
|
695
695
|
value: function () {
|
|
696
696
|
var _reconnectMedia = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
|
|
697
|
-
var
|
|
697
|
+
var turnServerResult, mc;
|
|
698
698
|
return _regenerator.default.wrap(function _callee4$(_context4) {
|
|
699
699
|
while (1) {
|
|
700
700
|
switch (_context4.prev = _context4.next) {
|
|
@@ -712,8 +712,8 @@ var ReconnectionManager = /*#__PURE__*/function () {
|
|
|
712
712
|
return this.meeting.roap.doTurnDiscovery(this.meeting, true);
|
|
713
713
|
|
|
714
714
|
case 6:
|
|
715
|
-
|
|
716
|
-
mc = this.meeting.createMediaConnection(turnServerInfo);
|
|
715
|
+
turnServerResult = _context4.sent;
|
|
716
|
+
mc = this.meeting.createMediaConnection(turnServerResult.turnServerInfo);
|
|
717
717
|
this.meeting.statsAnalyzer.updateMediaConnection(mc);
|
|
718
718
|
return _context4.abrupt("return", mc.initiateOffer());
|
|
719
719
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NeedsRetryError","Error","NeedsRejoinError","wasSharing","error","ReconnectionManager","meeting","iceState","disconnected","resolve","timer","undefined","timeoutDuration","config","reconnection","iceReconnectionTimeout","status","RECONNECTION","STATE","DEFAULT_STATUS","tryCount","DEFAULT_TRY_COUNT","webex","maxRejoinAttempts","rejoinAttempts","autoRejoinEnabled","autoRejoin","reset","clearTimeout","LoggerProxy","logger","log","resetReconnectionTimer","reject","setTimeout","IN_PROGRESS","enabled","COMPLETE","info","ReconnectInProgress","ReconnectionError","networkDisconnect","networkRetry","id","validate","Metrics","postEvent","event","eventType","MEDIA_RECONNECTING","executeReconnection","then","MEDIA_RECOVERED","data","recoveredBy","RECOVERED_BY_NEW","catch","reconnectError","reconnect","message","reconnectMetric","CALL_ABORTED","errors","category","errorObjects","expected","errorCode","fatal","name","mediaEngine","shownToUser","rejoinMeeting","reconnectMercuryWebSocket","internal","device","url","FAILURE","shareStatus","SHARE_STATUS","LOCAL_SHARE_ACTIVE","meetings","syncMeetings","getMeetingByType","_ID_","state","_LEFT_","type","_CALL_","reconnectMedia","media","join","rejoin","Media","stopTracks","mediaProperties","shareTrack","isSharing","NO_SHARE","mediaDirection","sendShare","Trigger","trigger","file","function","EVENT_TRIGGERS","MEETING_STOPPED_SHARING_LOCAL","reason","SHARE_STOPPED_REASON","MEETING_REJOIN","sendBehavioralMetric","BEHAVIORAL_METRICS","MEETING_MAX_REJOIN_FAILURE","locus_id","locusUrl","split","pop","stack","closePeerConnections","unsetPeerConnection","roap","doTurnDiscovery","turnServerInfo","mc","createMediaConnection","statsAnalyzer","updateMediaConnection","initiateOffer","mercury","connected","disconnect","connect"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-disable no-warning-comments */\n\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport Trigger from '../common/events/trigger-proxy';\nimport {\n EVENT_TRIGGERS,\n RECONNECTION,\n SHARE_STATUS,\n SHARE_STOPPED_REASON,\n _CALL_,\n _LEFT_,\n _ID_\n} from '../constants';\nimport BEHAVIORAL_METRICS from '../metrics/constants';\nimport ReconnectionError from '../common/errors/reconnection';\nimport ReconnectInProgress from '../common/errors/reconnection-in-progress';\nimport {eventType, reconnection, errorObjects} from '../metrics/config';\nimport Media from '../media';\nimport Metrics from '../metrics';\n\n/**\n * Used to indicate that the reconnect logic needs to be retried.\n *\n * @class NeedsRetryError\n * @extends {Error}\n */\nclass NeedsRetryError extends Error {}\n\n/**\n * Used to indicate that the meeting needs to be rejoined, not just media reconnected\n *\n * @class NeedsRejoinError\n * @extends {Error}\n */\nclass NeedsRejoinError extends Error {\n /**\n * Creates an instance of NeedsRejoinError.\n * @param {Object} params\n * @param {boolean} params.wasSharing\n * @param {Error} params.error\n * @memberof NeedsRejoinError\n */\n constructor({wasSharing, error = new Error('Meeting needs to be rejoined')}) {\n super(error);\n\n this.wasSharing = wasSharing;\n }\n}\n\n/**\n * @export\n * @class ReconnectionManager\n*/\nexport default class ReconnectionManager {\n /**\n * @param {Meeting} meeting\n */\n constructor(meeting) {\n /**\n * Stores ICE reconnection state data.\n *\n * @instance\n * @type {Object}\n * @private\n * @memberof ReconnectionManager\n */\n this.iceState = {\n disconnected: false,\n resolve: () => {},\n timer: undefined,\n timeoutDuration: meeting.config.reconnection.iceReconnectionTimeout\n };\n\n /**\n * @instance\n * @type {String}\n * @private\n * @memberof ReconnectionManager\n */\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n /**\n * @instance\n * @type {Number}\n * @private\n * @memberof ReconnectionManager\n */\n this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n /**\n * @instance\n * @type {Object}\n * @private\n * @memberof ReconnectionManager\n */\n // TODO : change this logic to not save the meeting instance\n // It gets complicated when meeting ends on remote side , We have a old meeting instance which is not up to date\n this.webex = meeting.webex;\n /**\n * @instance\n * @type {Meeting}\n * @private\n * @memberof ReconnectionManager\n */\n // TODO: try removing the circular dependency for meeting and reconnection manager\n // try moving this to meetings collection\n this.meeting = meeting;\n\n this.maxRejoinAttempts = meeting.config.reconnection.maxRejoinAttempts;\n this.rejoinAttempts = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n this.autoRejoinEnabled = meeting.config.reconnection.autoRejoin;\n\n\n // Make sure reconnection state is in default\n this.reset();\n }\n\n /**\n * @public\n * @memberof ReconnectionManager\n * @returns {void}\n */\n resetReconnectionTimer() {\n this.iceState.resolve();\n this.iceState.resolve = () => {};\n\n if (this.iceState.timer) {\n clearTimeout(this.iceState.timer);\n delete this.iceState.timer;\n }\n }\n\n /**\n * Sets the iceState to connected and clears any disconnect timeouts and\n * related timeout data within the iceState.\n *\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n iceReconnected() {\n if (this.iceState.disconnected) {\n LoggerProxy.logger.log('ReconnectionManager:index#iceReconnected --> ice has reconnected');\n\n this.resetReconnectionTimer();\n\n this.iceState.disconnected = false;\n }\n }\n\n /**\n * Set the iceState to disconnected and generates a timeout that waits for the\n * iceState to reconnect and then resolves. If the ice state is already\n * processing a reconnect, it immediately resolves. Rejects if the timeout\n * duration is reached.\n *\n * @returns {Promise<undefined>}\n * @public\n * @memberof ReconnectionManager\n */\n waitForIceReconnect() {\n if (!this.iceState.disconnected) {\n LoggerProxy.logger.log('ReconnectionManager:index#waitForIceReconnect --> waiting for ice reconnect');\n\n this.iceState.disconnected = true;\n\n return new Promise((resolve, reject) => {\n this.iceState.timer = setTimeout(() => {\n if (this.iceState.disconnected === false) {\n resolve();\n }\n else {\n this.iceState.disconnected = false;\n reject(new Error(`ice reconnection did not occur in ${this.iceState.timeoutDuration}ms`));\n }\n }, this.iceState.timeoutDuration);\n\n this.iceState.resolve = resolve;\n });\n }\n\n // return a resolved promise to prevent multiple catch executions of reconnect\n return Promise.resolve();\n }\n\n /**\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n reset() {\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n this.rejoinAttempts = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n }\n\n /**\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n cleanUp() {\n this.reset();\n this.meeting = null;\n }\n\n\n /**\n * @public\n * @memberof ReconnectionManager\n * @returns {Boolean} true if reconnection operation is in progress\n */\n isReconnectInProgress() {\n return (this.status === RECONNECTION.STATE.IN_PROGRESS);\n }\n\n /**\n * @returns {Boolean}\n * @throws {ReconnectionError}\n * @private\n * @memberof ReconnectionManager\n */\n validate() {\n if (this.meeting.config.reconnection.enabled) {\n if (\n this.status === RECONNECTION.STATE.DEFAULT_STATUS ||\n this.status === RECONNECTION.STATE.COMPLETE\n ) {\n return true;\n }\n\n LoggerProxy.logger.info('ReconnectionManager:index#validate --> Reconnection already in progress.');\n\n throw new ReconnectInProgress('Reconnection already in progress.');\n }\n\n LoggerProxy.logger.info('ReconnectionManager:index#validate --> Reconnection is not enabled.');\n\n throw new ReconnectionError('Reconnection is not enabled.');\n }\n\n /**\n * Initiates a media reconnect for the active meeting\n * @param {Object} reconnectOptions\n * @param {boolean} [reconnectOptions.networkDisconnect=false] indicates if a network disconnect event happened\n * @param {boolean} [reconnectOptions.networkRetry=false] indicates if we are retrying the reconnect\n * @returns {Promise}\n * @public\n * @memberof ReconnectionManager\n */\n async reconnect({networkDisconnect = false, networkRetry = false} = {}) {\n LoggerProxy.logger.info(`ReconnectionManager:index#reconnect --> Reconnection start for meeting ${this.meeting.id}.`);\n // First, validate that we can reconnect, if not, it will throw an error\n try {\n this.validate();\n }\n catch (error) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection unable to begin.', error);\n throw error;\n }\n\n if (!networkRetry) {\n // Only log START metrics on the initial reconnect\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect start metric.');\n Metrics.postEvent({\n event: eventType.MEDIA_RECONNECTING,\n meeting: this.meeting\n });\n }\n\n return this.executeReconnection({networkDisconnect})\n .then(() => {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection successful.');\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect success metric.');\n Metrics.postEvent({\n event: eventType.MEDIA_RECOVERED,\n meeting: this.meeting,\n data: {recoveredBy: reconnection.RECOVERED_BY_NEW}\n });\n })\n .catch((reconnectError) => {\n if (reconnectError instanceof NeedsRetryError) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection not successful, retrying.');\n // Reset our reconnect status since we are looping back to the beginning\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n\n // This is a network retry, so we should not log START metrics again\n return this.reconnect({networkDisconnect: true, networkRetry: true});\n }\n\n // Reconnect has failed\n LoggerProxy.logger.error('ReconnectionManager:index#reconnect --> Reconnection failed.', reconnectError.message);\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect abort metric.');\n\n const reconnectMetric = {\n event: eventType.CALL_ABORTED,\n meeting: this.meeting,\n data: {\n errors: [\n {\n category: errorObjects.category.expected,\n errorCode: 2008,\n fatal: true,\n name: errorObjects.name.mediaEngine,\n shownToUser: false\n }\n ]\n }\n };\n\n Metrics.postEvent(reconnectMetric);\n if (reconnectError instanceof NeedsRejoinError) {\n // send call aborded event with catogery as expected as we are trying to rejoin\n\n if (this.autoRejoinEnabled) {\n return this.rejoinMeeting(reconnectError.wasSharing);\n }\n }\n\n\n throw reconnectError;\n });\n }\n\n /**\n * @param {Object} reconnectOptions\n * @param {boolean} [reconnectOptions.networkDisconnect=false] indicates if a network disconnect event happened\n * @returns {Promise}\n * @throws {NeedsRetryError}\n * @private\n * @memberof ReconnectionManager\n */\n async executeReconnection({networkDisconnect = false}) {\n this.status = RECONNECTION.STATE.IN_PROGRESS;\n\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Attempting to reconnect to meeting.');\n\n if (networkDisconnect) {\n try {\n await this.reconnectMercuryWebSocket();\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Websocket reconnected.', this.webex.internal.device.url);\n }\n catch (error) {\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Unable to reconnect to websocket, giving up.');\n this.status = RECONNECTION.STATE.FAILURE;\n throw (error);\n }\n }\n\n const wasSharing = this.meeting.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE;\n\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Updating meeting data from server.');\n await this.webex.meetings.syncMeetings();\n }\n catch (syncError) {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Unable to sync meetings, reconnecting.', syncError);\n throw (new NeedsRetryError(syncError));\n }\n\n // TODO: try to improve this logic as the reconnection manager saves the instance of deleted meeting object\n // So that on rejoin it known what parametrs it was using\n if (!this.meeting || !this.webex.meetings.getMeetingByType(_ID_, this.meeting.id)) {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Meeting got deleted due to inactivity or ended remotely ');\n\n throw new Error('Unable to rejoin a meeting already ended or inactive .');\n }\n\n LoggerProxy.logger.info(`ReconnectionManager:index#executeReconnection --> Current state of meeting is ${this.meeting.state}`);\n\n // If the meeting state was left, no longer reconnect media\n if (this.meeting.state === _LEFT_) {\n if (this.meeting.type === _CALL_) {\n throw new Error('Unable to rejoin a call in LEFT state.');\n }\n\n throw (new NeedsRejoinError({wasSharing}));\n }\n\n try {\n const media = await this.reconnectMedia();\n\n LoggerProxy.logger.log('ReconnectionManager:index#executeReconnection --> Media reestablished');\n this.status = RECONNECTION.STATE.COMPLETE;\n\n return media;\n }\n catch (error) {\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Media reestablishment failed');\n this.status = RECONNECTION.STATE.FAILURE;\n\n throw (error);\n }\n }\n\n /**\n * Rejoins a meeting after detecting the member was in a LEFT state\n *\n * @async\n * @param {boolean} wasSharing\n * @returns {Promise}\n */\n async rejoinMeeting(wasSharing = false) {\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> attemping meeting rejoin');\n\n await this.meeting.join({rejoin: true});\n LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> meeting rejoined');\n\n if (wasSharing) {\n // Stop the share streams if user tried to rejoin\n Media.stopTracks(this.meeting.mediaProperties.shareTrack);\n this.meeting.isSharing = false;\n if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {\n this.meeting.shareStatus = SHARE_STATUS.NO_SHARE;\n }\n this.meeting.mediaProperties.mediaDirection.sendShare = false;\n Trigger.trigger(\n this.meeting,\n {\n file: 'reconnection-manager/index',\n function: 'rejoinMeeting'\n },\n EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,\n {\n reason: SHARE_STOPPED_REASON.MEETING_REJOIN\n }\n );\n }\n }\n catch (joinError) {\n this.rejoinAttempts += 1;\n if (this.rejoinAttempts <= this.maxRejoinAttempts) {\n LoggerProxy.logger.info(`ReconnectionManager:index#rejoinMeeting --> Unable to rejoin meeting, attempt #${this.rejoinAttempts}, retrying.`, joinError);\n this.rejoinMeeting();\n }\n else {\n LoggerProxy.logger.error('ReconnectionManager:index#rejoinMeeting --> Unable to rejoin meeting after max attempts.', joinError);\n Metrics.sendBehavioralMetric(\n BEHAVIORAL_METRICS.MEETING_MAX_REJOIN_FAILURE,\n {\n locus_id: this.meeting.locusUrl.split('/').pop(),\n reason: joinError.message,\n stack: joinError.stack\n }\n );\n this.status = RECONNECTION.STATE.FAILURE;\n throw joinError;\n }\n }\n\n try {\n await this.reconnectMedia();\n }\n catch (mediaError) {\n LoggerProxy.logger.error('ReconnectionManager:index#rejoinMeeting --> Unable to reestablish media after rejoining.', mediaError);\n throw mediaError;\n }\n }\n\n /**\n * @returns {Promise}\n * @private\n * @memberof ReconnectionManager\n */\n async reconnectMedia() {\n LoggerProxy.logger.log('ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media');\n\n // we are not simply calling this.meeting.mediaProperties.webrtcMediaConnection.reconnect(),\n // but instead manually closing and creating new media connection, because we need to do the TURN discovery again\n\n await this.meeting.closePeerConnections();\n this.meeting.mediaProperties.unsetPeerConnection();\n\n const turnServerInfo = await this.meeting.roap.doTurnDiscovery(this.meeting, true);\n\n const mc = this.meeting.createMediaConnection(turnServerInfo);\n\n this.meeting.statsAnalyzer.updateMediaConnection(mc);\n\n return mc.initiateOffer();\n }\n\n /**\n * Attempt to Reconnect Mercury Websocket\n * @returns {Promise}\n * @private\n * @memberof ReconnectionManager\n */\n async reconnectMercuryWebSocket() {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Reconnecting websocket.');\n // First, attempt to disconnect if we think we are already connected.\n if (this.webex.internal.mercury.connected) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Disconnecting existing websocket.');\n try {\n await this.webex.internal.mercury.disconnect();\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Websocket disconnected successfully.');\n }\n catch (disconnectError) {\n // If we can't disconnect, the sdk is in such a bad state that reconnecting is not going to happen.\n LoggerProxy.logger.error('ReconnectionManager:index#reconnectMercuryWebSocket --> Unable to disconnect from websocket, giving up.', disconnectError);\n throw disconnectError;\n }\n }\n\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Connecting websocket.');\n await this.webex.internal.mercury.connect();\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Websocket connected successfully.');\n }\n catch (connectError) {\n LoggerProxy.logger.error('ReconnectionManager:index#reconnectMercuryWebSocket --> Unable to connect to websocket, giving up.', connectError);\n\n throw (connectError);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["NeedsRetryError","Error","NeedsRejoinError","wasSharing","error","ReconnectionManager","meeting","iceState","disconnected","resolve","timer","undefined","timeoutDuration","config","reconnection","iceReconnectionTimeout","status","RECONNECTION","STATE","DEFAULT_STATUS","tryCount","DEFAULT_TRY_COUNT","webex","maxRejoinAttempts","rejoinAttempts","autoRejoinEnabled","autoRejoin","reset","clearTimeout","LoggerProxy","logger","log","resetReconnectionTimer","reject","setTimeout","IN_PROGRESS","enabled","COMPLETE","info","ReconnectInProgress","ReconnectionError","networkDisconnect","networkRetry","id","validate","Metrics","postEvent","event","eventType","MEDIA_RECONNECTING","executeReconnection","then","MEDIA_RECOVERED","data","recoveredBy","RECOVERED_BY_NEW","catch","reconnectError","reconnect","message","reconnectMetric","CALL_ABORTED","errors","category","errorObjects","expected","errorCode","fatal","name","mediaEngine","shownToUser","rejoinMeeting","reconnectMercuryWebSocket","internal","device","url","FAILURE","shareStatus","SHARE_STATUS","LOCAL_SHARE_ACTIVE","meetings","syncMeetings","getMeetingByType","_ID_","state","_LEFT_","type","_CALL_","reconnectMedia","media","join","rejoin","Media","stopTracks","mediaProperties","shareTrack","isSharing","NO_SHARE","mediaDirection","sendShare","Trigger","trigger","file","function","EVENT_TRIGGERS","MEETING_STOPPED_SHARING_LOCAL","reason","SHARE_STOPPED_REASON","MEETING_REJOIN","sendBehavioralMetric","BEHAVIORAL_METRICS","MEETING_MAX_REJOIN_FAILURE","locus_id","locusUrl","split","pop","stack","closePeerConnections","unsetPeerConnection","roap","doTurnDiscovery","turnServerResult","mc","createMediaConnection","turnServerInfo","statsAnalyzer","updateMediaConnection","initiateOffer","mercury","connected","disconnect","connect"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-disable no-warning-comments */\n\nimport LoggerProxy from '../common/logs/logger-proxy';\nimport Trigger from '../common/events/trigger-proxy';\nimport {\n EVENT_TRIGGERS,\n RECONNECTION,\n SHARE_STATUS,\n SHARE_STOPPED_REASON,\n _CALL_,\n _LEFT_,\n _ID_\n} from '../constants';\nimport BEHAVIORAL_METRICS from '../metrics/constants';\nimport ReconnectionError from '../common/errors/reconnection';\nimport ReconnectInProgress from '../common/errors/reconnection-in-progress';\nimport {eventType, reconnection, errorObjects} from '../metrics/config';\nimport Media from '../media';\nimport Metrics from '../metrics';\n\n/**\n * Used to indicate that the reconnect logic needs to be retried.\n *\n * @class NeedsRetryError\n * @extends {Error}\n */\nclass NeedsRetryError extends Error {}\n\n/**\n * Used to indicate that the meeting needs to be rejoined, not just media reconnected\n *\n * @class NeedsRejoinError\n * @extends {Error}\n */\nclass NeedsRejoinError extends Error {\n /**\n * Creates an instance of NeedsRejoinError.\n * @param {Object} params\n * @param {boolean} params.wasSharing\n * @param {Error} params.error\n * @memberof NeedsRejoinError\n */\n constructor({wasSharing, error = new Error('Meeting needs to be rejoined')}) {\n super(error);\n\n this.wasSharing = wasSharing;\n }\n}\n\n/**\n * @export\n * @class ReconnectionManager\n*/\nexport default class ReconnectionManager {\n /**\n * @param {Meeting} meeting\n */\n constructor(meeting) {\n /**\n * Stores ICE reconnection state data.\n *\n * @instance\n * @type {Object}\n * @private\n * @memberof ReconnectionManager\n */\n this.iceState = {\n disconnected: false,\n resolve: () => {},\n timer: undefined,\n timeoutDuration: meeting.config.reconnection.iceReconnectionTimeout\n };\n\n /**\n * @instance\n * @type {String}\n * @private\n * @memberof ReconnectionManager\n */\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n /**\n * @instance\n * @type {Number}\n * @private\n * @memberof ReconnectionManager\n */\n this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n /**\n * @instance\n * @type {Object}\n * @private\n * @memberof ReconnectionManager\n */\n // TODO : change this logic to not save the meeting instance\n // It gets complicated when meeting ends on remote side , We have a old meeting instance which is not up to date\n this.webex = meeting.webex;\n /**\n * @instance\n * @type {Meeting}\n * @private\n * @memberof ReconnectionManager\n */\n // TODO: try removing the circular dependency for meeting and reconnection manager\n // try moving this to meetings collection\n this.meeting = meeting;\n\n this.maxRejoinAttempts = meeting.config.reconnection.maxRejoinAttempts;\n this.rejoinAttempts = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n this.autoRejoinEnabled = meeting.config.reconnection.autoRejoin;\n\n\n // Make sure reconnection state is in default\n this.reset();\n }\n\n /**\n * @public\n * @memberof ReconnectionManager\n * @returns {void}\n */\n resetReconnectionTimer() {\n this.iceState.resolve();\n this.iceState.resolve = () => {};\n\n if (this.iceState.timer) {\n clearTimeout(this.iceState.timer);\n delete this.iceState.timer;\n }\n }\n\n /**\n * Sets the iceState to connected and clears any disconnect timeouts and\n * related timeout data within the iceState.\n *\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n iceReconnected() {\n if (this.iceState.disconnected) {\n LoggerProxy.logger.log('ReconnectionManager:index#iceReconnected --> ice has reconnected');\n\n this.resetReconnectionTimer();\n\n this.iceState.disconnected = false;\n }\n }\n\n /**\n * Set the iceState to disconnected and generates a timeout that waits for the\n * iceState to reconnect and then resolves. If the ice state is already\n * processing a reconnect, it immediately resolves. Rejects if the timeout\n * duration is reached.\n *\n * @returns {Promise<undefined>}\n * @public\n * @memberof ReconnectionManager\n */\n waitForIceReconnect() {\n if (!this.iceState.disconnected) {\n LoggerProxy.logger.log('ReconnectionManager:index#waitForIceReconnect --> waiting for ice reconnect');\n\n this.iceState.disconnected = true;\n\n return new Promise((resolve, reject) => {\n this.iceState.timer = setTimeout(() => {\n if (this.iceState.disconnected === false) {\n resolve();\n }\n else {\n this.iceState.disconnected = false;\n reject(new Error(`ice reconnection did not occur in ${this.iceState.timeoutDuration}ms`));\n }\n }, this.iceState.timeoutDuration);\n\n this.iceState.resolve = resolve;\n });\n }\n\n // return a resolved promise to prevent multiple catch executions of reconnect\n return Promise.resolve();\n }\n\n /**\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n reset() {\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n this.rejoinAttempts = RECONNECTION.STATE.DEFAULT_TRY_COUNT;\n }\n\n /**\n * @returns {undefined}\n * @public\n * @memberof ReconnectionManager\n */\n cleanUp() {\n this.reset();\n this.meeting = null;\n }\n\n\n /**\n * @public\n * @memberof ReconnectionManager\n * @returns {Boolean} true if reconnection operation is in progress\n */\n isReconnectInProgress() {\n return (this.status === RECONNECTION.STATE.IN_PROGRESS);\n }\n\n /**\n * @returns {Boolean}\n * @throws {ReconnectionError}\n * @private\n * @memberof ReconnectionManager\n */\n validate() {\n if (this.meeting.config.reconnection.enabled) {\n if (\n this.status === RECONNECTION.STATE.DEFAULT_STATUS ||\n this.status === RECONNECTION.STATE.COMPLETE\n ) {\n return true;\n }\n\n LoggerProxy.logger.info('ReconnectionManager:index#validate --> Reconnection already in progress.');\n\n throw new ReconnectInProgress('Reconnection already in progress.');\n }\n\n LoggerProxy.logger.info('ReconnectionManager:index#validate --> Reconnection is not enabled.');\n\n throw new ReconnectionError('Reconnection is not enabled.');\n }\n\n /**\n * Initiates a media reconnect for the active meeting\n * @param {Object} reconnectOptions\n * @param {boolean} [reconnectOptions.networkDisconnect=false] indicates if a network disconnect event happened\n * @param {boolean} [reconnectOptions.networkRetry=false] indicates if we are retrying the reconnect\n * @returns {Promise}\n * @public\n * @memberof ReconnectionManager\n */\n async reconnect({networkDisconnect = false, networkRetry = false} = {}) {\n LoggerProxy.logger.info(`ReconnectionManager:index#reconnect --> Reconnection start for meeting ${this.meeting.id}.`);\n // First, validate that we can reconnect, if not, it will throw an error\n try {\n this.validate();\n }\n catch (error) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection unable to begin.', error);\n throw error;\n }\n\n if (!networkRetry) {\n // Only log START metrics on the initial reconnect\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect start metric.');\n Metrics.postEvent({\n event: eventType.MEDIA_RECONNECTING,\n meeting: this.meeting\n });\n }\n\n return this.executeReconnection({networkDisconnect})\n .then(() => {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection successful.');\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect success metric.');\n Metrics.postEvent({\n event: eventType.MEDIA_RECOVERED,\n meeting: this.meeting,\n data: {recoveredBy: reconnection.RECOVERED_BY_NEW}\n });\n })\n .catch((reconnectError) => {\n if (reconnectError instanceof NeedsRetryError) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Reconnection not successful, retrying.');\n // Reset our reconnect status since we are looping back to the beginning\n this.status = RECONNECTION.STATE.DEFAULT_STATUS;\n\n // This is a network retry, so we should not log START metrics again\n return this.reconnect({networkDisconnect: true, networkRetry: true});\n }\n\n // Reconnect has failed\n LoggerProxy.logger.error('ReconnectionManager:index#reconnect --> Reconnection failed.', reconnectError.message);\n LoggerProxy.logger.info('ReconnectionManager:index#reconnect --> Sending reconnect abort metric.');\n\n const reconnectMetric = {\n event: eventType.CALL_ABORTED,\n meeting: this.meeting,\n data: {\n errors: [\n {\n category: errorObjects.category.expected,\n errorCode: 2008,\n fatal: true,\n name: errorObjects.name.mediaEngine,\n shownToUser: false\n }\n ]\n }\n };\n\n Metrics.postEvent(reconnectMetric);\n if (reconnectError instanceof NeedsRejoinError) {\n // send call aborded event with catogery as expected as we are trying to rejoin\n\n if (this.autoRejoinEnabled) {\n return this.rejoinMeeting(reconnectError.wasSharing);\n }\n }\n\n\n throw reconnectError;\n });\n }\n\n /**\n * @param {Object} reconnectOptions\n * @param {boolean} [reconnectOptions.networkDisconnect=false] indicates if a network disconnect event happened\n * @returns {Promise}\n * @throws {NeedsRetryError}\n * @private\n * @memberof ReconnectionManager\n */\n async executeReconnection({networkDisconnect = false}) {\n this.status = RECONNECTION.STATE.IN_PROGRESS;\n\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Attempting to reconnect to meeting.');\n\n if (networkDisconnect) {\n try {\n await this.reconnectMercuryWebSocket();\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Websocket reconnected.', this.webex.internal.device.url);\n }\n catch (error) {\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Unable to reconnect to websocket, giving up.');\n this.status = RECONNECTION.STATE.FAILURE;\n throw (error);\n }\n }\n\n const wasSharing = this.meeting.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE;\n\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Updating meeting data from server.');\n await this.webex.meetings.syncMeetings();\n }\n catch (syncError) {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Unable to sync meetings, reconnecting.', syncError);\n throw (new NeedsRetryError(syncError));\n }\n\n // TODO: try to improve this logic as the reconnection manager saves the instance of deleted meeting object\n // So that on rejoin it known what parametrs it was using\n if (!this.meeting || !this.webex.meetings.getMeetingByType(_ID_, this.meeting.id)) {\n LoggerProxy.logger.info('ReconnectionManager:index#executeReconnection --> Meeting got deleted due to inactivity or ended remotely ');\n\n throw new Error('Unable to rejoin a meeting already ended or inactive .');\n }\n\n LoggerProxy.logger.info(`ReconnectionManager:index#executeReconnection --> Current state of meeting is ${this.meeting.state}`);\n\n // If the meeting state was left, no longer reconnect media\n if (this.meeting.state === _LEFT_) {\n if (this.meeting.type === _CALL_) {\n throw new Error('Unable to rejoin a call in LEFT state.');\n }\n\n throw (new NeedsRejoinError({wasSharing}));\n }\n\n try {\n const media = await this.reconnectMedia();\n\n LoggerProxy.logger.log('ReconnectionManager:index#executeReconnection --> Media reestablished');\n this.status = RECONNECTION.STATE.COMPLETE;\n\n return media;\n }\n catch (error) {\n LoggerProxy.logger.error('ReconnectionManager:index#executeReconnection --> Media reestablishment failed');\n this.status = RECONNECTION.STATE.FAILURE;\n\n throw (error);\n }\n }\n\n /**\n * Rejoins a meeting after detecting the member was in a LEFT state\n *\n * @async\n * @param {boolean} wasSharing\n * @returns {Promise}\n */\n async rejoinMeeting(wasSharing = false) {\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> attemping meeting rejoin');\n\n await this.meeting.join({rejoin: true});\n LoggerProxy.logger.info('ReconnectionManager:index#rejoinMeeting --> meeting rejoined');\n\n if (wasSharing) {\n // Stop the share streams if user tried to rejoin\n Media.stopTracks(this.meeting.mediaProperties.shareTrack);\n this.meeting.isSharing = false;\n if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {\n this.meeting.shareStatus = SHARE_STATUS.NO_SHARE;\n }\n this.meeting.mediaProperties.mediaDirection.sendShare = false;\n Trigger.trigger(\n this.meeting,\n {\n file: 'reconnection-manager/index',\n function: 'rejoinMeeting'\n },\n EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,\n {\n reason: SHARE_STOPPED_REASON.MEETING_REJOIN\n }\n );\n }\n }\n catch (joinError) {\n this.rejoinAttempts += 1;\n if (this.rejoinAttempts <= this.maxRejoinAttempts) {\n LoggerProxy.logger.info(`ReconnectionManager:index#rejoinMeeting --> Unable to rejoin meeting, attempt #${this.rejoinAttempts}, retrying.`, joinError);\n this.rejoinMeeting();\n }\n else {\n LoggerProxy.logger.error('ReconnectionManager:index#rejoinMeeting --> Unable to rejoin meeting after max attempts.', joinError);\n Metrics.sendBehavioralMetric(\n BEHAVIORAL_METRICS.MEETING_MAX_REJOIN_FAILURE,\n {\n locus_id: this.meeting.locusUrl.split('/').pop(),\n reason: joinError.message,\n stack: joinError.stack\n }\n );\n this.status = RECONNECTION.STATE.FAILURE;\n throw joinError;\n }\n }\n\n try {\n await this.reconnectMedia();\n }\n catch (mediaError) {\n LoggerProxy.logger.error('ReconnectionManager:index#rejoinMeeting --> Unable to reestablish media after rejoining.', mediaError);\n throw mediaError;\n }\n }\n\n /**\n * @returns {Promise}\n * @private\n * @memberof ReconnectionManager\n */\n async reconnectMedia() {\n LoggerProxy.logger.log('ReconnectionManager:index#reconnectMedia --> Begin reestablishment of media');\n\n // we are not simply calling this.meeting.mediaProperties.webrtcMediaConnection.reconnect(),\n // but instead manually closing and creating new media connection, because we need to do the TURN discovery again\n\n await this.meeting.closePeerConnections();\n this.meeting.mediaProperties.unsetPeerConnection();\n\n const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true);\n\n const mc = this.meeting.createMediaConnection(turnServerResult.turnServerInfo);\n\n this.meeting.statsAnalyzer.updateMediaConnection(mc);\n\n return mc.initiateOffer();\n }\n\n /**\n * Attempt to Reconnect Mercury Websocket\n * @returns {Promise}\n * @private\n * @memberof ReconnectionManager\n */\n async reconnectMercuryWebSocket() {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Reconnecting websocket.');\n // First, attempt to disconnect if we think we are already connected.\n if (this.webex.internal.mercury.connected) {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Disconnecting existing websocket.');\n try {\n await this.webex.internal.mercury.disconnect();\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Websocket disconnected successfully.');\n }\n catch (disconnectError) {\n // If we can't disconnect, the sdk is in such a bad state that reconnecting is not going to happen.\n LoggerProxy.logger.error('ReconnectionManager:index#reconnectMercuryWebSocket --> Unable to disconnect from websocket, giving up.', disconnectError);\n throw disconnectError;\n }\n }\n\n try {\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Connecting websocket.');\n await this.webex.internal.mercury.connect();\n LoggerProxy.logger.info('ReconnectionManager:index#reconnectMercuryWebSocket --> Websocket connected successfully.');\n }\n catch (connectError) {\n LoggerProxy.logger.error('ReconnectionManager:index#reconnectMercuryWebSocket --> Unable to connect to websocket, giving up.', connectError);\n\n throw (connectError);\n }\n }\n}\n"],"mappings":"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"}
|
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.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
]
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@webex/plugin-meetings": "3.0.0-beta.
|
|
32
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
33
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
34
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
35
|
-
"@webex/test-helper-retry": "3.0.0-beta.
|
|
36
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
31
|
+
"@webex/plugin-meetings": "3.0.0-beta.4",
|
|
32
|
+
"@webex/test-helper-chai": "3.0.0-beta.4",
|
|
33
|
+
"@webex/test-helper-mocha": "3.0.0-beta.4",
|
|
34
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.4",
|
|
35
|
+
"@webex/test-helper-retry": "3.0.0-beta.4",
|
|
36
|
+
"@webex/test-helper-test-users": "3.0.0-beta.4",
|
|
37
37
|
"chai": "^4.3.4",
|
|
38
38
|
"chai-as-promised": "^7.1.1",
|
|
39
39
|
"jsdom-global": "3.0.2",
|
|
@@ -41,18 +41,18 @@
|
|
|
41
41
|
"typed-emitter": "^2.1.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@webex/common": "3.0.0-beta.
|
|
45
|
-
"@webex/internal-media-core": "^0.0.
|
|
46
|
-
"@webex/internal-plugin-conversation": "3.0.0-beta.
|
|
47
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
48
|
-
"@webex/internal-plugin-mercury": "3.0.0-beta.
|
|
49
|
-
"@webex/internal-plugin-metrics": "3.0.0-beta.
|
|
50
|
-
"@webex/internal-plugin-support": "3.0.0-beta.
|
|
51
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
52
|
-
"@webex/plugin-people": "3.0.0-beta.
|
|
53
|
-
"@webex/plugin-rooms": "3.0.0-beta.
|
|
44
|
+
"@webex/common": "3.0.0-beta.4",
|
|
45
|
+
"@webex/internal-media-core": "^0.0.17-beta",
|
|
46
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.4",
|
|
47
|
+
"@webex/internal-plugin-device": "3.0.0-beta.4",
|
|
48
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.4",
|
|
49
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.4",
|
|
50
|
+
"@webex/internal-plugin-support": "3.0.0-beta.4",
|
|
51
|
+
"@webex/internal-plugin-user": "3.0.0-beta.4",
|
|
52
|
+
"@webex/plugin-people": "3.0.0-beta.4",
|
|
53
|
+
"@webex/plugin-rooms": "3.0.0-beta.4",
|
|
54
54
|
"@webex/ts-sdp": "^1.0.1",
|
|
55
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
55
|
+
"@webex/webex-core": "3.0.0-beta.4",
|
|
56
56
|
"bowser": "^2.11.0",
|
|
57
57
|
"btoa": "^1.2.1",
|
|
58
58
|
"dotenv": "^4.0.0",
|
|
@@ -94,6 +94,7 @@ export class MediaRequestManager {
|
|
|
94
94
|
const wcmeMediaRequests: MC.MediaRequest[] = [];
|
|
95
95
|
|
|
96
96
|
// todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)
|
|
97
|
+
const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
|
|
97
98
|
|
|
98
99
|
// map all the client media requests to wcme media requests
|
|
99
100
|
Object.values(this.clientRequests).forEach((mr) => {
|
|
@@ -111,6 +112,7 @@ export class MediaRequestManager {
|
|
|
111
112
|
)
|
|
112
113
|
: new MC.ReceiverSelectedInfo(mr.policyInfo.csi),
|
|
113
114
|
mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
|
|
115
|
+
maxPayloadBitsPerSecond,
|
|
114
116
|
mr.codecInfo && [
|
|
115
117
|
new MC.CodecInfo(
|
|
116
118
|
0x80,
|
|
@@ -474,9 +474,9 @@ export default class ReconnectionManager {
|
|
|
474
474
|
await this.meeting.closePeerConnections();
|
|
475
475
|
this.meeting.mediaProperties.unsetPeerConnection();
|
|
476
476
|
|
|
477
|
-
const
|
|
477
|
+
const turnServerResult = await this.meeting.roap.doTurnDiscovery(this.meeting, true);
|
|
478
478
|
|
|
479
|
-
const mc = this.meeting.createMediaConnection(turnServerInfo);
|
|
479
|
+
const mc = this.meeting.createMediaConnection(turnServerResult.turnServerInfo);
|
|
480
480
|
|
|
481
481
|
this.meeting.statsAnalyzer.updateMediaConnection(mc);
|
|
482
482
|
|
|
@@ -15,6 +15,8 @@ type ExpectedReceiverSelected = {
|
|
|
15
15
|
};
|
|
16
16
|
type ExpectedRequest = ExpectedActiveSpeaker | ExpectedReceiverSelected;
|
|
17
17
|
|
|
18
|
+
const maxPayloadBitsPerSecond = 10 * 1000 * 1000; // for now we always send this fixed constant
|
|
19
|
+
|
|
18
20
|
describe('MediaRequestManager', () => {
|
|
19
21
|
const CROSS_PRIORITY_DUPLICATION = true;
|
|
20
22
|
const CROSS_POLICY_DUPLICATION = true;
|
|
@@ -109,6 +111,7 @@ describe('MediaRequestManager', () => {
|
|
|
109
111
|
preferLiveVideo: PREFER_LIVE_VIDEO,
|
|
110
112
|
}),
|
|
111
113
|
receiveSlots: expectedRequest.receiveSlots,
|
|
114
|
+
maxPayloadBitsPerSecond,
|
|
112
115
|
codecInfos: [
|
|
113
116
|
sinon.match({
|
|
114
117
|
payloadType: 0x80,
|
|
@@ -126,6 +129,7 @@ describe('MediaRequestManager', () => {
|
|
|
126
129
|
csi: expectedRequest.csi,
|
|
127
130
|
}),
|
|
128
131
|
receiveSlots: [expectedRequest.receiveSlot],
|
|
132
|
+
maxPayloadBitsPerSecond,
|
|
129
133
|
codecInfos: [
|
|
130
134
|
sinon.match({
|
|
131
135
|
payloadType: 0x80,
|
|
@@ -2,8 +2,8 @@ import 'jsdom-global/register';
|
|
|
2
2
|
import chai from 'chai';
|
|
3
3
|
import chaiAsPromised from 'chai-as-promised';
|
|
4
4
|
import sinon from 'sinon';
|
|
5
|
-
|
|
6
|
-
import
|
|
5
|
+
import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
|
|
6
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
7
7
|
|
|
8
8
|
const {assert} = chai;
|
|
9
9
|
|
|
@@ -11,6 +11,71 @@ chai.use(chaiAsPromised);
|
|
|
11
11
|
sinon.assert.expose(chai.assert, {prefix: ''});
|
|
12
12
|
|
|
13
13
|
describe('plugin-meetings', () => {
|
|
14
|
+
describe('ReconnectionManager.reconnect', () => {
|
|
15
|
+
it('uses correct TURN TLS information on the new connection', async () => {
|
|
16
|
+
Metrics.postEvent = sinon.stub();
|
|
17
|
+
|
|
18
|
+
const fakeMediaConnection = {
|
|
19
|
+
initiateOffer: sinon.stub().resolves({})
|
|
20
|
+
};
|
|
21
|
+
const fakeMeeting = {
|
|
22
|
+
closePeerConnections: sinon.stub().resolves({}),
|
|
23
|
+
createMediaConnection: sinon.stub().returns(fakeMediaConnection),
|
|
24
|
+
config: {
|
|
25
|
+
reconnection: {
|
|
26
|
+
enabled: true,
|
|
27
|
+
detection: true,
|
|
28
|
+
iceReconnectionTimeout: 10000,
|
|
29
|
+
retry: {
|
|
30
|
+
times: 2,
|
|
31
|
+
backOff: {
|
|
32
|
+
start: 1000,
|
|
33
|
+
rate: 2
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
mediaProperties: {
|
|
39
|
+
unsetPeerConnection: sinon.stub(),
|
|
40
|
+
},
|
|
41
|
+
roap: {
|
|
42
|
+
doTurnDiscovery: sinon.stub().resolves({
|
|
43
|
+
turnServerInfo: {
|
|
44
|
+
url: 'fake_turn_url',
|
|
45
|
+
username: 'fake_turn_username',
|
|
46
|
+
password: 'fake_turn_password',
|
|
47
|
+
},
|
|
48
|
+
turnDiscoverySkippedReason: undefined
|
|
49
|
+
})
|
|
50
|
+
},
|
|
51
|
+
statsAnalyzer: {
|
|
52
|
+
updateMediaConnection: sinon.stub(),
|
|
53
|
+
},
|
|
54
|
+
webex: {
|
|
55
|
+
meetings: {
|
|
56
|
+
getMeetingByType: sinon.stub().returns(true),
|
|
57
|
+
syncMeetings: sinon.stub().resolves({})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const rm = new ReconnectionManager(fakeMeeting);
|
|
63
|
+
|
|
64
|
+
await rm.reconnect();
|
|
65
|
+
|
|
66
|
+
assert.calledOnce(fakeMeeting.closePeerConnections);
|
|
67
|
+
assert.calledOnce(fakeMeeting.mediaProperties.unsetPeerConnection);
|
|
68
|
+
assert.calledOnce(fakeMeeting.roap.doTurnDiscovery);
|
|
69
|
+
assert.calledOnce(fakeMeeting.createMediaConnection);
|
|
70
|
+
assert.calledWith(fakeMeeting.createMediaConnection, {
|
|
71
|
+
url: 'fake_turn_url',
|
|
72
|
+
username: 'fake_turn_username',
|
|
73
|
+
password: 'fake_turn_password',
|
|
74
|
+
});
|
|
75
|
+
assert.calledOnce(fakeMediaConnection.initiateOffer);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
14
79
|
/**
|
|
15
80
|
* Currently, testing dependent classes that aren't available at the top
|
|
16
81
|
* level causes testing errors in CI based around related files. Skipping this here until a solution
|