@3dsource/angular-unreal-module 0.0.94 → 0.0.97

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, Injectable, signal, makeEnvironmentProviders, provideEnvironmentInitializer, ChangeDetectionStrategy, Component, Pipe, ElementRef, input, HostListener, Input, ViewChild, DestroyRef, computed, viewChild, effect, untracked, output, afterNextRender } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, signal, ChangeDetectionStrategy, Component, Pipe, ElementRef, input, HostListener, Input, ViewChild, DestroyRef, computed, viewChild, effect, untracked, output, afterNextRender, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
3
3
  import { filter, withLatestFrom, distinctUntilChanged, switchMap, catchError, timeout, tap, map as map$1, takeUntil, exhaustMap, debounceTime, takeWhile, delay, skip as skip$1 } from 'rxjs/operators';
4
- import { createAction, props, Store, provideState, createReducer, on, createFeature, createSelector } from '@ngrx/store';
4
+ import { createAction, props, createReducer, on, createFeature, Store, createSelector, provideState } from '@ngrx/store';
5
5
  import { concatLatestFrom } from '@ngrx/operators';
6
- import { Actions, ofType, provideEffects, createEffect } from '@ngrx/effects';
6
+ import { Actions, ofType, createEffect, provideEffects } from '@ngrx/effects';
7
7
  import { skip, share, merge, Subject, interval, of, map, from, take, fromEvent, timer, throwError, defer, Observable, switchMap as switchMap$1, retry, timeout as timeout$1, tap as tap$1, startWith, takeUntil as takeUntil$1, auditTime, EMPTY, filter as filter$1, throttleTime, debounceTime as debounceTime$1, mergeMap, scan, concatMap, animationFrameScheduler, combineLatestWith, BehaviorSubject, distinctUntilChanged as distinctUntilChanged$1, concat } from 'rxjs';
8
8
  import { Falsy, Truthy, Logger, calculateMedian, clampf, Signal, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, lerp, getCanvasCached, getSnapshot, whereNot, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB, fitIntoRectangle } from '@3dsource/utils';
9
9
  import { HttpClient } from '@angular/common/http';
@@ -312,102 +312,362 @@ const DEFAULT_RECONNECT_DELAY_MS = 1000;
312
312
  const DEFAULT_RECONNECT_ON_ICE_FAILURE = true;
313
313
  const DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE = true;
314
314
 
315
- class SubService {
316
- constructor() {
317
- this.store = inject(Store);
318
- this.disconnect$ = this.store
319
- .select(unrealFeature.selectCirrusConnected)
320
- .pipe(skip(1), filter(Falsy), share());
321
- }
322
- }
323
-
324
- class AFKService extends SubService {
325
- constructor() {
326
- super();
327
- // Optionally detect if the user is not interacting (AFK) and disconnect them.
328
- this.enabled = true; // Set to true to enable the AFK system.
329
- this.closeTimeout = DEFAULT_AFK_TIMEOUT_PERIOD; // The time after the warning when we disconnect the user.
330
- this.active = false; // Whether the AFK system is currently looking for inactivity.
331
- this.countdown = 0; // The inactivity warning overlay has a countdown to show time until disconnect.
332
- this.selectWarnTimeout = this.store.selectSignal(selectWarnTimeout);
333
- this.isViewportReady = this.store.selectSignal(unrealFeature.selectViewportReady);
334
- this.init();
335
- }
336
- init() {
337
- merge(this.store.select(selectWarnTimeout), fromSignal(UnrealInternalSignalEvents.RestAfkTimer))
338
- .pipe(withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady))
339
- .subscribe(() => this.resetAfkWarningTimer());
340
- this.store
341
- .select(unrealFeature.selectViewportReady)
342
- .pipe(distinctUntilChanged(), filter(Truthy))
343
- .subscribe(() => this.startAfkWarningTimer());
344
- this.disconnect$.subscribe(() => this.stop());
345
- }
346
- hideOverlay() {
347
- sendSignal(UnrealInternalSignalEvents.ClickableOverlay);
348
- this.store.dispatch(setAfkTimerHide());
349
- }
350
- /**
351
- * Start a timer which when elapsed will warn the user they are inactive.
352
- */
353
- startAfkWarningTimer() {
354
- this.active = this.enabled;
355
- this.resetAfkWarningTimer();
356
- }
357
- /**
358
- * If the user interacts, then reset the warning timer.
359
- */
360
- resetAfkWarningTimer() {
361
- if (this.active) {
362
- this.clearTimers();
363
- this.warnTimer = setTimeout(() => {
364
- this.showAfkOverlay();
365
- }, this.selectWarnTimeout() * 1000);
366
- }
315
+ const initialState = {
316
+ streamRequestContext: null,
317
+ environmentId: null,
318
+ lowBandwidthStats: undefined,
319
+ wasInitialized: false,
320
+ isFirstSuccessLoad: false,
321
+ isAfkTimerVisible: false,
322
+ lowBandwidth: false,
323
+ cirrusConnected: false,
324
+ establishingConnection: false,
325
+ isReconnecting: false,
326
+ viewportReady: false,
327
+ dataChannelConnected: false,
328
+ isVideoPlaying: false,
329
+ statusPercentSignallingServer: null,
330
+ statusMessage: null,
331
+ errorMessage: null,
332
+ ssInfo: null,
333
+ ssData: null,
334
+ streamResolution: { width: null, height: null },
335
+ freezeFrameFromVideo: { dataUrl: null, progress: null },
336
+ freezeFrame: { dataUrl: null, progress: null },
337
+ disconnectReason: DisconnectReason.None,
338
+ awsInstance: {
339
+ wsUrl: null,
340
+ instanceName: null,
341
+ pollingUrl: null,
342
+ progressComplete: 0,
343
+ },
344
+ streamConfig: {
345
+ autoStart: true,
346
+ warnTimeout: DEFAULT_AFK_TIMEOUT,
347
+ },
348
+ loaderCommands: {
349
+ commandsInProgress: [],
350
+ totalCommandsStarted: 0,
351
+ totalCommandsCompleted: 0,
352
+ },
353
+ matchUrls: [],
354
+ streamClientCompanyId: '',
355
+ streamViewId: 'default',
356
+ videoIntroSrc: null,
357
+ imageIntroSrc: null,
358
+ imageLoadingSrc: '',
359
+ analyticsEvent: null,
360
+ };
361
+ const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state, { lowBandwidth, stats }) => ({
362
+ ...state,
363
+ lowBandwidth: lowBandwidth,
364
+ lowBandwidthStats: lowBandwidth ? stats : undefined,
365
+ })), on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
366
+ return { ...state, isVideoPlaying: isVideoPlaying };
367
+ }), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl }) => {
368
+ return { ...state, awsInstance: { instanceName, wsUrl, pollingUrl } };
369
+ }), on(setViewportReady, (state) => {
370
+ return {
371
+ ...state,
372
+ viewportReady: true,
373
+ statusMessage: null,
374
+ errorMessage: null,
375
+ };
376
+ }), on(setViewportNotReady, (state) => {
377
+ return { ...state, viewportReady: false };
378
+ }), on(updateCirrusInfo, (state, { ssInfo, ssData }) => {
379
+ return {
380
+ ...state,
381
+ ssInfo: ssInfo, // For back compatibility
382
+ ssData: ssData, // Contains all the data from the ssInfo as an object
383
+ };
384
+ }), on(changeStreamResolutionSuccessAction, (state, { width, height }) => {
385
+ return { ...state, streamResolution: { width: width, height: height } };
386
+ }), on(setFreezeFrame, (state, freezeFrame) => {
387
+ return {
388
+ ...state,
389
+ freezeFrame: {
390
+ dataUrl: freezeFrame.progress === 0 ||
391
+ freezeFrame.progress === 1 ||
392
+ freezeFrame.progress === null
393
+ ? freezeFrame.dataUrl
394
+ : state.freezeFrame.dataUrl,
395
+ progress: freezeFrame.progress || null,
396
+ },
397
+ };
398
+ }), on(disconnectStream, (state, errorMessage) => {
399
+ if (state.dataChannelConnected ||
400
+ state.disconnectReason === DisconnectReason.Destroy) {
401
+ return state;
367
402
  }
368
- /**
369
- * Update the count-down spans number for the overlay
370
- * @param countdown the count down number to be inserted into the span for updating
371
- */
372
- updateCountDown(countdown) {
373
- this.dispatchMessage(String(countdown));
403
+ return {
404
+ ...state,
405
+ errorMessage,
406
+ statusMessage: null,
407
+ wasInitialized: true,
408
+ };
409
+ }), on(setFreezeFrameFromVideo, (state, freezeFrameFromVideo) => {
410
+ return {
411
+ ...state,
412
+ freezeFrameFromVideo: {
413
+ dataUrl: freezeFrameFromVideo.dataUrl,
414
+ progress: freezeFrameFromVideo.progress || null,
415
+ },
416
+ };
417
+ }), on(setStatusMessage, (state, { statusMessage }) => {
418
+ return { ...state, statusMessage };
419
+ }), on(setEstablishingConnection, (state, { value }) => {
420
+ return { ...state, establishingConnection: value };
421
+ }), on(setStatusPercentSignallingServer, (state, { percent }) => {
422
+ return { ...state, statusPercentSignallingServer: percent };
423
+ }), on(dataChannelConnected, (state, { statusMessage }) => {
424
+ return {
425
+ ...state,
426
+ statusMessage,
427
+ dataChannelConnected: true,
428
+ establishingConnection: false,
429
+ wasInitialized: true,
430
+ };
431
+ }), on(setConfig, startStream, (state, { config }) => {
432
+ return { ...state, streamConfig: { ...state.streamConfig, ...config } };
433
+ }), on(resetConfig, (state) => {
434
+ return { ...state, streamConfig: initialState.streamConfig };
435
+ }), on(resetWarnTimeout, (state) => {
436
+ return {
437
+ ...state,
438
+ streamConfig: { ...state.streamConfig, warnTimeout: DEFAULT_AFK_TIMEOUT },
439
+ };
440
+ }), on(initSignalling, (state, { resetDisconnectionReason = true }) => {
441
+ return {
442
+ ...state,
443
+ disconnectReason: resetDisconnectionReason
444
+ ? DisconnectReason.None
445
+ : state.disconnectReason,
446
+ };
447
+ }), on(destroyRemoteConnections, (state, { reason }) => {
448
+ if (state.disconnectReason === DisconnectReason.Destroy) {
449
+ return state;
374
450
  }
375
- /**
376
- * Update the text overlays inner text
377
- * @param message the update text to be inserted into the overlay
378
- */
379
- dispatchMessage(message) {
380
- sendSignal(UnrealInternalSignalEvents.ClickableOverlay, {
451
+ return { ...state, disconnectReason: reason };
452
+ }), on(reconnectPeerSuccess, (state) => {
453
+ return { ...state, isReconnecting: false };
454
+ }), on(setSignalingName, (state, { instanceName }) => {
455
+ return { ...state, awsInstance: { ...state.awsInstance, instanceName } };
456
+ }), on(setOrchestrationParameters, (state, { instanceName, streamRequestId, eta }) => {
457
+ return {
458
+ ...state,
459
+ awsInstance: {
460
+ ...state.awsInstance,
461
+ message: undefined,
462
+ instanceName,
463
+ streamRequestId,
464
+ eta,
465
+ },
466
+ };
467
+ }), on(setOrchestrationProgress, (state, { progressComplete }) => {
468
+ return {
469
+ ...state,
470
+ awsInstance: {
471
+ ...state.awsInstance,
472
+ progressComplete,
473
+ },
474
+ };
475
+ }), on(setOrchestrationMessage, (state, { message }) => {
476
+ return {
477
+ ...state,
478
+ awsInstance: {
479
+ ...state.awsInstance,
381
480
  message,
382
- isActivityDetected: true,
383
- className: 'clickableState',
384
- onOverlayClick: () => this.reset(),
385
- });
386
- }
387
- clearTimers() {
388
- clearInterval(this.countdownTimer);
389
- clearTimeout(this.warnTimer);
390
- }
391
- stop() {
392
- this.clearTimers();
393
- this.hideOverlay();
394
- }
395
- reset() {
396
- this.hideOverlay();
397
- clearInterval(this.countdownTimer);
398
- this.startAfkWarningTimer();
399
- }
400
- showAfkOverlay() {
401
- if (!this.isViewportReady()) {
402
- return;
403
- }
404
- // Pause the timer while the user is looking at the inactivity warning overlay.
405
- this.active = false;
406
- this.countdown = this.closeTimeout;
407
- this.store.dispatch(setAfkTimerVisible());
408
- this.updateCountDown(this.countdown);
409
- if (InputOptions.controlScheme === EControlSchemeType.LockedMouse) {
410
- document.exitPointerLock();
481
+ },
482
+ };
483
+ }), on(setLoopBackCommandIsCompleted, (state) => {
484
+ return { ...state, isFirstSuccessLoad: true };
485
+ }), on(setAfkTimerVisible, (state) => {
486
+ return { ...state, isAfkTimerVisible: true };
487
+ }), on(setAfkTimerHide, (state) => {
488
+ return { ...state, isAfkTimerVisible: false };
489
+ }), on(setOrchestrationContext, (state, { urls, environmentId, streamRequestContext }) => {
490
+ return {
491
+ ...state,
492
+ matchUrls: urls,
493
+ environmentId,
494
+ streamRequestContext,
495
+ };
496
+ }), on(setStreamClientCompanyId, (state, { id }) => {
497
+ return { ...state, streamClientCompanyId: id };
498
+ }), on(setStreamViewId, (state, { id }) => {
499
+ return { ...state, streamViewId: id };
500
+ }), on(setIntroImageSrc, (state, { src }) => {
501
+ return { ...state, imageIntroSrc: src };
502
+ }), on(setLoadingImageSrc, (state, { src }) => {
503
+ return { ...state, imageLoadingSrc: src };
504
+ }), on(setIntroVideoSrc, (state, { src }) => {
505
+ return { ...state, videoIntroSrc: src };
506
+ }), on(resetIntroSrc, (state) => {
507
+ return { ...state, imageIntroSrc: '', videoIntroSrc: '' };
508
+ }), on(saveAnalyticsEvent, (state, { event }) => {
509
+ return { ...state, analyticsEvent: event };
510
+ }), on(commandStarted, (state, { id, command }) => {
511
+ return {
512
+ ...state,
513
+ loaderCommands: {
514
+ ...state.loaderCommands,
515
+ totalCommandsStarted: state.loaderCommands.totalCommandsStarted + 1,
516
+ commandsInProgress: [
517
+ ...state.loaderCommands.commandsInProgress,
518
+ { id, command, timeStamp: new Date().getTime() },
519
+ ],
520
+ },
521
+ };
522
+ }), on(commandCompleted, (state, { id }) => {
523
+ return {
524
+ ...state,
525
+ loaderCommands: removeExileCommands(state.loaderCommands, id),
526
+ };
527
+ }), on(setCirrusConnected, (state) => {
528
+ return { ...state, cirrusConnected: true };
529
+ }), on(setCirrusDisconnected, (state) => {
530
+ return {
531
+ ...initialState,
532
+ establishingConnection: state.establishingConnection,
533
+ disconnectReason: state.disconnectReason,
534
+ wasInitialized: state.wasInitialized,
535
+ isFirstSuccessLoad: state.isFirstSuccessLoad,
536
+ matchUrls: state.matchUrls,
537
+ streamClientCompanyId: state.streamClientCompanyId,
538
+ streamViewId: state.streamViewId,
539
+ imageIntroSrc: state.imageIntroSrc,
540
+ videoIntroSrc: state.videoIntroSrc,
541
+ imageLoadingSrc: state.imageLoadingSrc,
542
+ environmentId: state.environmentId,
543
+ streamRequestContext: state.streamRequestContext,
544
+ streamConfig: state.streamConfig,
545
+ };
546
+ }), on(dropConnection, (state) => {
547
+ return { ...state, establishingConnection: false };
548
+ }), on(resetDataChannelForReconnect, (state) => {
549
+ return {
550
+ ...state,
551
+ isReconnecting: true,
552
+ dataChannelConnected: false,
553
+ viewportReady: false,
554
+ statusMessage: 'Reconnecting...',
555
+ };
556
+ }), on(setUnrealPlaywrightConfig, (state) => {
557
+ return {
558
+ ...state,
559
+ wasInitialized: true,
560
+ isFirstSuccessLoad: true,
561
+ cirrusConnected: true,
562
+ viewportReady: true,
563
+ dataChannelConnected: true,
564
+ isVideoPlaying: true,
565
+ };
566
+ }), on(destroyUnrealScene, () => {
567
+ return initialState;
568
+ }));
569
+
570
+ const unrealFeature = createFeature({
571
+ name: 'unrealFeature',
572
+ reducer: unrealReducer,
573
+ });
574
+
575
+ class SubService {
576
+ constructor() {
577
+ this.store = inject(Store);
578
+ this.disconnect$ = this.store
579
+ .select(unrealFeature.selectCirrusConnected)
580
+ .pipe(skip(1), filter(Falsy), share());
581
+ }
582
+ }
583
+
584
+ class AFKService extends SubService {
585
+ constructor() {
586
+ super();
587
+ // Optionally detect if the user is not interacting (AFK) and disconnect them.
588
+ this.enabled = true; // Set to true to enable the AFK system.
589
+ this.closeTimeout = DEFAULT_AFK_TIMEOUT_PERIOD; // The time after the warning when we disconnect the user.
590
+ this.active = false; // Whether the AFK system is currently looking for inactivity.
591
+ this.countdown = 0; // The inactivity warning overlay has a countdown to show time until disconnect.
592
+ this.selectWarnTimeout = this.store.selectSignal(selectWarnTimeout);
593
+ this.isViewportReady = this.store.selectSignal(unrealFeature.selectViewportReady);
594
+ this.init();
595
+ }
596
+ init() {
597
+ merge(this.store.select(selectWarnTimeout), fromSignal(UnrealInternalSignalEvents.RestAfkTimer))
598
+ .pipe(withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady))
599
+ .subscribe(() => this.resetAfkWarningTimer());
600
+ this.store
601
+ .select(unrealFeature.selectViewportReady)
602
+ .pipe(distinctUntilChanged(), filter(Truthy))
603
+ .subscribe(() => this.startAfkWarningTimer());
604
+ this.disconnect$.subscribe(() => this.stop());
605
+ }
606
+ hideOverlay() {
607
+ sendSignal(UnrealInternalSignalEvents.ClickableOverlay);
608
+ this.store.dispatch(setAfkTimerHide());
609
+ }
610
+ /**
611
+ * Start a timer which when elapsed will warn the user they are inactive.
612
+ */
613
+ startAfkWarningTimer() {
614
+ this.active = this.enabled;
615
+ this.resetAfkWarningTimer();
616
+ }
617
+ /**
618
+ * If the user interacts, then reset the warning timer.
619
+ */
620
+ resetAfkWarningTimer() {
621
+ if (this.active) {
622
+ this.clearTimers();
623
+ this.warnTimer = setTimeout(() => {
624
+ this.showAfkOverlay();
625
+ }, this.selectWarnTimeout() * 1000);
626
+ }
627
+ }
628
+ /**
629
+ * Update the count-down spans number for the overlay
630
+ * @param countdown the count down number to be inserted into the span for updating
631
+ */
632
+ updateCountDown(countdown) {
633
+ this.dispatchMessage(String(countdown));
634
+ }
635
+ /**
636
+ * Update the text overlays inner text
637
+ * @param message the update text to be inserted into the overlay
638
+ */
639
+ dispatchMessage(message) {
640
+ sendSignal(UnrealInternalSignalEvents.ClickableOverlay, {
641
+ message,
642
+ isActivityDetected: true,
643
+ className: 'clickableState',
644
+ onOverlayClick: () => this.reset(),
645
+ });
646
+ }
647
+ clearTimers() {
648
+ clearInterval(this.countdownTimer);
649
+ clearTimeout(this.warnTimer);
650
+ }
651
+ stop() {
652
+ this.clearTimers();
653
+ this.hideOverlay();
654
+ }
655
+ reset() {
656
+ this.hideOverlay();
657
+ clearInterval(this.countdownTimer);
658
+ this.startAfkWarningTimer();
659
+ }
660
+ showAfkOverlay() {
661
+ if (!this.isViewportReady()) {
662
+ return;
663
+ }
664
+ // Pause the timer while the user is looking at the inactivity warning overlay.
665
+ this.active = false;
666
+ this.countdown = this.closeTimeout;
667
+ this.store.dispatch(setAfkTimerVisible());
668
+ this.updateCountDown(this.countdown);
669
+ if (InputOptions.controlScheme === EControlSchemeType.LockedMouse) {
670
+ document.exitPointerLock();
411
671
  }
412
672
  this.countdownTimer = setInterval(() => {
413
673
  this.countdown--;
@@ -3809,398 +4069,60 @@ class FpsMonitorService {
3809
4069
  if (newTarget !== this.currentFpsTarget) {
3810
4070
  this.currentFpsTarget = newTarget;
3811
4071
  this.store.dispatch(setMaxFps({ maxFps: newTarget }));
3812
- }
3813
- this.lastDecisionTime = now;
3814
- this.samples = [];
3815
- }
3816
- /**
3817
- * Get the next FPS target based on current target and decision.
3818
- * Clamps to valid FPS_STEPS range.
3819
- */
3820
- getNextFpsTarget(currentTarget, decision) {
3821
- const currentIndex = this.FPS_STEPS.indexOf(currentTarget);
3822
- // If current target is not in FPS_STEPS, find closest one
3823
- const validIndex = currentIndex === -1
3824
- ? this.findClosestFpsIndex(currentTarget)
3825
- : currentIndex;
3826
- const delta = decision === 'increase' ? 1 : -1;
3827
- const newIndex = clampf(0, this.FPS_STEPS.length - 1, validIndex + delta);
3828
- return this.FPS_STEPS[newIndex];
3829
- }
3830
- /**
3831
- * Find the closest FPS step index for a given target value.
3832
- */
3833
- findClosestFpsIndex(target) {
3834
- let closestIndex = 0;
3835
- let closestDiff = Math.abs(this.FPS_STEPS[0] - target);
3836
- for (let i = 1; i < this.FPS_STEPS.length; i++) {
3837
- const diff = Math.abs(this.FPS_STEPS[i] - target);
3838
- if (diff < closestDiff) {
3839
- closestDiff = diff;
3840
- closestIndex = i;
3841
- }
3842
- }
3843
- return closestIndex;
3844
- }
3845
- /** Average all collected samples for the current evaluation window. */
3846
- averageSamples() {
3847
- const len = this.samples.length;
3848
- if (len === 0)
3849
- return { fps: 0, bitrate: 0, qp: 0 };
3850
- let fps = 0;
3851
- let bitrate = 0;
3852
- let qp = 0;
3853
- for (const s of this.samples) {
3854
- fps += s.fps;
3855
- bitrate += s.bitrate;
3856
- qp += s.qp;
3857
- }
3858
- return { fps: fps / len, bitrate: bitrate / len, qp: qp / len };
3859
- }
3860
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3861
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService }); }
3862
- }
3863
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService, decorators: [{
3864
- type: Injectable
3865
- }], ctorParameters: () => [] });
3866
-
3867
- function provideAngularUnrealModule(config) {
3868
- return makeEnvironmentProviders([
3869
- provideState(unrealFeature),
3870
- provideEffects([UnrealEffects]),
3871
- ConsoleExtensionsService,
3872
- InputService,
3873
- VideoService,
3874
- WebRtcPlayerService,
3875
- RegionsPingService,
3876
- FileReceiverService,
3877
- FileHandlerService,
3878
- FpsMonitorService,
3879
- AnalyticsService,
3880
- {
3881
- provide: StreamStatusTelemetryService,
3882
- useClass: config?.playwright
3883
- ? StreamStatusTelemetryPlaywrightService
3884
- : StreamStatusTelemetryService,
3885
- },
3886
- {
3887
- provide: CommandTelemetryService,
3888
- useClass: config?.playwright
3889
- ? CommandTelemetryPlaywrightService
3890
- : CommandTelemetryService,
3891
- },
3892
- {
3893
- provide: AggregatorService,
3894
- useClass: config?.playwright
3895
- ? AggregatorPlaywrightService
3896
- : AggregatorService,
3897
- },
3898
- {
3899
- provide: FreezeFrameService,
3900
- useClass: config?.playwright
3901
- ? FreezeFramePlaywrightService
3902
- : FreezeFrameService,
3903
- },
3904
- {
3905
- provide: UnrealCommunicatorService,
3906
- useClass: config?.playwright
3907
- ? UnrealCommunicatorPlaywrightService
3908
- : UnrealCommunicatorService,
3909
- },
3910
- {
3911
- provide: AFKService,
3912
- useClass: config?.playwright ? AfkPlaywrightService : AFKService,
3913
- },
3914
- {
3915
- provide: SignallingService,
3916
- useClass: config?.playwright
3917
- ? SignallingPlaywrightService
3918
- : SignallingService,
3919
- },
3920
- {
3921
- provide: ConsoleExtensionsService,
3922
- useClass: config?.playwright
3923
- ? ConsoleExtensionsPlaywrightService
3924
- : ConsoleExtensionsService,
3925
- },
3926
- {
3927
- provide: FileReceiverService,
3928
- useClass: config?.playwright
3929
- ? FileReceiverPlaywrightService
3930
- : FileReceiverService,
3931
- },
3932
- provideEnvironmentInitializer(() => {
3933
- inject(AggregatorService);
3934
- inject(InputService);
3935
- inject(StreamStatusTelemetryService);
3936
- inject(ConsoleExtensionsService);
3937
- inject(AFKService);
3938
- inject(FreezeFrameService);
3939
- inject(AnalyticsService);
3940
- inject(FpsMonitorService);
3941
- }),
3942
- ]);
3943
- }
3944
-
3945
- const initialState = {
3946
- streamRequestContext: null,
3947
- environmentId: null,
3948
- lowBandwidthStats: undefined,
3949
- wasInitialized: false,
3950
- isFirstSuccessLoad: false,
3951
- isAfkTimerVisible: false,
3952
- lowBandwidth: false,
3953
- cirrusConnected: false,
3954
- establishingConnection: false,
3955
- isReconnecting: false,
3956
- viewportReady: false,
3957
- dataChannelConnected: false,
3958
- isVideoPlaying: false,
3959
- statusPercentSignallingServer: null,
3960
- statusMessage: null,
3961
- errorMessage: null,
3962
- ssInfo: null,
3963
- ssData: null,
3964
- streamResolution: { width: null, height: null },
3965
- freezeFrameFromVideo: { dataUrl: null, progress: null },
3966
- freezeFrame: { dataUrl: null, progress: null },
3967
- disconnectReason: DisconnectReason.None,
3968
- awsInstance: {
3969
- wsUrl: null,
3970
- instanceName: null,
3971
- pollingUrl: null,
3972
- progressComplete: 0,
3973
- },
3974
- streamConfig: {
3975
- autoStart: true,
3976
- warnTimeout: DEFAULT_AFK_TIMEOUT,
3977
- },
3978
- loaderCommands: {
3979
- commandsInProgress: [],
3980
- totalCommandsStarted: 0,
3981
- totalCommandsCompleted: 0,
3982
- },
3983
- matchUrls: [],
3984
- streamClientCompanyId: '',
3985
- streamViewId: 'default',
3986
- videoIntroSrc: null,
3987
- imageIntroSrc: null,
3988
- imageLoadingSrc: '',
3989
- analyticsEvent: null,
3990
- };
3991
- const unrealReducer = createReducer(initialState, on(changeLowBandwidth, (state, { lowBandwidth, stats }) => ({
3992
- ...state,
3993
- lowBandwidth: lowBandwidth,
3994
- lowBandwidthStats: lowBandwidth ? stats : undefined,
3995
- })), on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
3996
- return { ...state, isVideoPlaying: isVideoPlaying };
3997
- }), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl }) => {
3998
- return { ...state, awsInstance: { instanceName, wsUrl, pollingUrl } };
3999
- }), on(setViewportReady, (state) => {
4000
- return {
4001
- ...state,
4002
- viewportReady: true,
4003
- statusMessage: null,
4004
- errorMessage: null,
4005
- };
4006
- }), on(setViewportNotReady, (state) => {
4007
- return { ...state, viewportReady: false };
4008
- }), on(updateCirrusInfo, (state, { ssInfo, ssData }) => {
4009
- return {
4010
- ...state,
4011
- ssInfo: ssInfo, // For back compatibility
4012
- ssData: ssData, // Contains all the data from the ssInfo as an object
4013
- };
4014
- }), on(changeStreamResolutionSuccessAction, (state, { width, height }) => {
4015
- return { ...state, streamResolution: { width: width, height: height } };
4016
- }), on(setFreezeFrame, (state, freezeFrame) => {
4017
- return {
4018
- ...state,
4019
- freezeFrame: {
4020
- dataUrl: freezeFrame.progress === 0 ||
4021
- freezeFrame.progress === 1 ||
4022
- freezeFrame.progress === null
4023
- ? freezeFrame.dataUrl
4024
- : state.freezeFrame.dataUrl,
4025
- progress: freezeFrame.progress || null,
4026
- },
4027
- };
4028
- }), on(disconnectStream, (state, errorMessage) => {
4029
- if (state.dataChannelConnected ||
4030
- state.disconnectReason === DisconnectReason.Destroy) {
4031
- return state;
4032
- }
4033
- return {
4034
- ...state,
4035
- errorMessage,
4036
- statusMessage: null,
4037
- wasInitialized: true,
4038
- };
4039
- }), on(setFreezeFrameFromVideo, (state, freezeFrameFromVideo) => {
4040
- return {
4041
- ...state,
4042
- freezeFrameFromVideo: {
4043
- dataUrl: freezeFrameFromVideo.dataUrl,
4044
- progress: freezeFrameFromVideo.progress || null,
4045
- },
4046
- };
4047
- }), on(setStatusMessage, (state, { statusMessage }) => {
4048
- return { ...state, statusMessage };
4049
- }), on(setEstablishingConnection, (state, { value }) => {
4050
- return { ...state, establishingConnection: value };
4051
- }), on(setStatusPercentSignallingServer, (state, { percent }) => {
4052
- return { ...state, statusPercentSignallingServer: percent };
4053
- }), on(dataChannelConnected, (state, { statusMessage }) => {
4054
- return {
4055
- ...state,
4056
- statusMessage,
4057
- dataChannelConnected: true,
4058
- establishingConnection: false,
4059
- wasInitialized: true,
4060
- };
4061
- }), on(setConfig, startStream, (state, { config }) => {
4062
- return { ...state, streamConfig: { ...state.streamConfig, ...config } };
4063
- }), on(resetConfig, (state) => {
4064
- return { ...state, streamConfig: initialState.streamConfig };
4065
- }), on(resetWarnTimeout, (state) => {
4066
- return {
4067
- ...state,
4068
- streamConfig: { ...state.streamConfig, warnTimeout: DEFAULT_AFK_TIMEOUT },
4069
- };
4070
- }), on(initSignalling, (state, { resetDisconnectionReason = true }) => {
4071
- return {
4072
- ...state,
4073
- disconnectReason: resetDisconnectionReason
4074
- ? DisconnectReason.None
4075
- : state.disconnectReason,
4076
- };
4077
- }), on(destroyRemoteConnections, (state, { reason }) => {
4078
- if (state.disconnectReason === DisconnectReason.Destroy) {
4079
- return state;
4072
+ }
4073
+ this.lastDecisionTime = now;
4074
+ this.samples = [];
4080
4075
  }
4081
- return { ...state, disconnectReason: reason };
4082
- }), on(reconnectPeerSuccess, (state) => {
4083
- return { ...state, isReconnecting: false };
4084
- }), on(setSignalingName, (state, { instanceName }) => {
4085
- return { ...state, awsInstance: { ...state.awsInstance, instanceName } };
4086
- }), on(setOrchestrationParameters, (state, { instanceName, streamRequestId, eta }) => {
4087
- return {
4088
- ...state,
4089
- awsInstance: {
4090
- ...state.awsInstance,
4091
- message: undefined,
4092
- instanceName,
4093
- streamRequestId,
4094
- eta,
4095
- },
4096
- };
4097
- }), on(setOrchestrationProgress, (state, { progressComplete }) => {
4098
- return {
4099
- ...state,
4100
- awsInstance: {
4101
- ...state.awsInstance,
4102
- progressComplete,
4103
- },
4104
- };
4105
- }), on(setOrchestrationMessage, (state, { message }) => {
4106
- return {
4107
- ...state,
4108
- awsInstance: {
4109
- ...state.awsInstance,
4110
- message,
4111
- },
4112
- };
4113
- }), on(setLoopBackCommandIsCompleted, (state) => {
4114
- return { ...state, isFirstSuccessLoad: true };
4115
- }), on(setAfkTimerVisible, (state) => {
4116
- return { ...state, isAfkTimerVisible: true };
4117
- }), on(setAfkTimerHide, (state) => {
4118
- return { ...state, isAfkTimerVisible: false };
4119
- }), on(setOrchestrationContext, (state, { urls, environmentId, streamRequestContext }) => {
4120
- return {
4121
- ...state,
4122
- matchUrls: urls,
4123
- environmentId,
4124
- streamRequestContext,
4125
- };
4126
- }), on(setStreamClientCompanyId, (state, { id }) => {
4127
- return { ...state, streamClientCompanyId: id };
4128
- }), on(setStreamViewId, (state, { id }) => {
4129
- return { ...state, streamViewId: id };
4130
- }), on(setIntroImageSrc, (state, { src }) => {
4131
- return { ...state, imageIntroSrc: src };
4132
- }), on(setLoadingImageSrc, (state, { src }) => {
4133
- return { ...state, imageLoadingSrc: src };
4134
- }), on(setIntroVideoSrc, (state, { src }) => {
4135
- return { ...state, videoIntroSrc: src };
4136
- }), on(resetIntroSrc, (state) => {
4137
- return { ...state, imageIntroSrc: '', videoIntroSrc: '' };
4138
- }), on(saveAnalyticsEvent, (state, { event }) => {
4139
- return { ...state, analyticsEvent: event };
4140
- }), on(commandStarted, (state, { id, command }) => {
4141
- return {
4142
- ...state,
4143
- loaderCommands: {
4144
- ...state.loaderCommands,
4145
- totalCommandsStarted: state.loaderCommands.totalCommandsStarted + 1,
4146
- commandsInProgress: [
4147
- ...state.loaderCommands.commandsInProgress,
4148
- { id, command, timeStamp: new Date().getTime() },
4149
- ],
4150
- },
4151
- };
4152
- }), on(commandCompleted, (state, { id }) => {
4153
- return {
4154
- ...state,
4155
- loaderCommands: removeExileCommands(state.loaderCommands, id),
4156
- };
4157
- }), on(setCirrusConnected, (state) => {
4158
- return { ...state, cirrusConnected: true };
4159
- }), on(setCirrusDisconnected, (state) => {
4160
- return {
4161
- ...initialState,
4162
- establishingConnection: state.establishingConnection,
4163
- disconnectReason: state.disconnectReason,
4164
- wasInitialized: state.wasInitialized,
4165
- isFirstSuccessLoad: state.isFirstSuccessLoad,
4166
- matchUrls: state.matchUrls,
4167
- streamClientCompanyId: state.streamClientCompanyId,
4168
- streamViewId: state.streamViewId,
4169
- imageIntroSrc: state.imageIntroSrc,
4170
- videoIntroSrc: state.videoIntroSrc,
4171
- imageLoadingSrc: state.imageLoadingSrc,
4172
- environmentId: state.environmentId,
4173
- streamRequestContext: state.streamRequestContext,
4174
- streamConfig: state.streamConfig,
4175
- };
4176
- }), on(dropConnection, (state) => {
4177
- return { ...state, establishingConnection: false };
4178
- }), on(resetDataChannelForReconnect, (state) => {
4179
- return {
4180
- ...state,
4181
- isReconnecting: true,
4182
- dataChannelConnected: false,
4183
- viewportReady: false,
4184
- statusMessage: 'Reconnecting...',
4185
- };
4186
- }), on(setUnrealPlaywrightConfig, (state) => {
4187
- return {
4188
- ...state,
4189
- wasInitialized: true,
4190
- isFirstSuccessLoad: true,
4191
- cirrusConnected: true,
4192
- viewportReady: true,
4193
- dataChannelConnected: true,
4194
- isVideoPlaying: true,
4195
- };
4196
- }), on(destroyUnrealScene, () => {
4197
- return initialState;
4198
- }));
4199
-
4200
- const unrealFeature = createFeature({
4201
- name: 'unrealFeature',
4202
- reducer: unrealReducer,
4203
- });
4076
+ /**
4077
+ * Get the next FPS target based on current target and decision.
4078
+ * Clamps to valid FPS_STEPS range.
4079
+ */
4080
+ getNextFpsTarget(currentTarget, decision) {
4081
+ const currentIndex = this.FPS_STEPS.indexOf(currentTarget);
4082
+ // If current target is not in FPS_STEPS, find closest one
4083
+ const validIndex = currentIndex === -1
4084
+ ? this.findClosestFpsIndex(currentTarget)
4085
+ : currentIndex;
4086
+ const delta = decision === 'increase' ? 1 : -1;
4087
+ const newIndex = clampf(0, this.FPS_STEPS.length - 1, validIndex + delta);
4088
+ return this.FPS_STEPS[newIndex];
4089
+ }
4090
+ /**
4091
+ * Find the closest FPS step index for a given target value.
4092
+ */
4093
+ findClosestFpsIndex(target) {
4094
+ let closestIndex = 0;
4095
+ let closestDiff = Math.abs(this.FPS_STEPS[0] - target);
4096
+ for (let i = 1; i < this.FPS_STEPS.length; i++) {
4097
+ const diff = Math.abs(this.FPS_STEPS[i] - target);
4098
+ if (diff < closestDiff) {
4099
+ closestDiff = diff;
4100
+ closestIndex = i;
4101
+ }
4102
+ }
4103
+ return closestIndex;
4104
+ }
4105
+ /** Average all collected samples for the current evaluation window. */
4106
+ averageSamples() {
4107
+ const len = this.samples.length;
4108
+ if (len === 0)
4109
+ return { fps: 0, bitrate: 0, qp: 0 };
4110
+ let fps = 0;
4111
+ let bitrate = 0;
4112
+ let qp = 0;
4113
+ for (const s of this.samples) {
4114
+ fps += s.fps;
4115
+ bitrate += s.bitrate;
4116
+ qp += s.qp;
4117
+ }
4118
+ return { fps: fps / len, bitrate: bitrate / len, qp: qp / len };
4119
+ }
4120
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4121
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService }); }
4122
+ }
4123
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FpsMonitorService, decorators: [{
4124
+ type: Injectable
4125
+ }], ctorParameters: () => [] });
4204
4126
 
4205
4127
  class UnrealErrorModalComponent {
4206
4128
  constructor() {
@@ -4945,7 +4867,7 @@ class LatencyTimings {
4945
4867
  OnAllLatencyTimingsReady(_) { }
4946
4868
  }
4947
4869
 
4948
- function mapQpToQuality(VideoEncoderQP) {
4870
+ const mapQpToQuality = (VideoEncoderQP) => {
4949
4871
  const orangeQP = 26;
4950
4872
  const redQP = 35;
4951
4873
  let quality = 'lime';
@@ -4956,17 +4878,17 @@ function mapQpToQuality(VideoEncoderQP) {
4956
4878
  quality = 'orange';
4957
4879
  }
4958
4880
  return quality;
4959
- }
4881
+ };
4960
4882
 
4961
4883
  const removeExileCommands = (state, idToRemove) => {
4962
- const exileTimout = 10000;
4884
+ const exileTimeout = 10000;
4963
4885
  const out = { ...state };
4964
4886
  out.commandsInProgress = out.commandsInProgress.filter(whereNot({ id: idToRemove }));
4965
4887
  out.totalCommandsCompleted++;
4966
4888
  const time = new Date().getTime();
4967
4889
  const markForDelete = [];
4968
4890
  Object.entries(out.commandsInProgress).forEach(([, { id, timeStamp }]) => {
4969
- if (time - timeStamp > exileTimout) {
4891
+ if (time - timeStamp > exileTimeout) {
4970
4892
  markForDelete.push(id);
4971
4893
  }
4972
4894
  });
@@ -5915,6 +5837,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
5915
5837
  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 <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\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 </div>\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);-webkit-text-decoration-line:underline;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"] }]
5916
5838
  }] });
5917
5839
 
5840
+ function provideAngularUnrealModule(config) {
5841
+ return makeEnvironmentProviders([
5842
+ provideState(unrealFeature),
5843
+ provideEffects([UnrealEffects]),
5844
+ ConsoleExtensionsService,
5845
+ InputService,
5846
+ VideoService,
5847
+ WebRtcPlayerService,
5848
+ RegionsPingService,
5849
+ FileReceiverService,
5850
+ FileHandlerService,
5851
+ FpsMonitorService,
5852
+ AnalyticsService,
5853
+ {
5854
+ provide: StreamStatusTelemetryService,
5855
+ useClass: config?.playwright
5856
+ ? StreamStatusTelemetryPlaywrightService
5857
+ : StreamStatusTelemetryService,
5858
+ },
5859
+ {
5860
+ provide: CommandTelemetryService,
5861
+ useClass: config?.playwright
5862
+ ? CommandTelemetryPlaywrightService
5863
+ : CommandTelemetryService,
5864
+ },
5865
+ {
5866
+ provide: AggregatorService,
5867
+ useClass: config?.playwright
5868
+ ? AggregatorPlaywrightService
5869
+ : AggregatorService,
5870
+ },
5871
+ {
5872
+ provide: FreezeFrameService,
5873
+ useClass: config?.playwright
5874
+ ? FreezeFramePlaywrightService
5875
+ : FreezeFrameService,
5876
+ },
5877
+ {
5878
+ provide: UnrealCommunicatorService,
5879
+ useClass: config?.playwright
5880
+ ? UnrealCommunicatorPlaywrightService
5881
+ : UnrealCommunicatorService,
5882
+ },
5883
+ {
5884
+ provide: AFKService,
5885
+ useClass: config?.playwright ? AfkPlaywrightService : AFKService,
5886
+ },
5887
+ {
5888
+ provide: SignallingService,
5889
+ useClass: config?.playwright
5890
+ ? SignallingPlaywrightService
5891
+ : SignallingService,
5892
+ },
5893
+ {
5894
+ provide: ConsoleExtensionsService,
5895
+ useClass: config?.playwright
5896
+ ? ConsoleExtensionsPlaywrightService
5897
+ : ConsoleExtensionsService,
5898
+ },
5899
+ {
5900
+ provide: FileReceiverService,
5901
+ useClass: config?.playwright
5902
+ ? FileReceiverPlaywrightService
5903
+ : FileReceiverService,
5904
+ },
5905
+ provideEnvironmentInitializer(() => {
5906
+ inject(AggregatorService);
5907
+ inject(InputService);
5908
+ inject(StreamStatusTelemetryService);
5909
+ inject(ConsoleExtensionsService);
5910
+ inject(AFKService);
5911
+ inject(FreezeFrameService);
5912
+ inject(AnalyticsService);
5913
+ inject(FpsMonitorService);
5914
+ }),
5915
+ ]);
5916
+ }
5917
+
5918
5918
  /**
5919
5919
  * Generated bundle index. Do not edit.
5920
5920
  */