Files
discord-decomp/lib/VideoHealthManager.tsx
T
2026-11-01 12:12:55 +02:00

197 lines
6.8 KiB
TypeScript

// 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);
}
}
}