@3dsource/angular-unreal-module 0.0.177 → 0.0.181

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3dsource/angular-unreal-module",
3
- "version": "0.0.177",
3
+ "version": "0.0.181",
4
4
  "description": "Angular integration for Unreal Engine Pixel Streaming",
5
5
  "keywords": [
6
6
  "3dsource",
@@ -16,11 +16,11 @@
16
16
  "author": "3dsource",
17
17
  "sideEffects": false,
18
18
  "dependencies": {
19
- "tslib": "^2.8.1"
19
+ "tslib": "^2.8.1",
20
+ "uuid": "^14.0.2"
20
21
  },
21
22
  "peerDependencies": {
22
- "@3dsource/types-unreal": ">=0.0.7",
23
- "@3dsource/utils": ">=1.0.21",
23
+ "@3dsource/types-unreal": ">=0.0.14 <0.1.0",
24
24
  "@angular/cdk": ">=19.0.0 <23.0.0",
25
25
  "@angular/common": ">=19.0.0 <23.0.0",
26
26
  "@angular/core": ">=19.0.0 <23.0.0",
@@ -9,7 +9,6 @@ import * as rxjs from 'rxjs';
9
9
  import { Subject, Observable, Subscription, OperatorFunction } from 'rxjs';
10
10
  import * as _ngrx_effects from '@ngrx/effects';
11
11
  import { Actions } from '@ngrx/effects';
12
- import { IKeyCode, ImageOutputValues, Area } from '@3dsource/utils';
13
12
  import { SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
14
13
  import { MetaBoxCommandPacket, MetaBoxCommandList, MetaBoxCallbackList, FOnSceneStateCallback, FOnFocusObjectCallback, FCameraChangedCallback, FOnObjectTransformChangedCallback, FOnChangeSequenceChangedCallback, FOnFinishSequenceChangedCallback } from '@3dsource/types-unreal';
15
14
 
@@ -260,6 +259,148 @@ declare const MouseButtonsMask: {
260
259
  FifthButton: number;
261
260
  };
262
261
 
262
+ /**
263
+ * `KeyboardEvent.code` → the Windows virtual-key code Unreal expects.
264
+ *
265
+ * Only keys whose `event.keyCode` cannot be trusted belong here. The browser
266
+ * already resolves `keyCode` correctly for everything else, so the table exists
267
+ * for two cases: browsers that disagree on a value (Gecko reports the
268
+ * punctuation keys by ASCII — `;` as 59, `=` as 61, `-` as 173) and keys Unreal
269
+ * numbers differently from the DOM (the right-hand modifiers, the Cmd/Win keys).
270
+ *
271
+ * The numpad is deliberately absent. A numpad key reports the same `code` in
272
+ * both NumLock states but a different virtual key — `Numpad4` is 37/`VK_LEFT`
273
+ * with NumLock off and 100/`VK_NUMPAD4` with it on — so any entry here would
274
+ * have to commit to one state and be wrong in the other. Falling through to
275
+ * `event.keyCode` is right in both. `NumpadAdd`/`Subtract`/`Multiply`/`Divide`
276
+ * stay because they do not change with NumLock.
277
+ *
278
+ * `SpecialKeyCodes` in `./buttons-codes` repeats seven of these entries with
279
+ * the same values. The two are kept apart because both are public API; merging
280
+ * them would be a breaking change.
281
+ *
282
+ * Values must stay unique so `InvertedKeyMap` is lossless. The `OSLeft`/
283
+ * `MetaLeft` and `OSRight`/`MetaRight` pairs share a value on purpose: they are
284
+ * legacy and current names for the same physical key.
285
+ */
286
+ declare const KeyboardNumericCode: {
287
+ readonly Backspace: 8;
288
+ readonly Tab: 9;
289
+ readonly Enter: 13;
290
+ readonly ShiftLeft: 16;
291
+ readonly ControlLeft: 17;
292
+ readonly AltLeft: 18;
293
+ readonly Escape: 27;
294
+ readonly Space: 32;
295
+ readonly ArrowLeft: 37;
296
+ readonly ArrowUp: 38;
297
+ readonly ArrowRight: 39;
298
+ readonly ArrowDown: 40;
299
+ readonly Digit0: 48;
300
+ readonly Digit1: 49;
301
+ readonly Digit2: 50;
302
+ readonly Digit3: 51;
303
+ readonly Digit4: 52;
304
+ readonly Digit5: 53;
305
+ readonly Digit6: 54;
306
+ readonly Digit7: 55;
307
+ readonly Digit8: 56;
308
+ readonly Digit9: 57;
309
+ readonly KeyA: 65;
310
+ readonly KeyB: 66;
311
+ readonly KeyC: 67;
312
+ readonly KeyD: 68;
313
+ readonly KeyE: 69;
314
+ readonly KeyF: 70;
315
+ readonly KeyG: 71;
316
+ readonly KeyH: 72;
317
+ readonly KeyI: 73;
318
+ readonly KeyJ: 74;
319
+ readonly KeyK: 75;
320
+ readonly KeyL: 76;
321
+ readonly KeyM: 77;
322
+ readonly KeyN: 78;
323
+ readonly KeyO: 79;
324
+ readonly KeyP: 80;
325
+ readonly KeyQ: 81;
326
+ readonly KeyR: 82;
327
+ readonly KeyS: 83;
328
+ readonly KeyT: 84;
329
+ readonly KeyU: 85;
330
+ readonly KeyV: 86;
331
+ readonly KeyW: 87;
332
+ readonly KeyX: 88;
333
+ readonly KeyY: 89;
334
+ readonly KeyZ: 90;
335
+ readonly OSLeft: 91;
336
+ readonly MetaLeft: 91;
337
+ readonly OSRight: 92;
338
+ readonly MetaRight: 92;
339
+ readonly ContextMenu: 93;
340
+ readonly NumpadMultiply: 106;
341
+ readonly NumpadAdd: 107;
342
+ readonly NumpadSubtract: 109;
343
+ readonly NumpadDivide: 111;
344
+ readonly F1: 112;
345
+ readonly F2: 113;
346
+ readonly F3: 114;
347
+ readonly F4: 115;
348
+ readonly F5: 116;
349
+ readonly F6: 117;
350
+ readonly F7: 118;
351
+ readonly F8: 119;
352
+ readonly F9: 120;
353
+ readonly F10: 121;
354
+ readonly F11: 122;
355
+ readonly F12: 123;
356
+ readonly NumLock: 144;
357
+ readonly ScrollLock: 145;
358
+ readonly Semicolon: 186;
359
+ readonly Equal: 187;
360
+ readonly Comma: 188;
361
+ readonly Minus: 189;
362
+ readonly Period: 190;
363
+ readonly Slash: 191;
364
+ readonly Backquote: 192;
365
+ readonly BracketLeft: 219;
366
+ readonly Backslash: 220;
367
+ readonly BracketRight: 221;
368
+ readonly Quote: 222;
369
+ readonly IntlBackslash: 226;
370
+ readonly ShiftRight: 253;
371
+ readonly ControlRight: 254;
372
+ readonly AltRight: 255;
373
+ };
374
+ /**
375
+ * The `keyCode` a browser reports while an IME is composing (`VK_PROCESSKEY`).
376
+ *
377
+ * `event.code` keeps naming the *physical* key during composition — `KeyW`,
378
+ * `KeyA` and friends — so a consumer that forwards keys has to drop these
379
+ * events explicitly or typing CJK text turns into keystrokes.
380
+ */
381
+ declare const IME_PROCESS_KEY = 229;
382
+ /**
383
+ * Codes of the keys that are themselves modifiers. A modifier's own `keydown`
384
+ * already reports `ctrlKey`/`metaKey` as pressed, so a "browser shortcut in
385
+ * flight" check has to exempt them or `ControlRight` would block itself.
386
+ */
387
+ declare const MODIFIER_KEY_CODES: ReadonlySet<string>;
388
+ type IKeyName = keyof typeof KeyboardNumericCode;
389
+ type IKeyCode = (typeof KeyboardNumericCode)[IKeyName];
390
+ /**
391
+ * A physical key name as reported by `KeyboardEvent.code`.
392
+ *
393
+ * The union lists the names this table knows a virtual-key code for, so editors
394
+ * complete them. The `string` branch keeps every other DOM code assignable —
395
+ * `'Numpad4'`, `'Delete'`, `'Home'` — since a key needs no entry here to be
396
+ * matched against `event.code`.
397
+ */
398
+ type IKeyCodeName = IKeyName | (string & Record<never, never>);
399
+ type InvertedKeyMapType = {
400
+ [K in IKeyName as (typeof KeyboardNumericCode)[K]]: K;
401
+ };
402
+ declare const InvertedKeyMap: InvertedKeyMapType;
403
+
263
404
  declare const ControlScheme: {
264
405
  readonly LockedMouse: 0;
265
406
  readonly HoveringMouse: 1;
@@ -356,6 +497,33 @@ interface FreezeFrameMessage {
356
497
  progress: number | null;
357
498
  }
358
499
 
500
+ /**
501
+ * Geometry primitives used by the resize helpers.
502
+ *
503
+ * Collapsed into one file from the four these were split across, so the
504
+ * package no longer names another library in its public type surface.
505
+ */
506
+ interface Size {
507
+ w: number;
508
+ h: number;
509
+ }
510
+ interface Coords {
511
+ x: number;
512
+ y: number;
513
+ }
514
+ interface Rect extends Size, Coords {
515
+ }
516
+ interface Area extends Rect {
517
+ scale: number;
518
+ }
519
+
520
+ /**
521
+ * Output format accepted by {@link getImageFromVideoStream}.
522
+ *
523
+ * `'canvas'` returns the element itself; the MIME types return a data URL.
524
+ */
525
+ type ImageOutputValues = 'canvas' | 'image/png' | 'image/webp' | 'image/jpeg';
526
+
359
527
  interface SignalingData {
360
528
  instanceType: string;
361
529
  branchInfo: string;
@@ -1261,10 +1429,10 @@ declare class FreezeFrameService extends SubService {
1261
1429
 
1262
1430
  declare class InputService extends SubService {
1263
1431
  private readonly commandsSender;
1264
- private readonly isDevMode;
1432
+ private readonly devModeService;
1265
1433
  private readonly videoService;
1266
1434
  private readonly unrealConfig;
1267
- readonly defaultKeys: (17 | 37 | 38 | 39 | 40 | 46 | 65 | 68 | 83 | 87 | 107 | 109 | 187 | 189)[];
1435
+ readonly defaultKeys: readonly IKeyCodeName[];
1268
1436
  private availableKeys;
1269
1437
  private options;
1270
1438
  private reInit$;
@@ -1275,6 +1443,7 @@ declare class InputService extends SubService {
1275
1443
  private fingerIds;
1276
1444
  private maxByteValue;
1277
1445
  private fingers;
1446
+ private readonly heldKeys;
1278
1447
  private readonly mouseMoveView;
1279
1448
  private readonly mouseButtonView;
1280
1449
  private readonly mouseWheelView;
@@ -1286,7 +1455,16 @@ declare class InputService extends SubService {
1286
1455
  private offsetParams;
1287
1456
  constructor();
1288
1457
  protected init(): void;
1289
- useKeyboardKeys(keys: IKeyCode[]): void;
1458
+ /**
1459
+ * Replaces the forwarded-key allow-list. Takes physical key names
1460
+ * (`KeyboardEvent.code`) such as `'KeyW'` or `'BracketLeft'`.
1461
+ *
1462
+ * Legacy numeric `keyCode` values are still accepted and converted, but they
1463
+ * cannot express every key: `InvertedKeyMap` is many-to-one, so 37 resolves
1464
+ * to `'ArrowLeft'` and 17 to `'ControlLeft'`, and `'Numpad4'`/`'ControlRight'`
1465
+ * are unreachable through the numeric form. Pass names.
1466
+ */
1467
+ useKeyboardKeys(keys: readonly (IKeyCode | IKeyCodeName)[]): void;
1290
1468
  get video(): HTMLVideoElement | null;
1291
1469
  private setup;
1292
1470
  /**
@@ -1315,22 +1493,46 @@ declare class InputService extends SubService {
1315
1493
  private pressMouseButtons;
1316
1494
  private registerHoveringMouseEvents;
1317
1495
  private registerMouseEnterAndLeaveEvents;
1318
- private isKeyCodeBrowserKey;
1319
1496
  /**
1320
- * The key code Unreal expects, which is the Windows virtual-key code.
1497
+ * Keys whose default browser action fights the stream: the function keys
1498
+ * (F5 reloads mid-session), Tab (moves focus out of the player) and the
1499
+ * arrows (scroll the page while they steer the camera).
1500
+ *
1501
+ * Takes the *resolved* virtual-key code rather than `event.code`, so a numpad
1502
+ * key acting as an arrow with NumLock off is caught as well.
1503
+ */
1504
+ private isBrowserKey;
1505
+ /**
1506
+ * The key code Unreal expects — the Windows virtual-key code — or `null` when
1507
+ * the event carries no key worth forwarding.
1321
1508
  *
1322
- * `e.keyCode` is not that on Gecko: Firefox reports the punctuation keys by
1323
- * their ASCII value — `=` as 61 and `-` as 173, where Chrome sends 187 and 189
1324
- * — so zoom-on-plus/minus reached the engine as codes it does not know.
1325
- * `KeyboardNumericCode` is keyed by `e.code` and holds the virtual-key value,
1326
- * so it resolves the same number on every browser; a no-op on Chrome.
1509
+ * `KeyboardNumericCode` covers only the keys whose `event.keyCode` cannot be
1510
+ * trusted; everything else, the numpad included, falls through to the value
1511
+ * the browser resolved.
1327
1512
  *
1328
- * Checked after the right-hand modifiers, which need their own distinct codes
1329
- * that the map does not carry.
1513
+ * Returns `null` for an IME composition — `event.code` still names the
1514
+ * physical key there, so the keystrokes would otherwise reach the engine as
1515
+ * camera movement — and for a `keyCode` of 0, which no engine key uses.
1330
1516
  */
1331
1517
  private getKeyCode;
1518
+ /**
1519
+ * True while a focused element owns the keyboard. Read off `composedPath()`
1520
+ * rather than `event.target`: retargeting reports the host element for an
1521
+ * `<input>` inside a shadow root, so a `target`-based check never fires in an
1522
+ * app built from web components.
1523
+ */
1524
+ private isTextEntryTarget;
1525
+ /** True when `event.code` is on the allow-list, or dev mode waives it. */
1526
+ private isKeyAllowed;
1527
+ private sendKeyUp;
1528
+ /**
1529
+ * Releases everything the engine still believes is held. Needed because a
1530
+ * `keyup` is not guaranteed to arrive: the window can lose focus to a browser
1531
+ * dialog or another tab first, and then the engine keeps moving the camera
1532
+ * forever.
1533
+ */
1534
+ private releaseKeys;
1332
1535
  private registerKeyboardEvents;
1333
- private startListenKeys;
1334
1536
  static ɵfac: i0.ɵɵFactoryDeclaration<InputService, never>;
1335
1537
  static ɵprov: i0.ɵɵInjectableDeclaration<InputService>;
1336
1538
  }
@@ -1783,11 +1985,10 @@ declare class StreamStatusTelemetryService2 {
1783
1985
  /**
1784
1986
  * Emit the region / signaling / webrtc phases for an ADOPTED connection.
1785
1987
  *
1786
- * The prefetch script established the connection in PARALLEL with page load,
1787
- * so the phases are positioned at their real (anchor-relative) spans — shifted
1788
- * LEFT to overlap the HTML-loading / route-idle rows — rather than sequentially
1789
- * after them. That's the whole point of the prefetch: the handshake already
1790
- * happened while the page was loading. Each phase ships:
1988
+ * The prefetch script established the connection in parallel with page load,
1989
+ * so phases sit at their real anchor-relative spans — shifted left to overlap
1990
+ * the HTML-loading / route-idle rows instead of following them. Each phase
1991
+ * ships:
1791
1992
  * - `duration` — the real sub-step time the prefetch spent.
1792
1993
  * - `spanStartMs` — anchor-relative start, so the viewer left-shifts the bar.
1793
1994
  * - `adoptionAtMs` — where Angular took over (the next phase, Unreal
@@ -1834,22 +2035,15 @@ declare class StreamStatusTelemetryService2 {
1834
2035
  */
1835
2036
  private collectBootstrapInfo;
1836
2037
  /**
1837
- * Aggregate all resources the browser has loaded so far
1838
- * (`performance.getEntriesByType('resource')`), grouped by `initiatorType`,
1839
- * **split by `window.load`**:
1840
- *
1841
- * - `resources` — fetched before `loadEventEnd` (= part of the actual
1842
- * page weight). What you'd traditionally call "page load."
1843
- * - `lazyResources` — fetched after `loadEventEnd` (= typically the
1844
- * route's lazy-loaded chunks, JSON manifests etc. that download
1845
- * when the user navigates to a sub-route the streamer lives on).
2038
+ * Aggregates `performance.getEntriesByType('resource')` by `initiatorType`,
2039
+ * split at `window.load`: `resources` for entries before `loadEventEnd` (the
2040
+ * actual page weight) and `lazyResources` for those after it (typically the
2041
+ * route's lazy chunks and manifests, downloaded when the user navigates to the
2042
+ * sub-route the streamer lives on). If `loadEventEnd` is 0 or unknown,
2043
+ * everything lands in `resources` and `lazyResources` is null.
1846
2044
  *
1847
- * When `loadEventEnd` is 0/unknown, everything goes into `resources`
1848
- * and `lazyResources` is null — old behavior.
1849
- *
1850
- * `transferSize === 0 && decodedBodySize > 0` means the resource came from
1851
- * the HTTP cache — counted separately so cached hits don't inflate
1852
- * the "bytes on the wire" metric.
2045
+ * `transferSize === 0 && decodedBodySize > 0` means an HTTP-cache hit, counted
2046
+ * separately so it does not inflate the bytes-on-the-wire metric.
1853
2047
  */
1854
2048
  private collectResourceTotals;
1855
2049
  private aggregate;
@@ -2176,77 +2370,22 @@ declare class AnalyticsService extends SubService {
2176
2370
  }
2177
2371
 
2178
2372
  /**
2179
- * Adaptive FPS controller that monitors the incoming WebRTC video stream
2180
- * quality and dynamically adjusts Unreal Engine's `t.MaxFPS` console variable.
2181
- *
2182
- * ## How it works
2183
- *
2184
- * The service subscribes to `VideoService.videoStats$` (emitted every 250 ms)
2185
- * and collects samples of FPS, bitrate, and QP (Quantization Parameter).
2186
- * Every {@link SAMPLE_WINDOW} ticks (~2 s) it averages the collected samples
2187
- * and feeds them into a set of **fuzzy-logic membership functions** that
2188
- * classify each metric into overlapping quality bands.
2189
- *
2190
- * ### Input signals
2191
- *
2192
- * **1. FPS ratio** = `actualFPS / currentTargetFPS` (0..1+)
2193
- *
2194
- * | Band | Range | Meaning |
2195
- * |--------|---------------------|-----------------------------------------------|
2196
- * | low | ratio < 0.7 → 1.0, linear ramp 0.7..0.9 → 0 | Stream cannot keep up with the target |
2197
- * | okays | triangle 0.8..1.0, peak at 0.95 | Stream roughly matches the target |
2198
- * | high | ratio 0.9..1.0 → ramp, >1.0 → 1.0 | Stream meets or exceeds the target |
2199
- *
2200
- * **2. Bitrate load** = `currentBitrate / MAX_BITRATE` (0..1+)
2201
- *
2202
- * `MAX_BITRATE` = 20 Mbit/s — the reference ceiling for the encoder.
2203
- *
2204
- * | Band | Range | Meaning |
2205
- * |--------|-------------------------------------------------|-----------------------------------------------|
2206
- * | high | load 0.5..0.8 → ramp, >0.8 → 1.0 | Encoder is using most of the available bandwidth |
2207
- * | not high | load < 0.5 → 0.0 | Bandwidth headroom is available |
2208
- *
2209
- * **3. Quality** — mapped from QP via `mapQpToQuality()` (see {@link Quality}):
2210
- *
2211
- * | Quality | QP range | Meaning |
2212
- * |----------|-----------|-------------------------------------------------|
2213
- * | `lime` | QP <= 26 | Good — encoder has enough headroom |
2214
- * | `orange` | QP 27..35 | Fair — encoder is under moderate pressure |
2215
- * | `red` | QP > 35 | Poor — heavy compression, visible artifacts |
2216
- *
2217
- * ### Decision rules (evaluated in priority order, first match wins)
2218
- *
2219
- * | # | Condition | Decision |
2220
- * |---|------------------------------------------------------------|--------------|
2221
- * | 1 | FPS ratio is **low** (membership > 0.5) | **decrease** |
2222
- * | 2 | FPS ratio is **ok** AND quality is `red` | **decrease** |
2223
- * | 3 | FPS ratio is **ok** AND bitrate **high** AND quality is `orange` | **hold** |
2224
- * | 4 | FPS ratio is **high** AND quality is `lime` AND bitrate **not high** | **increase** |
2225
- * | — | Everything else | **hold** |
2373
+ * Adaptive FPS controller: watches the incoming WebRTC stream and steers Unreal's
2374
+ * `t.MaxFPS` between the discrete {@link FPS_STEPS}.
2226
2375
  *
2227
- * ## Cooldown-based throttling
2376
+ * Every {@link SAMPLE_WINDOW} ticks of `VideoService.videoStats$` (250 ms each)
2377
+ * it averages FPS, bitrate and QP, runs them through the fuzzy membership
2378
+ * functions below, and picks a decision by first-match rules. Fuzzy bands rather
2379
+ * than hard thresholds so a metric sitting on a boundary does not oscillate.
2228
2380
  *
2229
- * The service continuously evaluates fuzzy rules in every sample window.
2230
- * After each FPS step change it enforces a cooldown before the next
2231
- * change can be applied. The cooldown duration depends on the
2232
- * (previous decision → next decision) pair:
2381
+ * Cooldowns between steps are deliberately asymmetric: stepping *down* again is
2382
+ * allowed after {@link COOLDOWN_DECREASE_AFTER_DECREASE_MS}, but stepping back
2383
+ * *up* after a decrease waits {@link COOLDOWN_INCREASE_AFTER_DECREASE_MS} — a
2384
+ * stream that just failed its target is likely to fail it again.
2233
2385
  *
2234
- * | Previous | Next | Cooldown |
2235
- * |-----------|-----------|-----------------------------------------------------|
2236
- * | decrease | decrease | {@link COOLDOWN_DECREASE_AFTER_DECREASE_MS} (2 s) |
2237
- * | decrease | increase | {@link COOLDOWN_INCREASE_AFTER_DECREASE_MS} (10 s) |
2238
- * | increase | decrease | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
2239
- * | increase | increase | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
2240
- *
2241
- * This means the system reacts quickly after an increase (2 s in any
2242
- * direction) but is cautious about stepping back up after a decrease
2243
- * (10 s), while still allowing consecutive decreases at 2 s intervals.
2244
- *
2245
- * ## FPS dispatch
2246
- *
2247
- * FPS changes are dispatched via the NgRx `setMaxFps` action, which triggers
2248
- * the existing `UnrealEffects.setMaxFps$` effect that sends the
2249
- * `t.MaxFPS <value>` console command to Unreal Engine.
2386
+ * Changes go out as the `setMaxFps` action, not a direct command: the existing
2387
+ * `UnrealEffects.setMaxFps$` effect turns it into the `t.MaxFPS <value>` console
2388
+ * command.
2250
2389
  */
2251
2390
  declare class FpsMonitorService extends SubService {
2252
2391
  private readonly videoService;
@@ -2299,52 +2438,15 @@ declare class FpsMonitorService extends SubService {
2299
2438
  */
2300
2439
  private processTick;
2301
2440
  private handleMonitoring;
2302
- /**
2303
- * Combines fuzzy membership values into a single discrete decision.
2304
- * Rules are evaluated in priority order — first match wins.
2305
- *
2306
- * Inputs:
2307
- * - `fpsRatio` = avgFPS / currentTargetFPS (e.g., 45/60 = 0.75)
2308
- * - `bitrateLoad` = avgBitrate / 20 Mbit/s (e.g., 12M/20M = 0.6)
2309
- * - `quality` = mapQpToQuality(avgQP) → 'lime' | 'orange' | 'red'
2310
- */
2441
+ /** Rules are evaluated in priority order — first match wins. */
2311
2442
  private evaluateDecision;
2312
- /**
2313
- * FPS ratio membership "low".
2314
- * Input: a ratio = actualFPS / targetFPS (e.g., 42/60 = 0.70).
2315
- *
2316
- * ratio < 0.7 → 1.0 (clearly failing, e.g., 40/60)
2317
- * ratio 0.7..0.9 → linear ramp down (e.g., 0.8 → 0.5)
2318
- * a ratio > 0.9 → 0.0 (keeping up)
2319
- */
2443
+ /** "low" — the stream cannot keep up with its target. */
2320
2444
  private membershipFpsLow;
2321
- /**
2322
- * FPS ratio membership "ok" — triangle shape.
2323
- * Input: ratio = actualFPS / targetFPS.
2324
- *
2325
- * ratio < 0.8 → 0.0
2326
- * ratio 0.8..0.95 → ramp up, peak at 0.95 (e.g., 57/60)
2327
- * ratio 0.95..1.0 → ramp down
2328
- * a ratio > 1.0 → 0.0
2329
- */
2445
+ /** "ok" — triangle peaking just under the target. */
2330
2446
  private membershipFpsOk;
2331
- /**
2332
- * FPS ratio membership "high".
2333
- * Input: ratio = actualFPS / targetFPS.
2334
- *
2335
- * ratio < 0.9 → 0.0
2336
- * ratio 0.9..1.0 → linear ramp up (e.g., 0.95 → 0.5)
2337
- * a ratio >= 1.0 → 1.0 (meeting or exceeding target, e.g., 60/60)
2338
- */
2447
+ /** "high" — the stream meets or exceeds its target. */
2339
2448
  private membershipFpsHigh;
2340
- /**
2341
- * Bitrate load membership "high".
2342
- * Input: a load = currentBitrate / MAX_BITRATE (20 Mbit/s).
2343
- *
2344
- * load < 0.5 → 0.0 (below 10 Mbit/s — plenty of headroom)
2345
- * load 0.5..0.8 → linear ramp (e.g., 13M/20M = 0.65 → 0.5)
2346
- * load > 0.8 → 1.0 (above 16 Mbit/s — near capacity)
2347
- */
2449
+ /** "high" — the encoder is using most of the available bandwidth. */
2348
2450
  private membershipBitrateHigh;
2349
2451
  private upgradeFps;
2350
2452
  /**
@@ -3481,5 +3583,5 @@ declare const selectUserInactivityDetected: _ngrx_store.MemoizedSelector<Record<
3481
3583
  /** Used by external consumers (e.g., metabox). Do not remove. */
3482
3584
  declare const selectIsVideoPlayingAndDataChannelConnected: _ngrx_store.MemoizedSelector<Record<string, any>, boolean, (s1: boolean, s2: boolean) => boolean>;
3483
3585
 
3484
- export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UnrealAfkSlotDirective, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealResumeSlotDirective, UnrealSceneComponent, UnrealStatusMessage, UnrealStatusSlotDirective, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isAppOwned, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, resolveSkin, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
3485
- export type { AggregatedStat, AnySignalingMessage, AppOwnedStream, AwsInstance, BootstrapInfo, CandidateMessage, CirrusRequestKind, CloseReason, CommandInProgress, ConfigMessage, ConnectionError, ControlSchemeValues, DisconnectReasonType, EmittedMilestone, EventId, FpsLowData, FreezeFrameMessage, InboundVideoStats, InputProps, InstanceReady, InstanceReserved, InterruptClientStream, ListStreamersMessage, MessageBase, MessageTypeValues, OrchestrationErrorMessage, OrchestrationMessageType, P2PEstablishedMessage, ParkedStream, PhaseEndedData, PhaseOverlay, PhaseParam, PlayerCountMessage, PollingOrchestrationMessage, Quality, QualityAnomalyData, ReceivedFile, RequestStream, ResourceBucket, ResourceTotals, SSInfo, SampleData, SelectedIceCandidatePair, SessionEndReason, SessionEndedData, SessionStartedData, SignalingData, SignalingMessageHandler, SignalingMessageMap, SignalingRetryData, SignalingRetryKind, SlowResource, StreamConfig, StreamRequestContext, StreamResolutionProps, StreamTelemetryEvent, StreamTelemetryEventMap, StreamTelemetryEventName, StreamTelemetryLifecycleData, StreamTelemetryLifecycleEventName, StreamTelemetryTrackData, StreamerListMessage, TelemetryEventEnvelope, TelemetryLifecycleEnvelope, TelemetryType, UnknownFields, UnrealAfkSlotContext, UnrealCallbackDescriptor, UnrealCallbackEventMap, UnrealCallbackJson, UnrealCallbackJsonWrapped, UnrealResumeSlotContext, UnrealSlotTemplateContext, UnrealState, UnrealStatusSlotContext, UnrealUiClasses, UnrealUiPart, WSCloseCodesValues, WindowAggregate };
3586
+ export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, IME_PROCESS_KEY, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, InvertedKeyMap, KeyboardNumericCode, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MODIFIER_KEY_CODES, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UnrealAfkSlotDirective, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealResumeSlotDirective, UnrealSceneComponent, UnrealStatusMessage, UnrealStatusSlotDirective, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isAppOwned, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, resolveSkin, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
3587
+ export type { AggregatedStat, AnySignalingMessage, AppOwnedStream, Area, AwsInstance, BootstrapInfo, CandidateMessage, CirrusRequestKind, CloseReason, CommandInProgress, ConfigMessage, ConnectionError, ControlSchemeValues, Coords, DisconnectReasonType, EmittedMilestone, EventId, FpsLowData, FreezeFrameMessage, IKeyCode, IKeyCodeName, IKeyName, ImageOutputValues, InboundVideoStats, InputProps, InstanceReady, InstanceReserved, InterruptClientStream, ListStreamersMessage, MessageBase, MessageTypeValues, OrchestrationErrorMessage, OrchestrationMessageType, P2PEstablishedMessage, ParkedStream, PhaseEndedData, PhaseOverlay, PhaseParam, PlayerCountMessage, PollingOrchestrationMessage, Quality, QualityAnomalyData, ReceivedFile, Rect, RequestStream, ResourceBucket, ResourceTotals, SSInfo, SampleData, SelectedIceCandidatePair, SessionEndReason, SessionEndedData, SessionStartedData, SignalingData, SignalingMessageHandler, SignalingMessageMap, SignalingRetryData, SignalingRetryKind, Size, SlowResource, StreamConfig, StreamRequestContext, StreamResolutionProps, StreamTelemetryEvent, StreamTelemetryEventMap, StreamTelemetryEventName, StreamTelemetryLifecycleData, StreamTelemetryLifecycleEventName, StreamTelemetryTrackData, StreamerListMessage, TelemetryEventEnvelope, TelemetryLifecycleEnvelope, TelemetryType, UnknownFields, UnrealAfkSlotContext, UnrealCallbackDescriptor, UnrealCallbackEventMap, UnrealCallbackJson, UnrealCallbackJsonWrapped, UnrealResumeSlotContext, UnrealSlotTemplateContext, UnrealState, UnrealStatusSlotContext, UnrealUiClasses, UnrealUiPart, WSCloseCodesValues, WindowAggregate };