@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,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Adaptive stream: receive each remote video at the size it is shown, and not
|
|
4
|
+
* at all while nobody can see it.
|
|
5
|
+
*
|
|
6
|
+
* The app attaches the element that shows a video (`attachVideo`). From that
|
|
7
|
+
* element's size on screen (CSS pixels × devicePixelRatio, at most 2) the
|
|
8
|
+
* smallest simulcast layer that fills it sharply is chosen; while no attached
|
|
9
|
+
* element is visible (scrolled away, `display: none`, the tab in the
|
|
10
|
+
* background) the video is paused at the media server, so it costs no
|
|
11
|
+
* bandwidth and no decoding. This is what LiveKit calls adaptiveStream and
|
|
12
|
+
* Tencent TRTC "small stream + receiveWhenViewVisible": the single biggest
|
|
13
|
+
* saving on a viewer with several tiles, where every tile otherwise decodes
|
|
14
|
+
* the publisher's full-size layer.
|
|
15
|
+
*
|
|
16
|
+
* Pure decisions are exported for tests; the browser parts (observers,
|
|
17
|
+
* timers) are injected.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.AdaptiveVideos = exports.LOWER_AFTER_MS = exports.PAUSE_AFTER_MS = exports.MAX_UPSCALE = exports.MAX_PIXEL_RATIO = void 0;
|
|
21
|
+
exports.layerForBox = layerForBox;
|
|
22
|
+
exports.wantFor = wantFor;
|
|
23
|
+
exports.pageIsVisible = pageIsVisible;
|
|
24
|
+
/** Never ask for more than twice the CSS size: a 3× phone screen gains nothing visible from it. */
|
|
25
|
+
exports.MAX_PIXEL_RATIO = 2;
|
|
26
|
+
/** A layer shown at most this much bigger than it is still looks sharp: avoids a whole layer for a few pixels. */
|
|
27
|
+
exports.MAX_UPSCALE = 1.15;
|
|
28
|
+
/**
|
|
29
|
+
* The smallest layer that fills `box` sharply, or null when the layers are
|
|
30
|
+
* not known (an older publisher): then the server chooses, as before.
|
|
31
|
+
*
|
|
32
|
+
* With `cover` the picture is scaled until it fills the box (a part is
|
|
33
|
+
* cropped), with `contain` until it fits. Either way the scale is measured
|
|
34
|
+
* against each layer's own size, so a portrait layer list works too.
|
|
35
|
+
*/
|
|
36
|
+
function layerForBox(layers, box, pixelRatio = 1) {
|
|
37
|
+
const usable = layers.filter((l) => l.width > 0 && l.height > 0).sort((a, b) => a.width * a.height - b.width * b.height);
|
|
38
|
+
if (!usable.length || box.width <= 0 || box.height <= 0)
|
|
39
|
+
return null;
|
|
40
|
+
const ratio = Math.min(exports.MAX_PIXEL_RATIO, Math.max(1, pixelRatio || 1));
|
|
41
|
+
const w = box.width * ratio;
|
|
42
|
+
const h = box.height * ratio;
|
|
43
|
+
for (const layer of usable) {
|
|
44
|
+
const scale = box.fit === 'cover' ? Math.max(w / layer.width, h / layer.height) : Math.min(w / layer.width, h / layer.height);
|
|
45
|
+
// The layer is at most slightly upscaled on screen.
|
|
46
|
+
if (scale <= exports.MAX_UPSCALE)
|
|
47
|
+
return layer.spatialLayer;
|
|
48
|
+
}
|
|
49
|
+
return usable[usable.length - 1].spatialLayer;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The largest view among the visible ones decides; any visible view keeps the video flowing. A view not measured
|
|
53
|
+
* yet (just attached, before layout) counts as shown at the server's choice, so attaching never pauses a video
|
|
54
|
+
* for the moment before the browser has sized the element.
|
|
55
|
+
*/
|
|
56
|
+
function wantFor(views, layers, pixelRatio, pageVisible) {
|
|
57
|
+
if (pageVisible && views.some((v) => v.measured === false))
|
|
58
|
+
return { visible: true, spatialLayer: null };
|
|
59
|
+
const shown = pageVisible ? views.filter((v) => v.visible && v.box.width > 0 && v.box.height > 0) : [];
|
|
60
|
+
if (!shown.length)
|
|
61
|
+
return { visible: false, spatialLayer: null };
|
|
62
|
+
let best = null;
|
|
63
|
+
for (const view of shown) {
|
|
64
|
+
const layer = layerForBox(layers, view.box, pixelRatio);
|
|
65
|
+
if (layer === null)
|
|
66
|
+
return { visible: true, spatialLayer: null };
|
|
67
|
+
best = best === null ? layer : Math.max(best, layer);
|
|
68
|
+
}
|
|
69
|
+
return { visible: true, spatialLayer: best };
|
|
70
|
+
}
|
|
71
|
+
/** Pause a hidden video only after this long, so scrolling past or a quick tab switch does not stop it. */
|
|
72
|
+
exports.PAUSE_AFTER_MS = 1500;
|
|
73
|
+
/** Go down a layer only after this long (going up is immediate): a tile shrinking for a moment keeps its picture. */
|
|
74
|
+
exports.LOWER_AFTER_MS = 1200;
|
|
75
|
+
const FLOWING = { visible: true, spatialLayer: null };
|
|
76
|
+
const sameWant = (a, b) => a !== null && a.visible === b.visible && a.spatialLayer === b.spatialLayer;
|
|
77
|
+
/**
|
|
78
|
+
* The videos being shown, by producer id. Decisions are applied through the
|
|
79
|
+
* host; going up (or becoming visible) is applied at once, going down (or
|
|
80
|
+
* hiding) after a short delay.
|
|
81
|
+
*/
|
|
82
|
+
class AdaptiveVideos {
|
|
83
|
+
host;
|
|
84
|
+
entries = new Map();
|
|
85
|
+
stopPage = null;
|
|
86
|
+
constructor(host) {
|
|
87
|
+
this.host = host;
|
|
88
|
+
}
|
|
89
|
+
/** Show `producerId` in `element`; returns the function that stops. */
|
|
90
|
+
attach(producerId, element) {
|
|
91
|
+
let entry = this.entries.get(producerId);
|
|
92
|
+
if (!entry) {
|
|
93
|
+
entry = { views: new Map(), applied: null, timer: null };
|
|
94
|
+
this.entries.set(producerId, entry);
|
|
95
|
+
}
|
|
96
|
+
if (entry.views.has(element))
|
|
97
|
+
return () => this.detach(producerId, element);
|
|
98
|
+
const view = { box: { width: 0, height: 0, fit: 'contain' }, visible: true, measured: false, stop: () => undefined };
|
|
99
|
+
entry.views.set(element, view);
|
|
100
|
+
view.stop = this.observe(element, (box, visible) => {
|
|
101
|
+
if (box) {
|
|
102
|
+
view.box = box;
|
|
103
|
+
view.measured = true;
|
|
104
|
+
}
|
|
105
|
+
if (visible !== null)
|
|
106
|
+
view.visible = visible;
|
|
107
|
+
this.update(producerId);
|
|
108
|
+
});
|
|
109
|
+
if (!this.stopPage)
|
|
110
|
+
this.stopPage = this.watchPage(() => { for (const id of this.entries.keys())
|
|
111
|
+
this.update(id); });
|
|
112
|
+
this.update(producerId);
|
|
113
|
+
return () => this.detach(producerId, element);
|
|
114
|
+
}
|
|
115
|
+
detach(producerId, element) {
|
|
116
|
+
const entry = this.entries.get(producerId);
|
|
117
|
+
const view = entry?.views.get(element);
|
|
118
|
+
if (!entry || !view)
|
|
119
|
+
return;
|
|
120
|
+
view.stop();
|
|
121
|
+
entry.views.delete(element);
|
|
122
|
+
if (entry.views.size) {
|
|
123
|
+
this.update(producerId);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Nothing shows it any more: back to how it was before (flowing, the server's choice).
|
|
127
|
+
this.clear(entry);
|
|
128
|
+
this.entries.delete(producerId);
|
|
129
|
+
if (entry.applied && !sameWant(entry.applied, { visible: true, spatialLayer: null }))
|
|
130
|
+
this.host.apply(producerId, { visible: true, spatialLayer: null });
|
|
131
|
+
if (!this.entries.size) {
|
|
132
|
+
this.stopPage?.();
|
|
133
|
+
this.stopPage = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Whether the app shows this video through attachVideo (its decisions then apply to new consumers too). */
|
|
137
|
+
has(producerId) {
|
|
138
|
+
return this.entries.has(producerId);
|
|
139
|
+
}
|
|
140
|
+
/** The decision in force, for a consumer made (or made again) after it: null when the app does not attach it. */
|
|
141
|
+
current(producerId) {
|
|
142
|
+
const entry = this.entries.get(producerId);
|
|
143
|
+
return entry ? this.decide(producerId, entry) : null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A new consumer was made with `current()` applied (its layers, and paused when hidden): record that as applied,
|
|
147
|
+
* so nothing is sent twice, and drop a pending change made for the old one.
|
|
148
|
+
*/
|
|
149
|
+
consumed(producerId) {
|
|
150
|
+
const entry = this.entries.get(producerId);
|
|
151
|
+
if (!entry)
|
|
152
|
+
return;
|
|
153
|
+
this.clear(entry);
|
|
154
|
+
entry.applied = this.decide(producerId, entry);
|
|
155
|
+
}
|
|
156
|
+
/** Stop everything (the session closed). */
|
|
157
|
+
dispose() {
|
|
158
|
+
for (const entry of this.entries.values()) {
|
|
159
|
+
this.clear(entry);
|
|
160
|
+
for (const view of entry.views.values())
|
|
161
|
+
view.stop();
|
|
162
|
+
}
|
|
163
|
+
this.entries.clear();
|
|
164
|
+
this.stopPage?.();
|
|
165
|
+
this.stopPage = null;
|
|
166
|
+
}
|
|
167
|
+
decide(producerId, entry) {
|
|
168
|
+
return wantFor([...entry.views.values()], this.host.layers(producerId), this.host.pixelRatio?.() ?? 1, this.host.pageVisible?.() ?? true);
|
|
169
|
+
}
|
|
170
|
+
update(producerId) {
|
|
171
|
+
const entry = this.entries.get(producerId);
|
|
172
|
+
if (!entry)
|
|
173
|
+
return;
|
|
174
|
+
const want = this.decide(producerId, entry);
|
|
175
|
+
// Nothing applied yet is the server's choice, flowing: asking for exactly that sends nothing.
|
|
176
|
+
if (sameWant(entry.applied ?? FLOWING, want)) {
|
|
177
|
+
this.clear(entry);
|
|
178
|
+
entry.applied ??= want;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Nothing applied yet means the video flows at the server's choice.
|
|
182
|
+
const prev = entry.applied ?? { visible: true, spatialLayer: null };
|
|
183
|
+
// Hiding, or a smaller layer than one applied, waits; anything else (showing, a bigger layer, a first layer) is at once.
|
|
184
|
+
const lower = (prev.visible && !want.visible) ||
|
|
185
|
+
(want.visible && prev.visible && want.spatialLayer !== null && prev.spatialLayer !== null && want.spatialLayer < prev.spatialLayer);
|
|
186
|
+
// Already waiting to lower: a repeated report must not restart the wait; it applies what is wanted when it runs out.
|
|
187
|
+
if (lower && entry.timer !== null)
|
|
188
|
+
return;
|
|
189
|
+
this.clear(entry);
|
|
190
|
+
if (!lower) {
|
|
191
|
+
entry.applied = want;
|
|
192
|
+
this.host.apply(producerId, want);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const delay = want.visible ? exports.LOWER_AFTER_MS : exports.PAUSE_AFTER_MS;
|
|
196
|
+
entry.timer = (this.host.setTimer ?? ((fn, ms) => setTimeout(fn, ms)))(() => {
|
|
197
|
+
entry.timer = null;
|
|
198
|
+
const now = this.decide(producerId, entry);
|
|
199
|
+
if (sameWant(entry.applied, now))
|
|
200
|
+
return;
|
|
201
|
+
entry.applied = now;
|
|
202
|
+
this.host.apply(producerId, now);
|
|
203
|
+
}, delay);
|
|
204
|
+
}
|
|
205
|
+
clear(entry) {
|
|
206
|
+
if (entry.timer !== null)
|
|
207
|
+
(this.host.clearTimer ?? ((h) => clearTimeout(h)))(entry.timer);
|
|
208
|
+
entry.timer = null;
|
|
209
|
+
}
|
|
210
|
+
observe(element, onChange) {
|
|
211
|
+
if (this.host.observe)
|
|
212
|
+
return this.host.observe(element, onChange);
|
|
213
|
+
return observeElement(element, onChange);
|
|
214
|
+
}
|
|
215
|
+
watchPage(listener) {
|
|
216
|
+
if (this.host.onPageVisibility)
|
|
217
|
+
return this.host.onPageVisibility(listener);
|
|
218
|
+
if (typeof document === 'undefined')
|
|
219
|
+
return () => undefined;
|
|
220
|
+
document.addEventListener('visibilitychange', listener);
|
|
221
|
+
return () => document.removeEventListener('visibilitychange', listener);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.AdaptiveVideos = AdaptiveVideos;
|
|
225
|
+
/** How an element fits its picture: a video's object-fit, else contain. */
|
|
226
|
+
function fitOf(element) {
|
|
227
|
+
try {
|
|
228
|
+
const fit = typeof getComputedStyle === 'function' ? getComputedStyle(element).objectFit : '';
|
|
229
|
+
return fit === 'cover' || fit === 'fill' ? 'cover' : 'contain';
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return 'contain';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** The browser's observers: size (ResizeObserver) and visibility on screen (IntersectionObserver). */
|
|
236
|
+
function observeElement(element, onChange) {
|
|
237
|
+
const stops = [];
|
|
238
|
+
const measure = () => {
|
|
239
|
+
const rect = element.getBoundingClientRect();
|
|
240
|
+
onChange({ width: rect.width, height: rect.height, fit: fitOf(element) }, null);
|
|
241
|
+
};
|
|
242
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
243
|
+
const resize = new ResizeObserver(() => measure());
|
|
244
|
+
resize.observe(element);
|
|
245
|
+
stops.push(() => resize.disconnect());
|
|
246
|
+
}
|
|
247
|
+
if (typeof IntersectionObserver !== 'undefined') {
|
|
248
|
+
const seen = new IntersectionObserver((entries) => {
|
|
249
|
+
const last = entries[entries.length - 1];
|
|
250
|
+
if (last)
|
|
251
|
+
onChange(null, last.isIntersecting);
|
|
252
|
+
});
|
|
253
|
+
seen.observe(element);
|
|
254
|
+
stops.push(() => seen.disconnect());
|
|
255
|
+
}
|
|
256
|
+
measure();
|
|
257
|
+
return () => stops.forEach((stop) => stop());
|
|
258
|
+
}
|
|
259
|
+
/** The page is visible (not a background tab). */
|
|
260
|
+
function pageIsVisible() {
|
|
261
|
+
return typeof document === 'undefined' || document.visibilityState !== 'hidden';
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=adaptive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive.js","sourceRoot":"","sources":["../../src/adaptive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cameras, microphones and speakers (docs/implementation-progress/RTC_PERF_PROGRESS.md I1).
|
|
3
|
+
*
|
|
4
|
+
* What the big SDKs do, read from their code:
|
|
5
|
+
* - Amazon Chime (`DefaultDeviceController`): lists devices by kind, watches `devicechange` and tells observers
|
|
6
|
+
* which lists changed; a microphone that ends is replaced so audio keeps flowing, a camera that ends is stopped and
|
|
7
|
+
* reported (`audioInputStreamEnded` / `videoInputStreamEnded`) -- never swapped for another camera on its own.
|
|
8
|
+
* - LiveKit (`Room.handleDeviceChange`, `switchActiveDevice`): switches the published track to a chosen device; when
|
|
9
|
+
* the active audio device disappears it moves to the first available one.
|
|
10
|
+
* - Tencent TRTC: `DEVICE_CHANGED {type, action: add | remove | active}`.
|
|
11
|
+
*
|
|
12
|
+
* This module holds the pure parts: comparing device lists and building the constraints for a chosen device.
|
|
13
|
+
*/
|
|
14
|
+
export type DeviceKind = 'audioinput' | 'videoinput' | 'audiooutput';
|
|
15
|
+
/** A camera, microphone or speaker, as the browser lists it. Labels are empty until a permission was granted. */
|
|
16
|
+
export interface MediaDevice {
|
|
17
|
+
deviceId: string;
|
|
18
|
+
groupId: string;
|
|
19
|
+
kind: DeviceKind;
|
|
20
|
+
label: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DevicesChangedEvent {
|
|
23
|
+
/** Every device now. */
|
|
24
|
+
devices: MediaDevice[];
|
|
25
|
+
added: MediaDevice[];
|
|
26
|
+
removed: MediaDevice[];
|
|
27
|
+
}
|
|
28
|
+
/** The browser's device list, optionally of one kind. Empty outside a browser. */
|
|
29
|
+
export declare function enumerate(kind?: DeviceKind): Promise<MediaDevice[]>;
|
|
30
|
+
/** What appeared and what went between two lists. */
|
|
31
|
+
export declare function diffDevices(before: readonly MediaDevice[], after: readonly MediaDevice[]): {
|
|
32
|
+
added: MediaDevice[];
|
|
33
|
+
removed: MediaDevice[];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The constraints for capturing `deviceId` (or the system default when null), keeping everything else the app asked
|
|
37
|
+
* for: size, frame rate, echo cancellation. `base` is what publish was given for that kind.
|
|
38
|
+
*/
|
|
39
|
+
export declare function constraintsFor(base: boolean | MediaTrackConstraints | undefined, deviceId: string | null): MediaTrackConstraints;
|
|
40
|
+
/** The device a live track captures, when it is a device capture at all (a canvas or a processed track has none). */
|
|
41
|
+
export declare function deviceIdOf(track: MediaStreamTrack | null | undefined): string | null;
|
|
42
|
+
//# sourceMappingURL=devices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../../src/devices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC;AAErE,iHAAiH;AACjH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,kFAAkF;AAClF,wBAAsB,SAAS,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAQzE;AAID,qDAAqD;AACrD,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAO3I;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,qBAAqB,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,CAKhI;AAED,qHAAqH;AACrH,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGpF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cameras, microphones and speakers (docs/implementation-progress/RTC_PERF_PROGRESS.md I1).
|
|
4
|
+
*
|
|
5
|
+
* What the big SDKs do, read from their code:
|
|
6
|
+
* - Amazon Chime (`DefaultDeviceController`): lists devices by kind, watches `devicechange` and tells observers
|
|
7
|
+
* which lists changed; a microphone that ends is replaced so audio keeps flowing, a camera that ends is stopped and
|
|
8
|
+
* reported (`audioInputStreamEnded` / `videoInputStreamEnded`) -- never swapped for another camera on its own.
|
|
9
|
+
* - LiveKit (`Room.handleDeviceChange`, `switchActiveDevice`): switches the published track to a chosen device; when
|
|
10
|
+
* the active audio device disappears it moves to the first available one.
|
|
11
|
+
* - Tencent TRTC: `DEVICE_CHANGED {type, action: add | remove | active}`.
|
|
12
|
+
*
|
|
13
|
+
* This module holds the pure parts: comparing device lists and building the constraints for a chosen device.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.enumerate = enumerate;
|
|
17
|
+
exports.diffDevices = diffDevices;
|
|
18
|
+
exports.constraintsFor = constraintsFor;
|
|
19
|
+
exports.deviceIdOf = deviceIdOf;
|
|
20
|
+
/** The browser's device list, optionally of one kind. Empty outside a browser. */
|
|
21
|
+
async function enumerate(kind) {
|
|
22
|
+
const md = typeof navigator !== 'undefined' ? navigator.mediaDevices : undefined;
|
|
23
|
+
if (!md?.enumerateDevices)
|
|
24
|
+
return [];
|
|
25
|
+
const all = await md.enumerateDevices();
|
|
26
|
+
return all
|
|
27
|
+
.filter((d) => d.kind === 'audioinput' || d.kind === 'videoinput' || d.kind === 'audiooutput')
|
|
28
|
+
.filter((d) => !kind || d.kind === kind)
|
|
29
|
+
.map((d) => ({ deviceId: d.deviceId, groupId: d.groupId, kind: d.kind, label: d.label }));
|
|
30
|
+
}
|
|
31
|
+
const key = (d) => `${d.kind}:${d.deviceId}`;
|
|
32
|
+
/** What appeared and what went between two lists. */
|
|
33
|
+
function diffDevices(before, after) {
|
|
34
|
+
const was = new Set(before.map(key));
|
|
35
|
+
const now = new Set(after.map(key));
|
|
36
|
+
return {
|
|
37
|
+
added: after.filter((d) => !was.has(key(d))),
|
|
38
|
+
removed: before.filter((d) => !now.has(key(d))),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The constraints for capturing `deviceId` (or the system default when null), keeping everything else the app asked
|
|
43
|
+
* for: size, frame rate, echo cancellation. `base` is what publish was given for that kind.
|
|
44
|
+
*/
|
|
45
|
+
function constraintsFor(base, deviceId) {
|
|
46
|
+
const rest = typeof base === 'object' && base !== null ? { ...base } : {};
|
|
47
|
+
delete rest.deviceId;
|
|
48
|
+
delete rest.groupId;
|
|
49
|
+
return deviceId === null ? rest : { ...rest, deviceId: { exact: deviceId } };
|
|
50
|
+
}
|
|
51
|
+
/** The device a live track captures, when it is a device capture at all (a canvas or a processed track has none). */
|
|
52
|
+
function deviceIdOf(track) {
|
|
53
|
+
const id = track?.getSettings?.().deviceId;
|
|
54
|
+
return typeof id === 'string' && id.length > 0 ? id : null;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/devices.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AAoBH,8BAQC;AAKD,kCAOC;AAMD,wCAKC;AAGD,gCAGC;AAtCD,kFAAkF;AAC3E,KAAK,UAAU,SAAS,CAAC,IAAiB;IAC/C,MAAM,EAAE,GAAG,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,CAAC,EAAE,EAAE,gBAAgB;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,gBAAgB,EAAE,CAAC;IACxC,OAAO,GAAG;SACP,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC;SAC7F,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;SACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AAC5G,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAc,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AAE1D,qDAAqD;AACrD,SAAgB,WAAW,CAAC,MAA8B,EAAE,KAA6B;IACvF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;KAChD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,IAAiD,EAAE,QAAuB;IACvG,MAAM,IAAI,GAA0B,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjG,OAAO,IAAI,CAAC,QAAQ,CAAC;IACrB,OAAO,IAAI,CAAC,OAAO,CAAC;IACpB,OAAO,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC;AAC/E,CAAC;AAED,qHAAqH;AACrH,SAAgB,UAAU,CAAC,KAA0C;IACnE,MAAM,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC;IAC3C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics for support (docs/implementation-progress/RTC_PERF_PROGRESS.md S4).
|
|
3
|
+
*
|
|
4
|
+
* Agora uploads its SDK log on request (`enableLogUpload`), Twilio sends call telemetry to Insights, Stream Video
|
|
5
|
+
* traces session events to its servers. Shipping telemetry needs a backend; what helps today is a snapshot an app can
|
|
6
|
+
* attach to a support ticket: the SDK's recent events (a ring buffer) and its state now.
|
|
7
|
+
*/
|
|
8
|
+
/** This package's version; kept equal to package.json by a test. */
|
|
9
|
+
export declare const SDK_VERSION = "0.8.0";
|
|
10
|
+
/** How many recent events the log keeps. */
|
|
11
|
+
export declare const DIAGNOSTICS_LOG_SIZE = 200;
|
|
12
|
+
export interface DiagnosticsEntry {
|
|
13
|
+
/** Unix ms. */
|
|
14
|
+
at: number;
|
|
15
|
+
event: string;
|
|
16
|
+
/** A short summary of what the event carried (no media objects). */
|
|
17
|
+
detail?: string;
|
|
18
|
+
}
|
|
19
|
+
/** A ring buffer of the SDK's recent events. */
|
|
20
|
+
export declare class DiagnosticsLog {
|
|
21
|
+
private readonly now;
|
|
22
|
+
private readonly entries;
|
|
23
|
+
constructor(now?: () => number);
|
|
24
|
+
/** Keep an emitted event, when it is one worth keeping. */
|
|
25
|
+
record(event: string, args: unknown[]): void;
|
|
26
|
+
list(): DiagnosticsEntry[];
|
|
27
|
+
}
|
|
28
|
+
export interface DiagnosticsTransport {
|
|
29
|
+
state: string;
|
|
30
|
+
path: {
|
|
31
|
+
protocol: 'udp' | 'tcp' | null;
|
|
32
|
+
relayed: boolean;
|
|
33
|
+
} | null;
|
|
34
|
+
}
|
|
35
|
+
export interface Diagnostics {
|
|
36
|
+
sdk: string;
|
|
37
|
+
at: number;
|
|
38
|
+
userAgent: string;
|
|
39
|
+
connectionState: string;
|
|
40
|
+
room: string | null;
|
|
41
|
+
identity: string | null;
|
|
42
|
+
role: string | null;
|
|
43
|
+
transports: {
|
|
44
|
+
send: DiagnosticsTransport | null;
|
|
45
|
+
recv: DiagnosticsTransport | null;
|
|
46
|
+
};
|
|
47
|
+
network: {
|
|
48
|
+
rttMs: number | null;
|
|
49
|
+
availableKbps: number | null;
|
|
50
|
+
} | null;
|
|
51
|
+
published: {
|
|
52
|
+
source: string;
|
|
53
|
+
kind: string;
|
|
54
|
+
paused: boolean;
|
|
55
|
+
}[];
|
|
56
|
+
received: {
|
|
57
|
+
identity: string;
|
|
58
|
+
source: string;
|
|
59
|
+
kind: string;
|
|
60
|
+
paused: boolean;
|
|
61
|
+
}[];
|
|
62
|
+
/** Recent events, oldest first. */
|
|
63
|
+
log: DiagnosticsEntry[];
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oEAAoE;AACpE,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAC/B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6BD,gDAAgD;AAChD,qBAAa,cAAc;IAGb,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAErB,GAAG,GAAE,MAAM,MAAiB;IAEzD,2DAA2D;IAC3D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAa5C,IAAI,IAAI,gBAAgB,EAAE;CAG3B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE;QAAE,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE;QAAE,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAA;KAAE,CAAC;IACrF,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC/D,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAChF,mCAAmC;IACnC,GAAG,EAAE,gBAAgB,EAAE,CAAC;CACzB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Diagnostics for support (docs/implementation-progress/RTC_PERF_PROGRESS.md S4).
|
|
4
|
+
*
|
|
5
|
+
* Agora uploads its SDK log on request (`enableLogUpload`), Twilio sends call telemetry to Insights, Stream Video
|
|
6
|
+
* traces session events to its servers. Shipping telemetry needs a backend; what helps today is a snapshot an app can
|
|
7
|
+
* attach to a support ticket: the SDK's recent events (a ring buffer) and its state now.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DiagnosticsLog = exports.DIAGNOSTICS_LOG_SIZE = exports.SDK_VERSION = void 0;
|
|
11
|
+
/** This package's version; kept equal to package.json by a test. */
|
|
12
|
+
exports.SDK_VERSION = '0.8.0';
|
|
13
|
+
/** How many recent events the log keeps. */
|
|
14
|
+
exports.DIAGNOSTICS_LOG_SIZE = 200;
|
|
15
|
+
/** The events worth keeping for support, and how to summarise each. */
|
|
16
|
+
const SUMMARY = {
|
|
17
|
+
connectionStateChanged: ([e]) => { const s = e; return `${s.previous} → ${s.state}`; },
|
|
18
|
+
disconnected: ([reason]) => String(reason),
|
|
19
|
+
closed: ([reason]) => String(reason),
|
|
20
|
+
mustReconnect: ([e]) => e.reason,
|
|
21
|
+
mediaReconnecting: ([e]) => { const m = e; return `${m.transportId} ${m.reconnecting ? 'recovering' : 'recovered'}`; },
|
|
22
|
+
error: ([e]) => {
|
|
23
|
+
const err = e;
|
|
24
|
+
const message = err.message ?? '';
|
|
25
|
+
// RpcError's message already starts with its code.
|
|
26
|
+
return (err.code && !message.startsWith(`${err.code}:`) ? `${err.code}: ${message}` : message).slice(0, 300);
|
|
27
|
+
},
|
|
28
|
+
streamFallback: ([e]) => { const f = e; return `${f.producerId} ${f.fallback ? 'held' : 'back'} (${f.reason})`; },
|
|
29
|
+
mediaWarning: ([e]) => { const w = e; return `${w.code} ${w.active ? 'started' : 'cleared'}`; },
|
|
30
|
+
cpuConstrained: ([e]) => String(e.constrained),
|
|
31
|
+
videoLimited: ([e]) => e.reason,
|
|
32
|
+
localTrackEnded: ([e]) => { const t = e; return `${t.kind} ${t.recovered ? 'recovered' : 'off'}`; },
|
|
33
|
+
devicesChanged: ([e]) => { const d = e; return `+${d.added?.length ?? 0} -${d.removed?.length ?? 0}`; },
|
|
34
|
+
kicked: ([e]) => e.reason,
|
|
35
|
+
roomClosed: ([e]) => e.reason,
|
|
36
|
+
usageLimit: ([e]) => e.reason,
|
|
37
|
+
autoplayFailed: () => undefined,
|
|
38
|
+
trackEnded: ([e]) => e.producerId,
|
|
39
|
+
track: ([e]) => { const t = e; return `${t.identity} ${t.source}`; },
|
|
40
|
+
};
|
|
41
|
+
/** A ring buffer of the SDK's recent events. */
|
|
42
|
+
class DiagnosticsLog {
|
|
43
|
+
now;
|
|
44
|
+
entries = [];
|
|
45
|
+
constructor(now = Date.now) {
|
|
46
|
+
this.now = now;
|
|
47
|
+
}
|
|
48
|
+
/** Keep an emitted event, when it is one worth keeping. */
|
|
49
|
+
record(event, args) {
|
|
50
|
+
const summarize = SUMMARY[event];
|
|
51
|
+
if (!summarize)
|
|
52
|
+
return;
|
|
53
|
+
let detail;
|
|
54
|
+
try {
|
|
55
|
+
detail = summarize(args);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
detail = undefined;
|
|
59
|
+
}
|
|
60
|
+
this.entries.push(detail === undefined ? { at: this.now(), event } : { at: this.now(), event, detail });
|
|
61
|
+
if (this.entries.length > exports.DIAGNOSTICS_LOG_SIZE)
|
|
62
|
+
this.entries.shift();
|
|
63
|
+
}
|
|
64
|
+
list() {
|
|
65
|
+
return this.entries.map((e) => ({ ...e }));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.DiagnosticsLog = DiagnosticsLog;
|
|
69
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio-only fallback (docs/implementation-progress/RTC_PERF_PROGRESS.md H1).
|
|
3
|
+
*
|
|
4
|
+
* When this viewer's connection is so poor that even the sound breaks up, remote camera videos are paused at the
|
|
5
|
+
* server so what bandwidth there is goes to the audio, and they come back once the connection has recovered.
|
|
6
|
+
*
|
|
7
|
+
* - Agora's `setStreamFallbackOption(uid, AUDIO_ONLY)` drops to the low stream and then to audio only, and reports
|
|
8
|
+
* `stream-fallback` (fallback / recover).
|
|
9
|
+
* - Amazon Chime watches the audio it receives (`UnusableAudioWarningConnectionHealthPolicy`): about half of the
|
|
10
|
+
* expected packets missing over 15 one-second samples suggests stopping video, with a 60 s cool-down.
|
|
11
|
+
*
|
|
12
|
+
* This follows Chime's signal -- the audio actually received -- measured as RTP loss (sequence gaps) rather than
|
|
13
|
+
* packet counts, because Opus DTX sends almost nothing during silence and a count would read silence as loss.
|
|
14
|
+
* Extreme only: {@link FALLBACK_LOSS} of the audio lost over {@link FALLBACK_SAMPLES} samples, with at least
|
|
15
|
+
* {@link MIN_PACKETS} packets to judge by. Back after a hold ({@link FIRST_HOLD_MS}, doubled for each relapse within
|
|
16
|
+
* {@link RELAPSE_MS}, up to {@link MAX_HOLD_MS}) once the loss is under {@link RECOVER_LOSS} -- or nobody spoke
|
|
17
|
+
* enough to tell, in which case video comes back and a still-bad connection falls back again, holding longer.
|
|
18
|
+
*
|
|
19
|
+
* The SFU cutting a video for bandwidth by itself is reported separately, by the server (`reason: 'bandwidth'`).
|
|
20
|
+
* Pure: the decisions take counters and a clock.
|
|
21
|
+
*/
|
|
22
|
+
import type { StatsEntry } from './stats.js';
|
|
23
|
+
/** How often the received audio is sampled while the fallback could be needed. */
|
|
24
|
+
export declare const PROTECT_SAMPLE_MS = 3000;
|
|
25
|
+
/** This share of the audio lost (RTP sequence gaps) over the window: fall back. */
|
|
26
|
+
export declare const FALLBACK_LOSS = 0.25;
|
|
27
|
+
/** Below this share lost over the recovery window: video may come back. */
|
|
28
|
+
export declare const RECOVER_LOSS = 0.05;
|
|
29
|
+
/** Samples judged for falling back (15 s at 3 s) -- at least 3 of them. */
|
|
30
|
+
export declare const FALLBACK_SAMPLES = 5;
|
|
31
|
+
/** Samples judged for coming back (12 s). */
|
|
32
|
+
export declare const RECOVER_SAMPLES = 4;
|
|
33
|
+
/** Fewer audio packets than this over the fallback window are too few to judge. */
|
|
34
|
+
export declare const MIN_PACKETS = 150;
|
|
35
|
+
/** Fewer than this over the recovery window: nobody spoke enough to tell. */
|
|
36
|
+
export declare const MIN_RECOVER_PACKETS = 30;
|
|
37
|
+
/** The first fallback lasts at least this long. */
|
|
38
|
+
export declare const FIRST_HOLD_MS = 20000;
|
|
39
|
+
/** A relapse doubles the hold, up to this. */
|
|
40
|
+
export declare const MAX_HOLD_MS = 160000;
|
|
41
|
+
/** Falling back again within this long of coming back is a relapse. */
|
|
42
|
+
export declare const RELAPSE_MS = 120000;
|
|
43
|
+
export interface AudioCounters {
|
|
44
|
+
received: number;
|
|
45
|
+
lost: number;
|
|
46
|
+
}
|
|
47
|
+
/** The received-audio counters of a receive transport's report, summed over its audio streams. */
|
|
48
|
+
export declare function readInboundAudio(report: Iterable<StatsEntry>): AudioCounters;
|
|
49
|
+
/**
|
|
50
|
+
* The sent-audio counters of a send transport's report: packets sent, and how many of them the server reported
|
|
51
|
+
* lost (its RTCP receiver reports). For the publisher side (Agora's setLocalPublishFallbackOption): `received` is what
|
|
52
|
+
* got through.
|
|
53
|
+
*/
|
|
54
|
+
export declare function readOutboundAudio(report: Iterable<StatsEntry>): AudioCounters;
|
|
55
|
+
/** Decides when to fall back to audio only and when video may come back. */
|
|
56
|
+
export declare class AudioProtection {
|
|
57
|
+
private readonly onChange;
|
|
58
|
+
private readonly now;
|
|
59
|
+
private samples;
|
|
60
|
+
private previous;
|
|
61
|
+
private activeSince;
|
|
62
|
+
private hold;
|
|
63
|
+
private recoveredAt;
|
|
64
|
+
constructor(onChange: (active: boolean) => void, now?: () => number);
|
|
65
|
+
/** Whether remote camera videos are held back now. */
|
|
66
|
+
get active(): boolean;
|
|
67
|
+
/** How long the current (or next first) fallback holds, in ms. */
|
|
68
|
+
get holdMs(): number;
|
|
69
|
+
/**
|
|
70
|
+
* One reading of the cumulative counters. They only go up on one transport; lower ones are a new transport, and
|
|
71
|
+
* judging starts over from them.
|
|
72
|
+
*/
|
|
73
|
+
feed(counters: AudioCounters): void;
|
|
74
|
+
/** Come back now (the app turned the fallback off). */
|
|
75
|
+
release(): void;
|
|
76
|
+
/** The transports went away (reconnect, close): forget everything, without telling. */
|
|
77
|
+
reset(): void;
|
|
78
|
+
private activate;
|
|
79
|
+
private recover;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,mFAAmF;AACnF,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,2EAA2E;AAC3E,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,6CAA6C;AAC7C,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,mFAAmF;AACnF,eAAO,MAAM,WAAW,MAAM,CAAC;AAC/B,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,mDAAmD;AACnD,eAAO,MAAM,aAAa,QAAS,CAAC;AACpC,8CAA8C;AAC9C,eAAO,MAAM,WAAW,SAAU,CAAC;AACnC,uEAAuE;AACvE,eAAO,MAAM,UAAU,SAAU,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAID,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAU5E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAW7E;AAED,4EAA4E;AAC5E,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,WAAW,CAAuB;gBAGvB,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,EACnC,GAAG,GAAE,MAAM,MAAiB;IAG/C,sDAAsD;IACtD,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,kEAAkE;IAClE,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAsBnC,uDAAuD;IACvD,OAAO,IAAI,IAAI;IAIf,uFAAuF;IACvF,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,OAAO;CAMhB"}
|