@3dsource/angular-unreal-module 0.0.156 → 0.0.158
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.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, DestroyRef, ChangeDetectionStrategy, Component, Injectable, signal, effect, untracked, computed,
|
|
2
|
+
import { InjectionToken, inject, DestroyRef, ChangeDetectionStrategy, Component, Injectable, signal, effect, untracked, computed, input, ElementRef, viewChild, afterNextRender, HostListener, SecurityContext, Pipe, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
3
3
|
import { SourceButtonComponent, SourceLoadingComponent } from '@3dsource/source-ui-native';
|
|
4
|
-
import { Subject, skip, share, merge, map, scan, timestamp, startWith, pairwise, concatMap, of, animationFrameScheduler, interval, Observable, timer, take, defer, switchMap, from, throwError, EMPTY, retry, timeout as timeout$1, repeat, BehaviorSubject, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, ignoreElements, shareReplay,
|
|
4
|
+
import { Subject, skip, share, merge, map, scan, timestamp, startWith, pairwise, concatMap, of, animationFrameScheduler, interval, Observable, timer, take, defer, switchMap, from, throwError, EMPTY, retry, timeout as timeout$1, repeat, BehaviorSubject, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, NEVER, ignoreElements, shareReplay, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, Subscription, bufferTime, combineLatestWith } from 'rxjs';
|
|
5
5
|
import { createAction, props, createReducer, on, createFeature, Store, createSelector, provideState } from '@ngrx/store';
|
|
6
|
-
import { whereNot, Falsy, Logger,
|
|
6
|
+
import { whereNot, Falsy, Logger, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, generateUuid, Truthy, tapLog, Semaphore, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
|
|
7
7
|
import { filter, map as map$1, distinctUntilChanged, takeWhile, takeUntil, catchError, tap, timeout, finalize, first, switchMap as switchMap$1, withLatestFrom, exhaustMap, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
|
|
8
8
|
import { Actions, ofType, createEffect, provideEffects } from '@ngrx/effects';
|
|
9
9
|
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
@@ -11,11 +11,8 @@ import { HttpClient } from '@angular/common/http';
|
|
|
11
11
|
import { ScrollStrategyOptions } from '@angular/cdk/overlay';
|
|
12
12
|
import { MetaBoxCommand, EPayloadVersion } from '@3dsource/types-unreal';
|
|
13
13
|
import { toSignal, toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
14
|
-
import { v4 } from 'uuid';
|
|
15
|
-
import { DomSanitizer } from '@angular/platform-browser';
|
|
16
14
|
import { NgOptimizedImage, JsonPipe } from '@angular/common';
|
|
17
|
-
import
|
|
18
|
-
import { FormsModule } from '@angular/forms';
|
|
15
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
19
16
|
|
|
20
17
|
const SpecialKeyCodes = {
|
|
21
18
|
BackSpace: 8,
|
|
@@ -872,85 +869,6 @@ function decodeData(anyData) {
|
|
|
872
869
|
}
|
|
873
870
|
}
|
|
874
871
|
|
|
875
|
-
class DataFlowMonitor {
|
|
876
|
-
/**
|
|
877
|
-
* Initializes the DataFlowMonitor monitor.
|
|
878
|
-
* @param yellowFlagThresholdPercentage - The percentage drop to trigger a YELLOW warning (default: 15%).
|
|
879
|
-
* @param redFlagThresholdPercentage - The percentage drop to trigger a RED warning (default: 30%).
|
|
880
|
-
* @param historyBufferLength - buffer length (default: 100).
|
|
881
|
-
* @param splitPoint - The point at which to split the history buffer into two halves (default: 0.5).
|
|
882
|
-
*/
|
|
883
|
-
constructor(yellowFlagThresholdPercentage = 15, redFlagThresholdPercentage = 30, historyBufferLength = 100, splitPoint = 0.5) {
|
|
884
|
-
this.yellowFlagThresholdPercentage = yellowFlagThresholdPercentage;
|
|
885
|
-
this.redFlagThresholdPercentage = redFlagThresholdPercentage;
|
|
886
|
-
this.historyBufferLength = historyBufferLength;
|
|
887
|
-
this.splitPoint = splitPoint;
|
|
888
|
-
this.dataHistory = [];
|
|
889
|
-
}
|
|
890
|
-
reset() {
|
|
891
|
-
this.dataHistory.length = 0;
|
|
892
|
-
}
|
|
893
|
-
config(data) {
|
|
894
|
-
this.reset();
|
|
895
|
-
this.yellowFlagThresholdPercentage = data.yellowFlag;
|
|
896
|
-
this.redFlagThresholdPercentage = data.redFlag;
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Adds a new bitrate measurement and checks for significant drops.
|
|
900
|
-
* @param currentValue - The current bitrate in kbps.
|
|
901
|
-
* @returns BitrateCheckResult indicating if a drop was detected.
|
|
902
|
-
*/
|
|
903
|
-
addValue(currentValue) {
|
|
904
|
-
if (isNaN(currentValue) || currentValue < 1) {
|
|
905
|
-
return {};
|
|
906
|
-
}
|
|
907
|
-
const historyLength = this.dataHistory.length;
|
|
908
|
-
if (historyLength > this.historyBufferLength) {
|
|
909
|
-
this.dataHistory.shift();
|
|
910
|
-
}
|
|
911
|
-
this.dataHistory.push(currentValue);
|
|
912
|
-
if (historyLength < this.historyBufferLength) {
|
|
913
|
-
this.dataHistory.length = this.historyBufferLength;
|
|
914
|
-
this.dataHistory.fill(currentValue, 0, this.historyBufferLength);
|
|
915
|
-
}
|
|
916
|
-
const splitIndex = Math.floor(historyLength * this.splitPoint);
|
|
917
|
-
const firstHalf = this.dataHistory.slice(0, splitIndex);
|
|
918
|
-
const secondHalf = this.dataHistory.slice(splitIndex);
|
|
919
|
-
const firstHalfMedian = calculateMedian(firstHalf);
|
|
920
|
-
const secondHalfMedian = calculateMedian(secondHalf);
|
|
921
|
-
const dropPercentage = clampf(0, 100, ((firstHalfMedian - secondHalfMedian) / firstHalfMedian) * 100);
|
|
922
|
-
const isRedFlag = dropPercentage >= this.redFlagThresholdPercentage;
|
|
923
|
-
if (dropPercentage >= this.yellowFlagThresholdPercentage) {
|
|
924
|
-
return {
|
|
925
|
-
config: {
|
|
926
|
-
yellowFlagThresholdPercentage: this.yellowFlagThresholdPercentage,
|
|
927
|
-
redFlagThresholdPercentage: this.redFlagThresholdPercentage,
|
|
928
|
-
historyBufferLength: this.historyBufferLength,
|
|
929
|
-
splitPoint: this.splitPoint,
|
|
930
|
-
},
|
|
931
|
-
isDropDetected: true,
|
|
932
|
-
dropPercentage,
|
|
933
|
-
activeMedian: secondHalfMedian,
|
|
934
|
-
quality: isRedFlag ? 'red' : 'orange',
|
|
935
|
-
message: `Significant flow drop detected: ${dropPercentage.toFixed(2)}% (from ${firstHalfMedian} to ${secondHalfMedian})`,
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
return {
|
|
939
|
-
config: {
|
|
940
|
-
yellowFlagThresholdPercentage: this.yellowFlagThresholdPercentage,
|
|
941
|
-
redFlagThresholdPercentage: this.redFlagThresholdPercentage,
|
|
942
|
-
historyBufferLength: this.historyBufferLength,
|
|
943
|
-
splitPoint: this.splitPoint,
|
|
944
|
-
},
|
|
945
|
-
isDropDetected: false,
|
|
946
|
-
dropPercentage,
|
|
947
|
-
activeMedian: secondHalfMedian,
|
|
948
|
-
quality: 'lime',
|
|
949
|
-
message: 'Stable flow',
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
872
|
const dispatchResize = () => {
|
|
955
873
|
window.dispatchEvent(new Event('resize'));
|
|
956
874
|
};
|
|
@@ -1033,51 +951,6 @@ function* getActiveUrl(urls) {
|
|
|
1033
951
|
}
|
|
1034
952
|
}
|
|
1035
953
|
|
|
1036
|
-
class KalmanFilter1D {
|
|
1037
|
-
/**
|
|
1038
|
-
* @param initialEstimate Initial guess of the state.
|
|
1039
|
-
* @param initialUncertainty Initial uncertainty in the guess.
|
|
1040
|
-
* @param processNoise Process variance (Q) representing model uncertainty.
|
|
1041
|
-
* @param measurementNoise Measurement variance (R) representing noise in measurements.
|
|
1042
|
-
*/
|
|
1043
|
-
constructor(initialEstimate, initialUncertainty, processNoise, measurementNoise) {
|
|
1044
|
-
this.estimate = initialEstimate;
|
|
1045
|
-
this.uncertainty = initialUncertainty;
|
|
1046
|
-
this.processNoise = processNoise;
|
|
1047
|
-
this.measurementNoise = measurementNoise;
|
|
1048
|
-
}
|
|
1049
|
-
/**
|
|
1050
|
-
* Incorporates a new measurement and returns the updated estimate.
|
|
1051
|
-
* @param measurement The new measurement value.
|
|
1052
|
-
*/
|
|
1053
|
-
update(measurement) {
|
|
1054
|
-
// === Prediction Step ===
|
|
1055
|
-
// For a constant model, the predicted state is the previous estimate.
|
|
1056
|
-
const predictedEstimate = this.estimate;
|
|
1057
|
-
// Increase the uncertainty by the process noise.
|
|
1058
|
-
const predictedUncertainty = this.uncertainty + this.processNoise;
|
|
1059
|
-
// === Update Step ===
|
|
1060
|
-
// Compute the innovation or measurement residual.
|
|
1061
|
-
const innovation = measurement - predictedEstimate;
|
|
1062
|
-
// Compute the innovation covariance.
|
|
1063
|
-
const innovationCovariance = predictedUncertainty + this.measurementNoise;
|
|
1064
|
-
// Compute the Kalman Gain.
|
|
1065
|
-
const kalmanGain = predictedUncertainty / innovationCovariance;
|
|
1066
|
-
// Update the estimate with the measurement, weighted by the Kalman Gain.
|
|
1067
|
-
this.estimate = predictedEstimate + kalmanGain * innovation;
|
|
1068
|
-
// Update the uncertainty.
|
|
1069
|
-
this.uncertainty = (1 - kalmanGain) * predictedUncertainty;
|
|
1070
|
-
return this.estimate;
|
|
1071
|
-
}
|
|
1072
|
-
config(data) {
|
|
1073
|
-
const { initialBitrateEstimate, initialErrorCovariance, processNoise, measurementNoise, } = data;
|
|
1074
|
-
this.estimate = initialBitrateEstimate;
|
|
1075
|
-
this.uncertainty = initialErrorCovariance;
|
|
1076
|
-
this.processNoise = processNoise;
|
|
1077
|
-
this.measurementNoise = measurementNoise;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
954
|
class LatencyTimings {
|
|
1082
955
|
constructor() {
|
|
1083
956
|
this.TestStartTimeMs = null;
|
|
@@ -2565,30 +2438,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
2565
2438
|
args: [{ providedIn: 'root' }]
|
|
2566
2439
|
}], ctorParameters: () => [] });
|
|
2567
2440
|
|
|
2568
|
-
/**
|
|
2569
|
-
* Default LBM Filter Parameters
|
|
2570
|
-
*/
|
|
2571
|
-
const DefaultFilterModel = {
|
|
2572
|
-
minimumBitrate: 1600,
|
|
2573
|
-
yellowFlag: 30,
|
|
2574
|
-
redFlag: 50,
|
|
2575
|
-
minimumFps: 5,
|
|
2576
|
-
monitoringDelayTime: 5000,
|
|
2577
|
-
initialBitrateEstimate: 1600,
|
|
2578
|
-
initialErrorCovariance: 1,
|
|
2579
|
-
processNoise: 3,
|
|
2580
|
-
measurementNoise: 500,
|
|
2581
|
-
panelOpen: false,
|
|
2582
|
-
};
|
|
2583
|
-
/**
|
|
2584
|
-
* Global LBM Filter Parameters
|
|
2585
|
-
*/
|
|
2586
|
-
const FilterModel = { ...DefaultFilterModel };
|
|
2587
|
-
/**
|
|
2588
|
-
* Bitrate Monitor Static Class
|
|
2589
|
-
*/
|
|
2590
|
-
const BITRATE_MONITOR = new DataFlowMonitor(FilterModel.yellowFlag, FilterModel.redFlag, 20);
|
|
2591
|
-
|
|
2592
2441
|
class VideoService extends SubService {
|
|
2593
2442
|
constructor() {
|
|
2594
2443
|
super();
|
|
@@ -2606,15 +2455,13 @@ class VideoService extends SubService {
|
|
|
2606
2455
|
this.videoTrack$ = new BehaviorSubject(null);
|
|
2607
2456
|
this.VideoEncoderQP = 0;
|
|
2608
2457
|
this.aggregatedStats = {};
|
|
2609
|
-
this.kalmanFilter1D = new KalmanFilter1D(FilterModel.initialBitrateEstimate, FilterModel.initialErrorCovariance, FilterModel.processNoise, FilterModel.measurementNoise);
|
|
2610
2458
|
/**
|
|
2611
2459
|
* Aggregate video stats and emit it as videoStats$
|
|
2612
2460
|
*/
|
|
2613
2461
|
this.videoStats$ = this.videoTrack$.pipe(
|
|
2614
2462
|
// If videoTrack$ emits null, switch to EMPTY → the current interval is canceled.
|
|
2615
2463
|
switchMap$1((track) => track
|
|
2616
|
-
?
|
|
2617
|
-
interval(250).pipe(map(() => track.pcClient), switchMap$1((pcClient) => from(this.getStats(pcClient))), filter(Truthy))
|
|
2464
|
+
? interval(250).pipe(map(() => track.pcClient), switchMap$1((pcClient) => from(this.getStats(pcClient))), filter(Truthy))
|
|
2618
2465
|
: of(null)), filter(Truthy), share());
|
|
2619
2466
|
this.destroyAbort = new AbortController();
|
|
2620
2467
|
this.destroyRef.onDestroy(() => this.destroy());
|
|
@@ -2843,9 +2690,6 @@ class VideoService extends SubService {
|
|
|
2843
2690
|
}
|
|
2844
2691
|
// Calculate duration of run
|
|
2845
2692
|
newStat.runTime = Math.floor((newStat.timestamp - newStat.timestampStart) / 1000);
|
|
2846
|
-
newStat.kalmanBitrate = Math.floor(this.kalmanFilter1D.update(newStat.bitrate || FilterModel.initialBitrateEstimate));
|
|
2847
|
-
newStat.dataFlowCheckResult = BITRATE_MONITOR.addValue(newStat.kalmanBitrate);
|
|
2848
|
-
newStat.bitrateDrop = Math.floor(newStat.dataFlowCheckResult.dropPercentage);
|
|
2849
2693
|
this.aggregatedStats = newStat;
|
|
2850
2694
|
return this.onAggregatedStats(newStat);
|
|
2851
2695
|
}
|
|
@@ -2882,8 +2726,6 @@ class VideoService extends SubService {
|
|
|
2882
2726
|
// Reset stat state to prevent stale deltas on next session
|
|
2883
2727
|
this.aggregatedStats = {};
|
|
2884
2728
|
this.VideoEncoderQP = 0;
|
|
2885
|
-
this.kalmanFilter1D = new KalmanFilter1D(FilterModel.initialBitrateEstimate, FilterModel.initialErrorCovariance, FilterModel.processNoise, FilterModel.measurementNoise);
|
|
2886
|
-
BITRATE_MONITOR.reset();
|
|
2887
2729
|
while (this.container?.firstChild) {
|
|
2888
2730
|
this.container.removeChild(this.container.firstChild);
|
|
2889
2731
|
}
|
|
@@ -3354,9 +3196,7 @@ class WebRtcPlayerService extends SubService {
|
|
|
3354
3196
|
dc.onmessage = null;
|
|
3355
3197
|
dc.onopen = null;
|
|
3356
3198
|
dc.onclose = null;
|
|
3357
|
-
|
|
3358
|
-
// subscribe to a native `open` event that may still be queued.
|
|
3359
|
-
this.dcClient = this.setupDataChannelCallbacks(dc, false);
|
|
3199
|
+
this.dcClient = this.setupDataChannelCallbacks(dc);
|
|
3360
3200
|
for (const buffered of dcBuffer) {
|
|
3361
3201
|
this.onDataChannelMessage$.next(buffered);
|
|
3362
3202
|
}
|
|
@@ -3500,7 +3340,7 @@ class WebRtcPlayerService extends SubService {
|
|
|
3500
3340
|
}
|
|
3501
3341
|
}
|
|
3502
3342
|
}
|
|
3503
|
-
setupDataChannelCallbacks(datachannel
|
|
3343
|
+
setupDataChannelCallbacks(datachannel) {
|
|
3504
3344
|
try {
|
|
3505
3345
|
Logger.log(`[DATACHANNEL] Setting up datachannel "${datachannel.label}" callbacks...`);
|
|
3506
3346
|
this.commandTelemetryService.trackStartCommand('EXT-DataChannel Connection');
|
|
@@ -3523,16 +3363,14 @@ class WebRtcPlayerService extends SubService {
|
|
|
3523
3363
|
const message = `[DATACHANNEL] Data channel "${datachannel.label}" error. id:${datachannel.id}`;
|
|
3524
3364
|
Logger.error(message, e);
|
|
3525
3365
|
});
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
});
|
|
3535
|
-
}
|
|
3366
|
+
fromEvent(datachannel, 'open')
|
|
3367
|
+
.pipe(tap$1(() => {
|
|
3368
|
+
Logger.info(`[DATACHANNEL] Data channel "${datachannel.label}" connected. id:${datachannel.id}`);
|
|
3369
|
+
}), takeUntil(merge(this.disconnect$, this.destroy$)))
|
|
3370
|
+
.subscribe(() => {
|
|
3371
|
+
this.commandTelemetryService.trackStopCommand('EXT-DataChannel Connection');
|
|
3372
|
+
this.onDataChannelConnected();
|
|
3373
|
+
});
|
|
3536
3374
|
fromEvent(datachannel, 'message')
|
|
3537
3375
|
.pipe(
|
|
3538
3376
|
/* tap((e: MessageEvent) => {
|
|
@@ -4081,8 +3919,25 @@ class UnrealEffects {
|
|
|
4081
3919
|
data: { content },
|
|
4082
3920
|
}).closed), map(() => showUnrealErrorMessage({ code: null })));
|
|
4083
3921
|
});
|
|
3922
|
+
/**
|
|
3923
|
+
* `dataChannelReady` is a ONCE-PER-SESSION signal: it starts app init
|
|
3924
|
+
* (console commands → loopback → viewport) and makes `SignallingService`
|
|
3925
|
+
* send `p2pEstablished`. Neither is idempotent, so a second
|
|
3926
|
+
* `dataChannelConnected` inside the same session must not re-emit it.
|
|
3927
|
+
*
|
|
3928
|
+
* That second announcement is real in the prefetch flow: `adopt()` declares
|
|
3929
|
+
* the already-open channel manually, while the transport it adopted can
|
|
3930
|
+
* still produce its own `dataChannelConnected` afterwards — the effect used
|
|
3931
|
+
* to turn both into `dataChannelReady`.
|
|
3932
|
+
*
|
|
3933
|
+
* `exhaustMap` over an inner stream that emits once and then idles keeps the
|
|
3934
|
+
* gate shut until the session actually ends; `takeUntil` re-opens it on a
|
|
3935
|
+
* cirrus drop, a scene destroy, or the start of a peer reconnect (which is
|
|
3936
|
+
* where the pre-existing `isReconnecting` filter hands over).
|
|
3937
|
+
*/
|
|
4084
3938
|
this.unrealReceiverReady$ = createEffect(() => {
|
|
4085
|
-
|
|
3939
|
+
const sessionEnded$ = this.actions$.pipe(ofType(setCirrusDisconnected, destroyUnrealScene, resetDataChannelForReconnect));
|
|
3940
|
+
return this.actions$.pipe(ofType(dataChannelConnected), withLatestFrom(this.store.select(unrealFeature.selectIsReconnecting)), filter(([, isReconnecting]) => !isReconnecting), exhaustMap(() => concat(of(dataChannelReady()), NEVER).pipe(takeUntil(sessionEnded$))));
|
|
4086
3941
|
});
|
|
4087
3942
|
this.initAppByDataChannelReady$ = createEffect(() => {
|
|
4088
3943
|
return this.actions$.pipe(ofType(dataChannelReady), tap(() => {
|
|
@@ -5558,6 +5413,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
5558
5413
|
args: [{ providedIn: 'root' }]
|
|
5559
5414
|
}], ctorParameters: () => [] });
|
|
5560
5415
|
|
|
5416
|
+
/**
|
|
5417
|
+
* Pro-grade telemetry for the WebRTC stream lifecycle.
|
|
5418
|
+
*
|
|
5419
|
+
* Wire-level event taxonomy (intentionally small — all phase-specific
|
|
5420
|
+
* details ride inside `stream.phase_ended`'s payload):
|
|
5421
|
+
* - `stream.session_started` — once per session, when a real connection
|
|
5422
|
+
* attempt begins (`setEstablishingConnection(true)`)
|
|
5423
|
+
* - `stream.phase_ended` — once per closer in {@link phaseCatalog};
|
|
5424
|
+
* carries phaseNo / id / color /
|
|
5425
|
+
* milestones / params / overlays
|
|
5426
|
+
* - `stream.sample` — every 5s, window-aggregated (p. 50/p. 95/min/max)
|
|
5427
|
+
* - `stream.quality_degraded` — on QP entering `red` (>35)
|
|
5428
|
+
* - `stream.quality_recovered` — on QP leaving `red`
|
|
5429
|
+
* - `stream.fps_low` — when the window FPS p50 < 70% of the target
|
|
5430
|
+
* - `stream.session_ended` — once per session, on disconnect/pagehide/restart
|
|
5431
|
+
*
|
|
5432
|
+
* Every event carries `sessionId` + `userId`. Samples carry a monotonic
|
|
5433
|
+
* `seq` for server-side ordering & dedup. Durations use `performance.now()`
|
|
5434
|
+
* — immune to wall-clock jumps.
|
|
5435
|
+
*
|
|
5436
|
+
* NOTE: For `stream.session_ended` on the tab close to survive teardown,
|
|
5437
|
+
* the Mixpanel client must be configured with `api_transport: 'sendBeacon'`.
|
|
5438
|
+
*
|
|
5439
|
+
* Every wire shape lives in `interfaces/stream-telemetry-events.interface.ts`
|
|
5440
|
+
* — `track()` / `emitLifecycle()` are typed against it, so a payload that
|
|
5441
|
+
* drifts from the contract is a compile error.
|
|
5442
|
+
*/
|
|
5561
5443
|
/** Trim a resource URL to its path: drop origin and query string so the
|
|
5562
5444
|
* emitted name is short, stable, and not a PII-bearing query. */
|
|
5563
5445
|
function shortResourceName(url) {
|
|
@@ -5860,14 +5742,14 @@ class StreamStatusTelemetryService2 {
|
|
|
5860
5742
|
const existing = localStorage.getItem(USER_ID_STORAGE_KEY);
|
|
5861
5743
|
if (existing && existing.length > 0)
|
|
5862
5744
|
return existing;
|
|
5863
|
-
const fresh =
|
|
5745
|
+
const fresh = generateUuid();
|
|
5864
5746
|
localStorage.setItem(USER_ID_STORAGE_KEY, fresh);
|
|
5865
5747
|
return fresh;
|
|
5866
5748
|
}
|
|
5867
5749
|
catch {
|
|
5868
5750
|
// localStorage disabled (private-mode restrictions, quota, etc.)
|
|
5869
5751
|
// Fall back to a per-tab ephemeral id so telemetry keeps working.
|
|
5870
|
-
return
|
|
5752
|
+
return generateUuid();
|
|
5871
5753
|
}
|
|
5872
5754
|
}
|
|
5873
5755
|
init() {
|
|
@@ -5920,7 +5802,7 @@ class StreamStatusTelemetryService2 {
|
|
|
5920
5802
|
startSession() {
|
|
5921
5803
|
// If a previous session is still active, close it first.
|
|
5922
5804
|
this.endSession('restart');
|
|
5923
|
-
const id =
|
|
5805
|
+
const id = generateUuid();
|
|
5924
5806
|
const startedAt = Date.now();
|
|
5925
5807
|
const startedAtPerf = performance.now();
|
|
5926
5808
|
const sub = new Subscription();
|
|
@@ -6129,6 +6011,8 @@ class StreamStatusTelemetryService2 {
|
|
|
6129
6011
|
// anchors were already captured at the top of `startSession()`.
|
|
6130
6012
|
const now = atPerf ?? performance.now();
|
|
6131
6013
|
const duration = Math.round(now - this.session.lastMilestonePerf);
|
|
6014
|
+
// Re-adding the omitted `duration` reconstructs the full payload, but TS
|
|
6015
|
+
// can't verify that through the generic key — assert instead.
|
|
6132
6016
|
this.track(event, { duration, ...extra });
|
|
6133
6017
|
this.session.lastMilestonePerf = now;
|
|
6134
6018
|
}
|
|
@@ -7124,11 +7008,11 @@ class AfkTimeoutModalComponent {
|
|
|
7124
7008
|
this.#afkService.reset();
|
|
7125
7009
|
}
|
|
7126
7010
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7127
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div
|
|
7011
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7128
7012
|
}
|
|
7129
7013
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
|
|
7130
7014
|
type: Component,
|
|
7131
|
-
args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "@if (isShowAfkPopup()) {\n <div
|
|
7015
|
+
args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "@if (isShowAfkPopup()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
|
|
7132
7016
|
}] });
|
|
7133
7017
|
|
|
7134
7018
|
class FreezeFrameComponent {
|
|
@@ -7146,57 +7030,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
7146
7030
|
args: [{ selector: 'app-freeze-frame', changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"] }]
|
|
7147
7031
|
}] });
|
|
7148
7032
|
|
|
7149
|
-
class SafePipe {
|
|
7150
|
-
constructor() {
|
|
7151
|
-
this.sanitizer = inject(DomSanitizer);
|
|
7152
|
-
}
|
|
7153
|
-
transform(value, type) {
|
|
7154
|
-
if (!value) {
|
|
7155
|
-
return value;
|
|
7156
|
-
}
|
|
7157
|
-
const context = (() => {
|
|
7158
|
-
switch (type) {
|
|
7159
|
-
case 'html':
|
|
7160
|
-
return SecurityContext.HTML;
|
|
7161
|
-
case 'style':
|
|
7162
|
-
return SecurityContext.STYLE;
|
|
7163
|
-
case 'script':
|
|
7164
|
-
return SecurityContext.SCRIPT;
|
|
7165
|
-
case 'url':
|
|
7166
|
-
return SecurityContext.URL;
|
|
7167
|
-
case 'resourceUrl':
|
|
7168
|
-
return SecurityContext.RESOURCE_URL;
|
|
7169
|
-
default:
|
|
7170
|
-
throw new Error(`Invalid safe type specified: ${type}`);
|
|
7171
|
-
}
|
|
7172
|
-
})();
|
|
7173
|
-
// Preserve the published return type while returning Angular-sanitized
|
|
7174
|
-
// content instead of disabling framework security checks.
|
|
7175
|
-
return (this.sanitizer.sanitize(context, value) ?? '');
|
|
7176
|
-
}
|
|
7177
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
7178
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
|
|
7179
|
-
}
|
|
7180
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, decorators: [{
|
|
7181
|
-
type: Pipe,
|
|
7182
|
-
args: [{
|
|
7183
|
-
name: 'safe',
|
|
7184
|
-
}]
|
|
7185
|
-
}] });
|
|
7186
|
-
|
|
7187
|
-
class LowBandwidthModalComponent {
|
|
7188
|
-
#dialogRef = inject(DialogRef);
|
|
7189
|
-
close(value) {
|
|
7190
|
-
this.#dialogRef.close(value || false);
|
|
7191
|
-
}
|
|
7192
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7193
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: LowBandwidthModalComponent, isStandalone: true, selector: "app-low-bandwidth-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7194
|
-
}
|
|
7195
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthModalComponent, decorators: [{
|
|
7196
|
-
type: Component,
|
|
7197
|
-
args: [{ selector: 'app-low-bandwidth-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafePipe, SourceButtonComponent], template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"] }]
|
|
7198
|
-
}] });
|
|
7199
|
-
|
|
7200
7033
|
class ImageLoadingSrcComponent {
|
|
7201
7034
|
constructor() {
|
|
7202
7035
|
this.#store = inject(Store);
|
|
@@ -7325,6 +7158,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
7325
7158
|
args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceLoadingComponent], template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div class=\"status-box\">\n @if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-surface-container-main, #fff);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-text-body-main, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"] }]
|
|
7326
7159
|
}] });
|
|
7327
7160
|
|
|
7161
|
+
class SafePipe {
|
|
7162
|
+
constructor() {
|
|
7163
|
+
this.sanitizer = inject(DomSanitizer);
|
|
7164
|
+
}
|
|
7165
|
+
transform(value, type) {
|
|
7166
|
+
if (!value) {
|
|
7167
|
+
return value;
|
|
7168
|
+
}
|
|
7169
|
+
const context = (() => {
|
|
7170
|
+
switch (type) {
|
|
7171
|
+
case 'html':
|
|
7172
|
+
return SecurityContext.HTML;
|
|
7173
|
+
case 'style':
|
|
7174
|
+
return SecurityContext.STYLE;
|
|
7175
|
+
case 'script':
|
|
7176
|
+
return SecurityContext.SCRIPT;
|
|
7177
|
+
case 'url':
|
|
7178
|
+
return SecurityContext.URL;
|
|
7179
|
+
case 'resourceUrl':
|
|
7180
|
+
return SecurityContext.RESOURCE_URL;
|
|
7181
|
+
default:
|
|
7182
|
+
throw new Error(`Invalid safe type specified: ${type}`);
|
|
7183
|
+
}
|
|
7184
|
+
})();
|
|
7185
|
+
// Preserve the published return type while returning Angular-sanitized
|
|
7186
|
+
// content instead of disabling framework security checks.
|
|
7187
|
+
return (this.sanitizer.sanitize(context, value) ?? '');
|
|
7188
|
+
}
|
|
7189
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
7190
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
|
|
7191
|
+
}
|
|
7192
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SafePipe, decorators: [{
|
|
7193
|
+
type: Pipe,
|
|
7194
|
+
args: [{
|
|
7195
|
+
name: 'safe',
|
|
7196
|
+
}]
|
|
7197
|
+
}] });
|
|
7198
|
+
|
|
7328
7199
|
class VideoStatsComponent {
|
|
7329
7200
|
constructor() {
|
|
7330
7201
|
this.collapsed = signal(false, /* @ts-ignore */
|
|
@@ -7362,8 +7233,6 @@ class VideoStatsComponent {
|
|
|
7362
7233
|
}, /* @ts-ignore */
|
|
7363
7234
|
...(ngDevMode ? [{ debugName: "videoStatus" }] : /* istanbul ignore next */ []));
|
|
7364
7235
|
this.fields = [
|
|
7365
|
-
'kalmanBitrate',
|
|
7366
|
-
'bitrateDrop',
|
|
7367
7236
|
'runTime',
|
|
7368
7237
|
'pixelRatio',
|
|
7369
7238
|
'frameHeight',
|
|
@@ -7434,11 +7303,11 @@ class VideoStatsComponent {
|
|
|
7434
7303
|
}));
|
|
7435
7304
|
}
|
|
7436
7305
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7437
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div
|
|
7306
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"graph.stat()\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div [innerHTML]=\"ssData() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"], dependencies: [{ kind: "component", type: StatGraphComponent, selector: "app-stat-graph", inputs: ["label", "stat", "color", "tickStep"] }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7438
7307
|
}
|
|
7439
7308
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VideoStatsComponent, decorators: [{
|
|
7440
7309
|
type: Component,
|
|
7441
|
-
args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, JsonPipe, SafePipe], template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div
|
|
7310
|
+
args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, JsonPipe, SafePipe], template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"graph.stat()\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div [innerHTML]=\"ssData() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"] }]
|
|
7442
7311
|
}], ctorParameters: () => [] });
|
|
7443
7312
|
|
|
7444
7313
|
class ResumeStreamButtonComponent {
|
|
@@ -7793,185 +7662,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
7793
7662
|
args: ['contextmenu', ['$event']]
|
|
7794
7663
|
}] } });
|
|
7795
7664
|
|
|
7796
|
-
class FilterSettingsComponent {
|
|
7797
|
-
constructor() {
|
|
7798
|
-
this.storageKey = 'LBMFilterSettings';
|
|
7799
|
-
this.defaultFilterModel = { ...DefaultFilterModel };
|
|
7800
|
-
this.settings = { ...DefaultFilterModel };
|
|
7801
|
-
}
|
|
7802
|
-
ngOnInit() {
|
|
7803
|
-
const savedSettings = localStorage.getItem(this.storageKey);
|
|
7804
|
-
if (savedSettings) {
|
|
7805
|
-
this.dispatch(JSON.parse(savedSettings));
|
|
7806
|
-
}
|
|
7807
|
-
}
|
|
7808
|
-
togglePanel() {
|
|
7809
|
-
this.settings.panelOpen = !this.settings.panelOpen;
|
|
7810
|
-
this.saveSettings();
|
|
7811
|
-
}
|
|
7812
|
-
saveSettings() {
|
|
7813
|
-
const valid = Object.entries(this.settings).reduce((acc, [key, value]) => {
|
|
7814
|
-
return Truthy(value) ? { ...acc, [key]: value } : acc;
|
|
7815
|
-
}, {});
|
|
7816
|
-
this.dispatch({ ...DefaultFilterModel, ...valid });
|
|
7817
|
-
}
|
|
7818
|
-
dispatch(settings) {
|
|
7819
|
-
try {
|
|
7820
|
-
localStorage.setItem(this.storageKey, JSON.stringify(settings));
|
|
7821
|
-
}
|
|
7822
|
-
catch (e) {
|
|
7823
|
-
Logger.error('Storage not available, Error saving filter settings:', e);
|
|
7824
|
-
}
|
|
7825
|
-
Object.assign(this.settings, settings);
|
|
7826
|
-
Object.assign(FilterModel, settings);
|
|
7827
|
-
}
|
|
7828
|
-
reset() {
|
|
7829
|
-
this.dispatch({
|
|
7830
|
-
...DefaultFilterModel,
|
|
7831
|
-
panelOpen: this.settings.panelOpen,
|
|
7832
|
-
});
|
|
7833
|
-
}
|
|
7834
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FilterSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7835
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FilterSettingsComponent, isStandalone: true, selector: "app-filter-settings", ngImport: i0, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7836
|
-
}
|
|
7837
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FilterSettingsComponent, decorators: [{
|
|
7838
|
-
type: Component,
|
|
7839
|
-
args: [{ selector: 'app-filter-settings', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"] }]
|
|
7840
|
-
}] });
|
|
7841
|
-
|
|
7842
|
-
class LowBandwidthStateService {
|
|
7843
|
-
constructor() {
|
|
7844
|
-
this.#isLowBandwidth = signal(false, /* @ts-ignore */
|
|
7845
|
-
...(ngDevMode ? [{ debugName: "#isLowBandwidth" }] : /* istanbul ignore next */ []));
|
|
7846
|
-
this.isLowBandwidth = this.#isLowBandwidth.asReadonly();
|
|
7847
|
-
this.isLowBandwidth$ = toObservable(this.isLowBandwidth);
|
|
7848
|
-
}
|
|
7849
|
-
#isLowBandwidth;
|
|
7850
|
-
enterLowBandwidthMode() {
|
|
7851
|
-
this.#isLowBandwidth.set(true);
|
|
7852
|
-
}
|
|
7853
|
-
exitLowBandwidthMode() {
|
|
7854
|
-
this.#isLowBandwidth.set(false);
|
|
7855
|
-
}
|
|
7856
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7857
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthStateService, providedIn: 'root' }); }
|
|
7858
|
-
}
|
|
7859
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthStateService, decorators: [{
|
|
7860
|
-
type: Injectable,
|
|
7861
|
-
args: [{ providedIn: 'root' }]
|
|
7862
|
-
}] });
|
|
7863
|
-
|
|
7864
|
-
class LowBandwidthDetectorComponent {
|
|
7865
|
-
constructor() {
|
|
7866
|
-
this.#store = inject(Store);
|
|
7867
|
-
this.dialog = inject(Dialog);
|
|
7868
|
-
this.destroyRef = inject(DestroyRef);
|
|
7869
|
-
this.videoService = inject(VideoService);
|
|
7870
|
-
this.lowBandwidthState = inject(LowBandwidthStateService);
|
|
7871
|
-
this.isDevMode = inject(DevModeService).isDevMode;
|
|
7872
|
-
this.viewportReady$ = this.#store.select(unrealFeature.selectViewportReady);
|
|
7873
|
-
this.isLowBandwidth$ = this.lowBandwidthState.isLowBandwidth$;
|
|
7874
|
-
this.lowBandwidthTrigger$ = this.isLowBandwidth$.pipe(distinctUntilChanged$1(), filter(Truthy));
|
|
7875
|
-
this.highBandwidthTrigger$ = this.isLowBandwidth$.pipe(distinctUntilChanged$1(), filter(Falsy));
|
|
7876
|
-
this.viewPortDestroyedTrigger$ = this.viewportReady$.pipe(distinctUntilChanged$1(), filter(Falsy));
|
|
7877
|
-
this.aggregatedStats$ = this.videoService.videoStats$.pipe(map((data) => data.aggregatedStats), share());
|
|
7878
|
-
this.nativeQuality$ = this.videoService.videoStats$.pipe(map((data) => data.quality));
|
|
7879
|
-
this.monitoringTrigger$ = this.viewportReady$
|
|
7880
|
-
.pipe(combineLatestWith(this.isLowBandwidth$), map(([viewPortReady, isLowBandwidth]) => viewPortReady && !isLowBandwidth))
|
|
7881
|
-
.pipe(share());
|
|
7882
|
-
this.reTrigger$ = this.monitoringTrigger$.pipe(map(() => ({ quality: 'lime' })));
|
|
7883
|
-
this.canSwitchToLowBand$ = this.monitoringTrigger$.pipe(debounceTime(100), switchMap((canSwitch) => concat(of(false), timer(FilterModel.monitoringDelayTime).pipe(map(() => canSwitch))).pipe(take(2), takeUntil$1(this.monitoringTrigger$))), distinctUntilChanged$1(), share());
|
|
7884
|
-
this.videoQuality$ = merge(this.nativeQuality$, this.reTrigger$).pipe(share(), takeUntil$1(this.viewPortDestroyedTrigger$));
|
|
7885
|
-
this.fps$ = this.aggregatedStats$.pipe(map((data) => data.framesPerSecond || 0));
|
|
7886
|
-
this.bitrateDrop$ = this.aggregatedStats$.pipe(map((data) => data.dataFlowCheckResult), filter((br) => br.isDropDetected), takeUntil$1(merge(this.viewPortDestroyedTrigger$, this.reTrigger$)));
|
|
7887
|
-
this.fps = toSignal(this.fps$, { initialValue: 0 });
|
|
7888
|
-
this.nativeQuality = toSignal(this.nativeQuality$);
|
|
7889
|
-
this.isLowBandwidth = this.lowBandwidthState.isLowBandwidth;
|
|
7890
|
-
this.aggregatedStats = toSignal(this.aggregatedStats$);
|
|
7891
|
-
this.isReducedQuality = signal(false, /* @ts-ignore */
|
|
7892
|
-
...(ngDevMode ? [{ debugName: "isReducedQuality" }] : /* istanbul ignore next */ []));
|
|
7893
|
-
this.isIndicatorExpanded = signal(false, /* @ts-ignore */
|
|
7894
|
-
...(ngDevMode ? [{ debugName: "isIndicatorExpanded" }] : /* istanbul ignore next */ []));
|
|
7895
|
-
this.isIndicatorExpandedTrigger$ = toObservable(this.isIndicatorExpanded).pipe(skip(1), distinctUntilChanged$1(), filter(Falsy));
|
|
7896
|
-
}
|
|
7897
|
-
#store;
|
|
7898
|
-
ngOnInit() {
|
|
7899
|
-
this.lowBandwidthTrigger$
|
|
7900
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7901
|
-
.subscribe(() => this.toggleIndicator(true));
|
|
7902
|
-
merge(this.viewPortDestroyedTrigger$, this.highBandwidthTrigger$)
|
|
7903
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7904
|
-
.subscribe(() => this.isReducedQuality.set(false));
|
|
7905
|
-
this.canSwitchToLowBand$
|
|
7906
|
-
.pipe(filter(Truthy), tap(() => {
|
|
7907
|
-
BITRATE_MONITOR.reset();
|
|
7908
|
-
this.isReducedQuality.set(false);
|
|
7909
|
-
}), switchMap(() => this.bitrateDrop$), tap(this.trySetLowBandwidth.bind(this)), map((dataFlowResult) => dataFlowResult?.isDropDetected), distinctUntilChanged$1(), tap(() => {
|
|
7910
|
-
this.isReducedQuality.set(true);
|
|
7911
|
-
}), switchMap(() => this.videoQuality$), filter((quality) => quality === 'lime'), tap(() => this.isReducedQuality.set(false)), takeUntilDestroyed(this.destroyRef))
|
|
7912
|
-
.subscribe();
|
|
7913
|
-
}
|
|
7914
|
-
trySetLowBandwidth(dataFlowResult) {
|
|
7915
|
-
// Evaluate FPS and native quality
|
|
7916
|
-
const isFpsLow = this.fps() < FilterModel.minimumFps;
|
|
7917
|
-
const isNativeQualityRed = this.nativeQuality() === 'red';
|
|
7918
|
-
const shouldSwitch = isNativeQualityRed || isFpsLow;
|
|
7919
|
-
// Exit if conditions are not met or already in low bandwidth
|
|
7920
|
-
if (!shouldSwitch || this.isLowBandwidth())
|
|
7921
|
-
return;
|
|
7922
|
-
const { quality, activeMedian } = dataFlowResult;
|
|
7923
|
-
const isQualityRed = quality === 'red';
|
|
7924
|
-
const isLowBitrate = activeMedian < FilterModel.minimumBitrate;
|
|
7925
|
-
// Final check for a red drop or low bitrate, or low FPS
|
|
7926
|
-
if (isQualityRed || isLowBitrate || isFpsLow) {
|
|
7927
|
-
let reason;
|
|
7928
|
-
if (isFpsLow) {
|
|
7929
|
-
reason = `FPS < ${FilterModel.minimumFps}`;
|
|
7930
|
-
}
|
|
7931
|
-
else if (quality === 'red') {
|
|
7932
|
-
reason = `Drop > ${FilterModel.redFlag}%`;
|
|
7933
|
-
}
|
|
7934
|
-
else {
|
|
7935
|
-
reason = `Speed < ${FilterModel.minimumBitrate}`;
|
|
7936
|
-
}
|
|
7937
|
-
Logger.info(`LBM DETECTED: ${reason}, switching to low bandwidth; `, dataFlowResult.message, {
|
|
7938
|
-
stats: {
|
|
7939
|
-
dataFlowResult,
|
|
7940
|
-
reason,
|
|
7941
|
-
triggerTime: this.aggregatedStats()?.runTime ?? 0,
|
|
7942
|
-
},
|
|
7943
|
-
});
|
|
7944
|
-
this.lowBandwidthState.enterLowBandwidthMode();
|
|
7945
|
-
}
|
|
7946
|
-
}
|
|
7947
|
-
toggleIndicator(value) {
|
|
7948
|
-
this.isIndicatorExpanded.set(value);
|
|
7949
|
-
if (value) {
|
|
7950
|
-
this.hideIndicatorAfterDelay();
|
|
7951
|
-
}
|
|
7952
|
-
}
|
|
7953
|
-
hideIndicatorAfterDelay() {
|
|
7954
|
-
timer(3000)
|
|
7955
|
-
.pipe(take(1), takeUntil$1(this.isIndicatorExpandedTrigger$))
|
|
7956
|
-
.subscribe(() => this.toggleIndicator(false));
|
|
7957
|
-
}
|
|
7958
|
-
openLBMDialog() {
|
|
7959
|
-
this.dialog
|
|
7960
|
-
.open(LowBandwidthModalComponent)
|
|
7961
|
-
.closed.pipe(filter(Truthy))
|
|
7962
|
-
.subscribe(() => {
|
|
7963
|
-
this.lowBandwidthState.exitLowBandwidthMode();
|
|
7964
|
-
this.toggleIndicator(false);
|
|
7965
|
-
});
|
|
7966
|
-
}
|
|
7967
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthDetectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7968
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: LowBandwidthDetectorComponent, isStandalone: true, selector: "app-low-bandwidth-detector", ngImport: i0, template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <button (click)=\"toggleIndicator(true)\" class=\"lbm-icon\" type=\"button\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </button>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:10;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"], dependencies: [{ kind: "component", type: FilterSettingsComponent, selector: "app-filter-settings" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
7969
|
-
}
|
|
7970
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: LowBandwidthDetectorComponent, decorators: [{
|
|
7971
|
-
type: Component,
|
|
7972
|
-
args: [{ selector: 'app-low-bandwidth-detector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FilterSettingsComponent], template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <button (click)=\"toggleIndicator(true)\" class=\"lbm-icon\" type=\"button\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </button>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:10;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"] }]
|
|
7973
|
-
}] });
|
|
7974
|
-
|
|
7975
7665
|
/**
|
|
7976
7666
|
* Registers the Unreal NgRx feature state. Call this ONCE at the application
|
|
7977
7667
|
* root.
|
|
@@ -8018,5 +7708,5 @@ function provideUnrealModule() {
|
|
|
8018
7708
|
* Generated bundle index. Do not edit.
|
|
8019
7709
|
*/
|
|
8020
7710
|
|
|
8021
|
-
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,
|
|
7711
|
+
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, 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, PingHandler, 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, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, 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, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, 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, smoothTransition, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
|
|
8022
7712
|
//# sourceMappingURL=3dsource-angular-unreal-module.mjs.map
|