@voltkit-rtc/client 0.7.3 → 0.8.0
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/CHANGELOG.md +57 -0
- package/README.md +244 -2
- package/dist/cjs/adaptive.d.ts +104 -0
- package/dist/cjs/adaptive.d.ts.map +1 -0
- package/dist/cjs/adaptive.js +263 -0
- package/dist/cjs/adaptive.js.map +1 -0
- package/dist/cjs/devices.d.ts +42 -0
- package/dist/cjs/devices.d.ts.map +1 -0
- package/dist/cjs/devices.js +56 -0
- package/dist/cjs/devices.js.map +1 -0
- package/dist/cjs/diagnostics.d.ts +65 -0
- package/dist/cjs/diagnostics.d.ts.map +1 -0
- package/dist/cjs/diagnostics.js +69 -0
- package/dist/cjs/diagnostics.js.map +1 -0
- package/dist/cjs/fallback.d.ts +81 -0
- package/dist/cjs/fallback.d.ts.map +1 -0
- package/dist/cjs/fallback.js +148 -0
- package/dist/cjs/fallback.js.map +1 -0
- package/dist/cjs/health.d.ts +70 -0
- package/dist/cjs/health.d.ts.map +1 -0
- package/dist/cjs/health.js +116 -0
- package/dist/cjs/health.js.map +1 -0
- package/dist/cjs/index.d.ts +339 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1098 -26
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/link.d.ts +99 -0
- package/dist/cjs/link.d.ts.map +1 -0
- package/dist/cjs/link.js +219 -0
- package/dist/cjs/link.js.map +1 -0
- package/dist/cjs/preflight.d.ts +79 -0
- package/dist/cjs/preflight.d.ts.map +1 -0
- package/dist/cjs/preflight.js +159 -0
- package/dist/cjs/preflight.js.map +1 -0
- package/dist/cjs/speakers.d.ts +34 -0
- package/dist/cjs/speakers.d.ts.map +1 -0
- package/dist/cjs/speakers.js +71 -0
- package/dist/cjs/speakers.js.map +1 -0
- package/dist/esm/adaptive.d.ts +104 -0
- package/dist/esm/adaptive.d.ts.map +1 -0
- package/dist/esm/adaptive.js +256 -0
- package/dist/esm/adaptive.js.map +1 -0
- package/dist/esm/devices.d.ts +42 -0
- package/dist/esm/devices.d.ts.map +1 -0
- package/dist/esm/devices.js +50 -0
- package/dist/esm/devices.js.map +1 -0
- package/dist/esm/diagnostics.d.ts +65 -0
- package/dist/esm/diagnostics.d.ts.map +1 -0
- package/dist/esm/diagnostics.js +65 -0
- package/dist/esm/diagnostics.js.map +1 -0
- package/dist/esm/fallback.d.ts +81 -0
- package/dist/esm/fallback.d.ts.map +1 -0
- package/dist/esm/fallback.js +142 -0
- package/dist/esm/fallback.js.map +1 -0
- package/dist/esm/health.d.ts +70 -0
- package/dist/esm/health.d.ts.map +1 -0
- package/dist/esm/health.js +111 -0
- package/dist/esm/health.js.map +1 -0
- package/dist/esm/index.d.ts +339 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1096 -26
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/link.d.ts +99 -0
- package/dist/esm/link.d.ts.map +1 -0
- package/dist/esm/link.js +214 -0
- package/dist/esm/link.js.map +1 -0
- package/dist/esm/preflight.d.ts +79 -0
- package/dist/esm/preflight.d.ts.map +1 -0
- package/dist/esm/preflight.js +153 -0
- package/dist/esm/preflight.js.map +1 -0
- package/dist/esm/speakers.d.ts +34 -0
- package/dist/esm/speakers.d.ts.map +1 -0
- package/dist/esm/speakers.js +67 -0
- package/dist/esm/speakers.js.map +1 -0
- package/package.json +1 -1
- package/src/adaptive.ts +295 -0
- package/src/devices.ts +70 -0
- package/src/diagnostics.ts +94 -0
- package/src/fallback.ts +169 -0
- package/src/health.ts +127 -0
- package/src/index.ts +1097 -27
- package/src/link.ts +255 -0
- package/src/preflight.ts +191 -0
- package/src/speakers.ts +66 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AudioProtection = exports.RELAPSE_MS = exports.MAX_HOLD_MS = exports.FIRST_HOLD_MS = exports.MIN_RECOVER_PACKETS = exports.MIN_PACKETS = exports.RECOVER_SAMPLES = exports.FALLBACK_SAMPLES = exports.RECOVER_LOSS = exports.FALLBACK_LOSS = exports.PROTECT_SAMPLE_MS = void 0;
|
|
4
|
+
exports.readInboundAudio = readInboundAudio;
|
|
5
|
+
exports.readOutboundAudio = readOutboundAudio;
|
|
6
|
+
/** How often the received audio is sampled while the fallback could be needed. */
|
|
7
|
+
exports.PROTECT_SAMPLE_MS = 3000;
|
|
8
|
+
/** This share of the audio lost (RTP sequence gaps) over the window: fall back. */
|
|
9
|
+
exports.FALLBACK_LOSS = 0.25;
|
|
10
|
+
/** Below this share lost over the recovery window: video may come back. */
|
|
11
|
+
exports.RECOVER_LOSS = 0.05;
|
|
12
|
+
/** Samples judged for falling back (15 s at 3 s) -- at least 3 of them. */
|
|
13
|
+
exports.FALLBACK_SAMPLES = 5;
|
|
14
|
+
/** Samples judged for coming back (12 s). */
|
|
15
|
+
exports.RECOVER_SAMPLES = 4;
|
|
16
|
+
/** Fewer audio packets than this over the fallback window are too few to judge. */
|
|
17
|
+
exports.MIN_PACKETS = 150;
|
|
18
|
+
/** Fewer than this over the recovery window: nobody spoke enough to tell. */
|
|
19
|
+
exports.MIN_RECOVER_PACKETS = 30;
|
|
20
|
+
/** The first fallback lasts at least this long. */
|
|
21
|
+
exports.FIRST_HOLD_MS = 20_000;
|
|
22
|
+
/** A relapse doubles the hold, up to this. */
|
|
23
|
+
exports.MAX_HOLD_MS = 160_000;
|
|
24
|
+
/** Falling back again within this long of coming back is a relapse. */
|
|
25
|
+
exports.RELAPSE_MS = 120_000;
|
|
26
|
+
const num = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : 0);
|
|
27
|
+
/** The received-audio counters of a receive transport's report, summed over its audio streams. */
|
|
28
|
+
function readInboundAudio(report) {
|
|
29
|
+
let received = 0;
|
|
30
|
+
let lost = 0;
|
|
31
|
+
for (const e of report) {
|
|
32
|
+
if (typeof e !== 'object' || e === null || e.type !== 'inbound-rtp')
|
|
33
|
+
continue;
|
|
34
|
+
if (e.kind !== 'audio' && e.mediaType !== 'audio')
|
|
35
|
+
continue;
|
|
36
|
+
received += num(e.packetsReceived);
|
|
37
|
+
lost += Math.max(0, num(e.packetsLost));
|
|
38
|
+
}
|
|
39
|
+
return { received, lost };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The sent-audio counters of a send transport's report: packets sent, and how many of them the server reported
|
|
43
|
+
* lost (its RTCP receiver reports). For the publisher side (Agora's setLocalPublishFallbackOption): `received` is what
|
|
44
|
+
* got through.
|
|
45
|
+
*/
|
|
46
|
+
function readOutboundAudio(report) {
|
|
47
|
+
let sent = 0;
|
|
48
|
+
let lost = 0;
|
|
49
|
+
for (const e of report) {
|
|
50
|
+
if (typeof e !== 'object' || e === null)
|
|
51
|
+
continue;
|
|
52
|
+
const audio = e.kind === 'audio' || e.mediaType === 'audio';
|
|
53
|
+
if (!audio)
|
|
54
|
+
continue;
|
|
55
|
+
if (e.type === 'outbound-rtp')
|
|
56
|
+
sent += num(e.packetsSent);
|
|
57
|
+
else if (e.type === 'remote-inbound-rtp')
|
|
58
|
+
lost += Math.max(0, num(e.packetsLost));
|
|
59
|
+
}
|
|
60
|
+
return { received: Math.max(0, sent - lost), lost };
|
|
61
|
+
}
|
|
62
|
+
/** Decides when to fall back to audio only and when video may come back. */
|
|
63
|
+
class AudioProtection {
|
|
64
|
+
onChange;
|
|
65
|
+
now;
|
|
66
|
+
samples = [];
|
|
67
|
+
previous = null;
|
|
68
|
+
activeSince = null;
|
|
69
|
+
hold = exports.FIRST_HOLD_MS;
|
|
70
|
+
recoveredAt = null;
|
|
71
|
+
constructor(onChange, now = Date.now) {
|
|
72
|
+
this.onChange = onChange;
|
|
73
|
+
this.now = now;
|
|
74
|
+
}
|
|
75
|
+
/** Whether remote camera videos are held back now. */
|
|
76
|
+
get active() {
|
|
77
|
+
return this.activeSince !== null;
|
|
78
|
+
}
|
|
79
|
+
/** How long the current (or next first) fallback holds, in ms. */
|
|
80
|
+
get holdMs() {
|
|
81
|
+
return this.hold;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* One reading of the cumulative counters. They only go up on one transport; lower ones are a new transport, and
|
|
85
|
+
* judging starts over from them.
|
|
86
|
+
*/
|
|
87
|
+
feed(counters) {
|
|
88
|
+
const before = this.previous;
|
|
89
|
+
this.previous = counters;
|
|
90
|
+
if (before === null || counters.received < before.received || counters.lost < before.lost) {
|
|
91
|
+
this.samples = [];
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.samples.push({ received: counters.received - before.received, lost: counters.lost - before.lost });
|
|
95
|
+
if (this.samples.length > Math.max(exports.FALLBACK_SAMPLES, exports.RECOVER_SAMPLES))
|
|
96
|
+
this.samples.shift();
|
|
97
|
+
if (this.activeSince === null) {
|
|
98
|
+
const window = this.samples.slice(-exports.FALLBACK_SAMPLES);
|
|
99
|
+
if (window.length < 3)
|
|
100
|
+
return;
|
|
101
|
+
const { total, lost } = sum(window);
|
|
102
|
+
if (total >= exports.MIN_PACKETS && lost / total >= exports.FALLBACK_LOSS)
|
|
103
|
+
this.activate();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (this.now() - this.activeSince < this.hold || this.samples.length < exports.RECOVER_SAMPLES)
|
|
107
|
+
return;
|
|
108
|
+
const { total, lost } = sum(this.samples.slice(-exports.RECOVER_SAMPLES));
|
|
109
|
+
if (total < exports.MIN_RECOVER_PACKETS || lost / total < exports.RECOVER_LOSS)
|
|
110
|
+
this.recover();
|
|
111
|
+
}
|
|
112
|
+
/** Come back now (the app turned the fallback off). */
|
|
113
|
+
release() {
|
|
114
|
+
if (this.activeSince !== null)
|
|
115
|
+
this.recover();
|
|
116
|
+
}
|
|
117
|
+
/** The transports went away (reconnect, close): forget everything, without telling. */
|
|
118
|
+
reset() {
|
|
119
|
+
this.activeSince = null;
|
|
120
|
+
this.samples = [];
|
|
121
|
+
this.previous = null;
|
|
122
|
+
}
|
|
123
|
+
activate() {
|
|
124
|
+
const now = this.now();
|
|
125
|
+
const relapse = this.recoveredAt !== null && now - this.recoveredAt < exports.RELAPSE_MS;
|
|
126
|
+
this.hold = relapse ? Math.min(this.hold * 2, exports.MAX_HOLD_MS) : exports.FIRST_HOLD_MS;
|
|
127
|
+
this.activeSince = now;
|
|
128
|
+
this.samples = [];
|
|
129
|
+
this.onChange(true);
|
|
130
|
+
}
|
|
131
|
+
recover() {
|
|
132
|
+
this.activeSince = null;
|
|
133
|
+
this.recoveredAt = this.now();
|
|
134
|
+
this.samples = [];
|
|
135
|
+
this.onChange(false);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.AudioProtection = AudioProtection;
|
|
139
|
+
function sum(samples) {
|
|
140
|
+
let total = 0;
|
|
141
|
+
let lost = 0;
|
|
142
|
+
for (const s of samples) {
|
|
143
|
+
total += s.received + s.lost;
|
|
144
|
+
lost += s.lost;
|
|
145
|
+
}
|
|
146
|
+
return { total, lost };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../src/fallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media health warnings (docs/implementation-progress/RTC_PERF_PROGRESS.md K1).
|
|
3
|
+
*
|
|
4
|
+
* - Agora's `exception` event pairs each problem with its recovery: FRAMERATE_INPUT_TOO_LOW / _RECOVER (the camera
|
|
5
|
+
* delivers too few frames), AUDIO_INPUT_LEVEL_TOO_LOW / _RECOVER, ...
|
|
6
|
+
* - LiveKit's `AudioSilenceDetected`: a microphone that delivers nothing but silence (a hardware mute switch, a
|
|
7
|
+
* broken device) right after it starts.
|
|
8
|
+
* - LiveKit watches the track's `mute` event (the OS took the device: a phone call, another app) and reacts after 5 s.
|
|
9
|
+
*
|
|
10
|
+
* Each warning is reported once when it starts (`active: true`) and once when it clears (`active: false`).
|
|
11
|
+
* Pure: the decisions take samples.
|
|
12
|
+
*/
|
|
13
|
+
export type MediaWarningCode =
|
|
14
|
+
/** The microphone is on but delivers exact digital silence: a mute switch, a broken or wrongly chosen device. */
|
|
15
|
+
'mic-silent'
|
|
16
|
+
/** The camera delivers far fewer frames than asked: usually a dark room (the camera exposes longer), or a busy device. */
|
|
17
|
+
| 'camera-fps-low'
|
|
18
|
+
/** The system took the camera from this page for now (a phone call, another app, a locked screen). */
|
|
19
|
+
| 'camera-interrupted'
|
|
20
|
+
/** The system took the microphone (a phone call, another app). */
|
|
21
|
+
| 'mic-interrupted';
|
|
22
|
+
export interface MediaWarning {
|
|
23
|
+
code: MediaWarningCode;
|
|
24
|
+
/** true when it starts, false when it clears. */
|
|
25
|
+
active: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** How often the published camera and microphone are sampled while someone listens. */
|
|
28
|
+
export declare const HEALTH_SAMPLE_MS = 2000;
|
|
29
|
+
/** Samples of exact silence before mic-silent (6 s). */
|
|
30
|
+
export declare const SILENT_SAMPLES = 3;
|
|
31
|
+
/** Below this the level counts as exact digital silence (a real room's noise floor is well above it). */
|
|
32
|
+
export declare const SILENCE_LEVEL = 0.0001;
|
|
33
|
+
/** Fewer frames than this per second, when 24 or more were asked, is camera-fps-low. */
|
|
34
|
+
export declare const LOW_FPS = 15;
|
|
35
|
+
/** Samples below LOW_FPS before warning (6 s); samples at RECOVERED_FPS or more before clearing. */
|
|
36
|
+
export declare const LOW_FPS_SAMPLES = 3;
|
|
37
|
+
export declare const RECOVERED_FPS = 20;
|
|
38
|
+
export declare const RECOVER_SAMPLES = 2;
|
|
39
|
+
/** A system mute shorter than this is a glitch, not an interruption. */
|
|
40
|
+
export declare const INTERRUPTED_AFTER_MS = 2000;
|
|
41
|
+
/** One reading of the published devices. null: not published (or nothing measured). */
|
|
42
|
+
export interface HealthSample {
|
|
43
|
+
/** The microphone's level, while it is on. */
|
|
44
|
+
micLevel: number | null;
|
|
45
|
+
/** The frames per second the camera delivers, while it is on, and what was asked. */
|
|
46
|
+
cameraFps: number | null;
|
|
47
|
+
askedFps: number;
|
|
48
|
+
}
|
|
49
|
+
/** Decides mic-silent and camera-fps-low from samples. */
|
|
50
|
+
export declare class HealthDecisions {
|
|
51
|
+
private readonly report;
|
|
52
|
+
private silent;
|
|
53
|
+
private lowFps;
|
|
54
|
+
private goodFps;
|
|
55
|
+
private readonly activeNow;
|
|
56
|
+
constructor(report: (warning: MediaWarning) => void);
|
|
57
|
+
isActive(code: MediaWarningCode): boolean;
|
|
58
|
+
feed(sample: HealthSample): void;
|
|
59
|
+
/** A system interruption started or ended (decided by the caller's timer). */
|
|
60
|
+
interrupted(code: 'camera-interrupted' | 'mic-interrupted', active: boolean): void;
|
|
61
|
+
/** Everything clears (unpublished, closed): each active warning is reported cleared. */
|
|
62
|
+
clearAll(): void;
|
|
63
|
+
private set;
|
|
64
|
+
}
|
|
65
|
+
/** The microphone level and camera frame rate from a producer's statistics (media-source). */
|
|
66
|
+
export declare function readMediaSource(report: Iterable<Record<string, unknown>>): {
|
|
67
|
+
level: number | null;
|
|
68
|
+
fps: number | null;
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,gBAAgB;AAC1B,iHAAiH;AAC/G,YAAY;AACd,0HAA0H;GACxH,gBAAgB;AAClB,sGAAsG;GACpG,oBAAoB;AACtB,kEAAkE;GAChE,iBAAiB,CAAC;AAEtB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,yGAAyG;AACzG,eAAO,MAAM,aAAa,SAAS,CAAC;AACpC,wFAAwF;AACxF,eAAO,MAAM,OAAO,KAAK,CAAC;AAC1B,oGAAoG;AACpG,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qFAAqF;IACrF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0DAA0D;AAC1D,qBAAa,eAAe;IAMd,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;gBAE5B,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI;IAEpE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO;IAIzC,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IA6BhC,8EAA8E;IAC9E,WAAW,CAAC,IAAI,EAAE,oBAAoB,GAAG,iBAAiB,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAIlF,wFAAwF;IACxF,QAAQ,IAAI,IAAI;IAOhB,OAAO,CAAC,GAAG;CAMZ;AAED,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CASvH"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Media health warnings (docs/implementation-progress/RTC_PERF_PROGRESS.md K1).
|
|
4
|
+
*
|
|
5
|
+
* - Agora's `exception` event pairs each problem with its recovery: FRAMERATE_INPUT_TOO_LOW / _RECOVER (the camera
|
|
6
|
+
* delivers too few frames), AUDIO_INPUT_LEVEL_TOO_LOW / _RECOVER, ...
|
|
7
|
+
* - LiveKit's `AudioSilenceDetected`: a microphone that delivers nothing but silence (a hardware mute switch, a
|
|
8
|
+
* broken device) right after it starts.
|
|
9
|
+
* - LiveKit watches the track's `mute` event (the OS took the device: a phone call, another app) and reacts after 5 s.
|
|
10
|
+
*
|
|
11
|
+
* Each warning is reported once when it starts (`active: true`) and once when it clears (`active: false`).
|
|
12
|
+
* Pure: the decisions take samples.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.HealthDecisions = exports.INTERRUPTED_AFTER_MS = exports.RECOVER_SAMPLES = exports.RECOVERED_FPS = exports.LOW_FPS_SAMPLES = exports.LOW_FPS = exports.SILENCE_LEVEL = exports.SILENT_SAMPLES = exports.HEALTH_SAMPLE_MS = void 0;
|
|
16
|
+
exports.readMediaSource = readMediaSource;
|
|
17
|
+
/** How often the published camera and microphone are sampled while someone listens. */
|
|
18
|
+
exports.HEALTH_SAMPLE_MS = 2000;
|
|
19
|
+
/** Samples of exact silence before mic-silent (6 s). */
|
|
20
|
+
exports.SILENT_SAMPLES = 3;
|
|
21
|
+
/** Below this the level counts as exact digital silence (a real room's noise floor is well above it). */
|
|
22
|
+
exports.SILENCE_LEVEL = 0.0001;
|
|
23
|
+
/** Fewer frames than this per second, when 24 or more were asked, is camera-fps-low. */
|
|
24
|
+
exports.LOW_FPS = 15;
|
|
25
|
+
/** Samples below LOW_FPS before warning (6 s); samples at RECOVERED_FPS or more before clearing. */
|
|
26
|
+
exports.LOW_FPS_SAMPLES = 3;
|
|
27
|
+
exports.RECOVERED_FPS = 20;
|
|
28
|
+
exports.RECOVER_SAMPLES = 2;
|
|
29
|
+
/** A system mute shorter than this is a glitch, not an interruption. */
|
|
30
|
+
exports.INTERRUPTED_AFTER_MS = 2000;
|
|
31
|
+
/** Decides mic-silent and camera-fps-low from samples. */
|
|
32
|
+
class HealthDecisions {
|
|
33
|
+
report;
|
|
34
|
+
silent = 0;
|
|
35
|
+
lowFps = 0;
|
|
36
|
+
goodFps = 0;
|
|
37
|
+
activeNow = new Set();
|
|
38
|
+
constructor(report) {
|
|
39
|
+
this.report = report;
|
|
40
|
+
}
|
|
41
|
+
isActive(code) {
|
|
42
|
+
return this.activeNow.has(code);
|
|
43
|
+
}
|
|
44
|
+
feed(sample) {
|
|
45
|
+
// mic-silent: exact silence held; any sound clears it. Off or unpublished: not a warning.
|
|
46
|
+
if (sample.micLevel === null) {
|
|
47
|
+
this.silent = 0;
|
|
48
|
+
this.set('mic-silent', false);
|
|
49
|
+
}
|
|
50
|
+
else if (sample.micLevel <= exports.SILENCE_LEVEL) {
|
|
51
|
+
this.silent += 1;
|
|
52
|
+
if (this.silent >= exports.SILENT_SAMPLES)
|
|
53
|
+
this.set('mic-silent', true);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.silent = 0;
|
|
57
|
+
this.set('mic-silent', false);
|
|
58
|
+
}
|
|
59
|
+
// camera-fps-low: only when a smooth rate was asked; cleared once it holds again.
|
|
60
|
+
if (sample.cameraFps === null || sample.askedFps < 24) {
|
|
61
|
+
this.lowFps = 0;
|
|
62
|
+
this.goodFps = 0;
|
|
63
|
+
this.set('camera-fps-low', false);
|
|
64
|
+
}
|
|
65
|
+
else if (sample.cameraFps < exports.LOW_FPS) {
|
|
66
|
+
this.goodFps = 0;
|
|
67
|
+
this.lowFps += 1;
|
|
68
|
+
if (this.lowFps >= exports.LOW_FPS_SAMPLES)
|
|
69
|
+
this.set('camera-fps-low', true);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.lowFps = 0;
|
|
73
|
+
if (sample.cameraFps >= exports.RECOVERED_FPS)
|
|
74
|
+
this.goodFps += 1;
|
|
75
|
+
if (this.goodFps >= exports.RECOVER_SAMPLES)
|
|
76
|
+
this.set('camera-fps-low', false);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** A system interruption started or ended (decided by the caller's timer). */
|
|
80
|
+
interrupted(code, active) {
|
|
81
|
+
this.set(code, active);
|
|
82
|
+
}
|
|
83
|
+
/** Everything clears (unpublished, closed): each active warning is reported cleared. */
|
|
84
|
+
clearAll() {
|
|
85
|
+
for (const code of [...this.activeNow])
|
|
86
|
+
this.set(code, false);
|
|
87
|
+
this.silent = 0;
|
|
88
|
+
this.lowFps = 0;
|
|
89
|
+
this.goodFps = 0;
|
|
90
|
+
}
|
|
91
|
+
set(code, active) {
|
|
92
|
+
if (this.activeNow.has(code) === active)
|
|
93
|
+
return;
|
|
94
|
+
if (active)
|
|
95
|
+
this.activeNow.add(code);
|
|
96
|
+
else
|
|
97
|
+
this.activeNow.delete(code);
|
|
98
|
+
this.report({ code, active });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.HealthDecisions = HealthDecisions;
|
|
102
|
+
/** The microphone level and camera frame rate from a producer's statistics (media-source). */
|
|
103
|
+
function readMediaSource(report) {
|
|
104
|
+
let level = null;
|
|
105
|
+
let fps = null;
|
|
106
|
+
for (const s of report) {
|
|
107
|
+
if (s.type !== 'media-source')
|
|
108
|
+
continue;
|
|
109
|
+
if (typeof s.audioLevel === 'number')
|
|
110
|
+
level = s.audioLevel;
|
|
111
|
+
if (typeof s.framesPerSecond === 'number')
|
|
112
|
+
fps = s.framesPerSecond;
|
|
113
|
+
}
|
|
114
|
+
return { level, fps };
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/health.ts"],"names":[],"mappings":"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"}
|