// Metro module 12879 (f12880). import { VideoToggleState } from "../Constants"; import Logger from "../modules/debug/Logger"; import { isMobile } from "../_bundle/Module4426"; import dispatchAutoDisableVideo from "../modules/media_engine/dispatchAutoDisableVideo"; import { now } from "../_bundle/sleep"; import Durations from "../utils/Durations"; export class VideoHealthManager { static defaultConfig = { featureEnabled: isMobile, windowLength: 5, allowedPoorFpsRatio: 1, fpsThreshold: 5, backoffTimeSec: 15 }; constructor(a0, a1, a2, a3) { const r27 = { __proto__: new.target.prototype, logger: new Logger("VideoHealthManager"), disabled: false, perUserFpsWindow: null, prevFramesCodec: null, prevTimestamp: null, streamDisabledUsers: null, retryBackoffCache: null, timeoutIdCache: null, currentVideoAutoToggleState: null, enableQueue: null }; r27.perUserFpsWindow = {}; r27.prevFramesCodec = {}; r27.prevTimestamp = {}; r27.streamDisabledUsers = new Set(); r27.retryBackoffCache = {}; r27.timeoutIdCache = {}; r27.currentVideoAutoToggleState = {}; r27.enableQueue = []; r27.windowLength = a0; r27.fpsThreshold = a2; r27.fpsWindowBorderlineCount = Math.ceil(a0 * a1); r27.backoffTimeSec = a3; r27.logger.enableNativeLogger(true); r27.logger.info(`constructor with windowLength = ${r27.windowLength}, fpsWindowBorderlineCount = ${r27.fpsWindowBorderlineCount}, fpsThreshold = ${r27.fpsThreshold}, backoffTimeSec = ${a3}`); return r27; } calculateFps(a0, a1, a2) { if (this.prevFramesCodec[a0] !== -1 && !(a1 < this.prevFramesCodec[a0]) && !(a2 < this.prevTimestamp[a0]) && !(a2 > this.prevTimestamp[a0] + 1000 * this.windowLength)) { if (a2 < this.prevTimestamp[a0] + 900) { return NaN; } const r24 = this.elapsedSeconds(a2, this.prevTimestamp[a0]); this.prevTimestamp[a0] = a2; const r29 = (a1 - this.prevFramesCodec[a0]) / r24; this.prevFramesCodec[a0] = a1; return r29; } this.prevFramesCodec[a0] = a1; this.prevTimestamp[a0] = a2; this.perUserFpsWindow[a0] = []; return NaN; } updateFps(a0, a1, a2) { L15: { if (!this.disabled && !this.streamDisabledUsers.has(a0)) { const r8 = this.calculateFps(a0, a1, a2); if (!(r8 < 0) && Number.isFinite(r8)) { this.perUserFpsWindow[a0].push(r8); if (this.perUserFpsWindow[a0].length < this.windowLength) { return void 0; } if (this.perUserFpsWindow[a0].length > this.windowLength) { this.perUserFpsWindow[a0].shift(); } if (this.perUserFpsWindow[a0].filter((item) => item < this.fpsThreshold).length >= this.fpsWindowBorderlineCount) { this.logger.info(`${a0}: detected poor network quality, turning off video`); this.streamDisabledUsers.add(a0); this.currentVideoAutoToggleState[a0] = VideoToggleState.DISABLED; dispatchAutoDisableVideo(a0, VideoToggleState.DISABLED); this.startReenableBackoffTimer(a0); } else if (this.currentVideoAutoToggleState[a0] === VideoToggleState.AUTO_PROBING) { this.currentVideoAutoToggleState[a0] = VideoToggleState.AUTO_ENABLED; this.logger.info(`acceptable conditions reached, will reset and send a AUTO_ENABLED for user ${a0}`); dispatchAutoDisableVideo(a0, VideoToggleState.AUTO_ENABLED); } if (this.probingUserId !== a0) { break L15; } this.probingUserId = void 0; this.tryReenableQueue(); break L15; } return void 0; } } } startReenableBackoffTimer(a0) { if (!this.disabled) { this.logger.info(`startReenableBackoffTimer for user ${a0}`); const r19 = this.retryBackoffCache[a0]; const { lastBackoffTime, expBackoffFactor } = r19; const expBackoffFactor2 = lastBackoffTime !== null ? expBackoffFactor > 16 ? 1 : this.elapsedSeconds(now(), lastBackoffTime) > 600 ? 1 : expBackoffFactor * 2 : 1; this.retryBackoffCache[a0] = { lastBackoffTime: now(), expBackoffFactor: expBackoffFactor2 }; const r50 = expBackoffFactor2 * this.backoffTimeSec * Durations.Millis.SECOND; this.logger.info(`starting backoff timer with time = ${r50} milliseconds`); this.timeoutIdCache[a0] = setTimeout(() => { this.queueReenable(a0); }, r50); } } queueReenable(a0) { this.enableQueue.push(a0); this.tryReenableQueue(); } tryReenableQueue() { let r14; L7: { if (!this.disabled) { if (this.probingUserId != null && this.probingUserId in this.perUserFpsWindow) { break L7; } const r9 = this.enableQueue.shift(); if (r9 != null && !this.reenableVideo(r9)) { do { r14 = this.enableQueue.shift(); if (r14 == null) { break; } } while (!this.reenableVideo(r14)); } } } } reenableVideo(a0) { let r0 = a0 in this.perUserFpsWindow; if (r0) { this.logger.info(`reenableVideo called for user ${a0} - time = ${now()}`); this.stateCleanupBeforeEnable(a0); this.currentVideoAutoToggleState[a0] = VideoToggleState.AUTO_PROBING; this.probingUserId = a0; dispatchAutoDisableVideo(a0, VideoToggleState.AUTO_PROBING); r0 = true; } return r0; } elapsedSeconds(a0, a1) { return (a0 - a1) / 1000; } stateCleanupBeforeEnable(a0) { this.logger.info("VideoHealthManager::stateCleanupBeforeEnable"); this.perUserFpsWindow[a0] = []; this.prevFramesCodec[a0] = -1; this.streamDisabledUsers.delete(a0); } getCurrentVideoToggleState(a0) { return this.currentVideoAutoToggleState[a0]; } createUser(a0) { this.logger.info(`VideoHealthManager::createUser ${a0}`); if (!(a0 in this.perUserFpsWindow)) { this.perUserFpsWindow[a0] = []; this.prevFramesCodec[a0] = -1; this.currentVideoAutoToggleState[a0] = VideoToggleState.NONE; this.retryBackoffCache[a0] = { lastBackoffTime: null, expBackoffFactor: 1 }; } } deleteUser(a0) { this.logger.info(`VideoHealthManager::deleteUser ${a0}`); delete this.perUserFpsWindow[a0]; delete this.prevFramesCodec[a0]; delete this.retryBackoffCache[a0]; delete this.currentVideoAutoToggleState[a0]; this.streamDisabledUsers.delete(a0); if (a0 === this.probingUserId) { this.probingUserId = void 0; this.tryReenableQueue(); } clearTimeout(this.timeoutIdCache[a0]); delete this.timeoutIdCache[a0]; } disable() { this.disabled = true; const perUserFpsWindow = this.perUserFpsWindow; for (const key in perUserFpsWindow) { this.deleteUser(key); } } }