@3dsource/angular-unreal-module 0.0.93 → 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,
|
|
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,
|
|
4
|
+
import { createAction, props, createReducer, on, createFeature, Store, createSelector, provideState } from '@ngrx/store';
|
|
5
5
|
import { concatLatestFrom } from '@ngrx/operators';
|
|
6
|
-
import { Actions, ofType,
|
|
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,6 +312,266 @@ 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
|
+
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;
|
|
402
|
+
}
|
|
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;
|
|
450
|
+
}
|
|
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,
|
|
480
|
+
message,
|
|
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
|
+
|
|
315
575
|
class SubService {
|
|
316
576
|
constructor() {
|
|
317
577
|
this.store = inject(Store);
|
|
@@ -3331,546 +3591,538 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3331
3591
|
type: Injectable
|
|
3332
3592
|
}] });
|
|
3333
3593
|
|
|
3334
|
-
class FreezeFramePlaywrightService extends FreezeFrameService {
|
|
3335
|
-
init() {
|
|
3336
|
-
return;
|
|
3337
|
-
}
|
|
3338
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3339
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService }); }
|
|
3340
|
-
}
|
|
3341
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService, decorators: [{
|
|
3342
|
-
type: Injectable
|
|
3343
|
-
}] });
|
|
3344
|
-
|
|
3345
|
-
class CommandTelemetryPlaywrightService extends CommandTelemetryService {
|
|
3346
|
-
init() {
|
|
3347
|
-
return;
|
|
3348
|
-
}
|
|
3349
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3350
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService }); }
|
|
3351
|
-
}
|
|
3352
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService, decorators: [{
|
|
3353
|
-
type: Injectable
|
|
3354
|
-
}] });
|
|
3355
|
-
|
|
3356
|
-
class StreamStatusTelemetryPlaywrightService extends StreamStatusTelemetryService {
|
|
3357
|
-
init() {
|
|
3358
|
-
return;
|
|
3359
|
-
}
|
|
3360
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3361
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService }); }
|
|
3362
|
-
}
|
|
3363
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService, decorators: [{
|
|
3364
|
-
type: Injectable
|
|
3365
|
-
}] });
|
|
3366
|
-
|
|
3367
|
-
class ConsoleExtensionsPlaywrightService extends ConsoleExtensionsService {
|
|
3368
|
-
init() {
|
|
3369
|
-
return;
|
|
3370
|
-
}
|
|
3371
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3372
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService }); }
|
|
3373
|
-
}
|
|
3374
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService, decorators: [{
|
|
3375
|
-
type: Injectable
|
|
3376
|
-
}] });
|
|
3377
|
-
|
|
3378
|
-
class FileReceiverPlaywrightService extends FileReceiverService {
|
|
3379
|
-
init() {
|
|
3380
|
-
return;
|
|
3381
|
-
}
|
|
3382
|
-
}
|
|
3383
|
-
|
|
3384
|
-
class InputPlaywrightService extends InputService {
|
|
3385
|
-
init() {
|
|
3386
|
-
return;
|
|
3387
|
-
}
|
|
3388
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3389
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService }); }
|
|
3390
|
-
}
|
|
3391
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService, decorators: [{
|
|
3392
|
-
type: Injectable
|
|
3393
|
-
}] });
|
|
3394
|
-
|
|
3395
|
-
/*
|
|
3396
|
-
* Copyright (c) 2025.
|
|
3397
|
-
* 3DSource.com. Sergii Karanda steve@3dsource.com. All Rights Reserved.
|
|
3398
|
-
*/
|
|
3399
|
-
const ReceivedMimeTypes = {
|
|
3400
|
-
ApplicationJson: 'application/json',
|
|
3401
|
-
};
|
|
3402
|
-
|
|
3403
|
-
class FileHandlerService {
|
|
3404
|
-
constructor() {
|
|
3405
|
-
this.fileService = inject(FileReceiverService);
|
|
3406
|
-
this.fileHandlers = {
|
|
3407
|
-
[ReceivedMimeTypes.ApplicationJson]: this.handleJsonFile.bind(this),
|
|
3408
|
-
};
|
|
3409
|
-
}
|
|
3410
|
-
handleJsonFile(data, correlationId) {
|
|
3411
|
-
const { blob } = data;
|
|
3412
|
-
return from(blob.text()).pipe(map$1((text) => JSON.parse(text)), filter(({ commandCallback }) => {
|
|
3413
|
-
return commandCallback.correlationId === correlationId;
|
|
3414
|
-
}));
|
|
3415
|
-
}
|
|
3416
|
-
observeFileResponse(mimetype, data, sender, timeOut = 60000) {
|
|
3417
|
-
const correlationId = generateUuid();
|
|
3418
|
-
const out = { ...data, correlationId };
|
|
3419
|
-
const observable = this.fileService.fileComplete$.pipe(switchMap$1((data) => {
|
|
3420
|
-
const { valid } = data;
|
|
3421
|
-
if (valid && this.fileHandlers[mimetype]) {
|
|
3422
|
-
return this.fileHandlers[mimetype](data, correlationId).pipe(filter(Truthy));
|
|
3423
|
-
}
|
|
3424
|
-
return of(null);
|
|
3425
|
-
}), timeout$1(timeOut), catchError((e) => {
|
|
3426
|
-
Logger.error(e);
|
|
3427
|
-
return of(null);
|
|
3428
|
-
}), take(1));
|
|
3429
|
-
setTimeout(() => sender(out), 0);
|
|
3430
|
-
return observable;
|
|
3594
|
+
class FreezeFramePlaywrightService extends FreezeFrameService {
|
|
3595
|
+
init() {
|
|
3596
|
+
return;
|
|
3431
3597
|
}
|
|
3432
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3433
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3598
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3599
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService }); }
|
|
3434
3600
|
}
|
|
3435
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3601
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FreezeFramePlaywrightService, decorators: [{
|
|
3436
3602
|
type: Injectable
|
|
3437
3603
|
}] });
|
|
3438
3604
|
|
|
3439
|
-
class
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
sendEventToMixPanel(name, params = {}) {
|
|
3443
|
-
Logger.log('Analytics Event:', name, params);
|
|
3444
|
-
this.#store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
|
|
3445
|
-
}
|
|
3446
|
-
constructor() {
|
|
3447
|
-
const abortSignal$ = this.#store
|
|
3448
|
-
.select(unrealFeature.selectViewportReady)
|
|
3449
|
-
.pipe(filter$1(Falsy), map(() => void 0));
|
|
3450
|
-
this.#store
|
|
3451
|
-
.select(unrealFeature.selectViewportReady)
|
|
3452
|
-
.pipe(distinctUntilChanged(), filter$1(Truthy), tap(() => this.monitorMouse(abortSignal$)))
|
|
3453
|
-
.subscribe();
|
|
3605
|
+
class CommandTelemetryPlaywrightService extends CommandTelemetryService {
|
|
3606
|
+
init() {
|
|
3607
|
+
return;
|
|
3454
3608
|
}
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
const mousedown$ = fromEvent(element, 'mousedown').pipe(takeUntil$1(stop$));
|
|
3466
|
-
const touchStart$ = fromEvent(element, 'touchstart').pipe(takeUntil$1(stop$));
|
|
3467
|
-
const touchMove$ = fromEvent(element, 'touchmove').pipe(takeUntil$1(stop$), throttleTime(200));
|
|
3468
|
-
// Track mouse movement while the left or right button is pressed
|
|
3469
|
-
mousedown$
|
|
3470
|
-
.pipe(filter$1((event) => event.button === 0 || event.button === 2), tap((downEvent) => {
|
|
3471
|
-
const { offsetX, offsetY } = this.getOffset(downEvent, element);
|
|
3472
|
-
this.sendEventToMixPanel('mouse_interaction', {
|
|
3473
|
-
viewX: offsetX / element.clientWidth,
|
|
3474
|
-
viewY: offsetY / element.clientHeight,
|
|
3475
|
-
action: downEvent.button === 0 ? 'spinning' : 'panning',
|
|
3476
|
-
});
|
|
3477
|
-
}))
|
|
3478
|
-
.subscribe();
|
|
3479
|
-
// Track mouse movement while the left or right button is pressed
|
|
3480
|
-
touchStart$
|
|
3481
|
-
.pipe(tap((downEvent) => {
|
|
3482
|
-
const totalTouches = downEvent.touches.length;
|
|
3483
|
-
const touch = downEvent.touches[0];
|
|
3484
|
-
const { offsetX, offsetY } = this.getOffset(touch, element);
|
|
3485
|
-
this.sendEventToMixPanel('touch_interaction', {
|
|
3486
|
-
viewX: offsetX / element.clientWidth,
|
|
3487
|
-
viewY: offsetY / element.clientHeight,
|
|
3488
|
-
action: totalTouches === 1 ? 'spinning' : 'panning',
|
|
3489
|
-
});
|
|
3490
|
-
}))
|
|
3491
|
-
.subscribe();
|
|
3492
|
-
let initialTouchDistance = 0;
|
|
3493
|
-
// Subscribe to mousewheel events
|
|
3494
|
-
touchMove$.subscribe((event) => {
|
|
3495
|
-
const totalTouches = event.touches.length;
|
|
3496
|
-
if (totalTouches < 2) {
|
|
3497
|
-
return;
|
|
3498
|
-
}
|
|
3499
|
-
// Calculate the current distance between two touches
|
|
3500
|
-
const touch1 = event.touches[0];
|
|
3501
|
-
const touch2 = event.touches[1];
|
|
3502
|
-
const offset1 = this.getOffset(touch1, element);
|
|
3503
|
-
const offset2 = this.getOffset(touch2, element);
|
|
3504
|
-
const currentDistance = Math.sqrt(Math.pow(offset2.offsetX - offset1.offsetX, 2) +
|
|
3505
|
-
Math.pow(offset2.offsetY - offset1.offsetY, 2));
|
|
3506
|
-
// Determine if zooming in or out based on distance change
|
|
3507
|
-
const deltaDistance = currentDistance - initialTouchDistance;
|
|
3508
|
-
const isZoomingIn = deltaDistance > 0;
|
|
3509
|
-
// Only send event if there's a significant change (avoid noise)
|
|
3510
|
-
if (Math.abs(deltaDistance) > 10) {
|
|
3511
|
-
this.sendEventToMixPanel('touch_zoom_interaction', {
|
|
3512
|
-
delta: isZoomingIn ? 1 : -1,
|
|
3513
|
-
action: isZoomingIn ? 'zoomin' : 'zoomout',
|
|
3514
|
-
});
|
|
3515
|
-
// Update initial distance for next comparison
|
|
3516
|
-
initialTouchDistance = currentDistance;
|
|
3517
|
-
}
|
|
3518
|
-
});
|
|
3519
|
-
mousewheel$.subscribe((event) => {
|
|
3520
|
-
this.sendEventToMixPanel('mouse_zoom_interaction', {
|
|
3521
|
-
delta: -(event.deltaY / Math.abs(event.deltaY)),
|
|
3522
|
-
action: event.deltaY < 0 ? 'zoomin' : 'zoomout',
|
|
3523
|
-
});
|
|
3524
|
-
});
|
|
3609
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3610
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService }); }
|
|
3611
|
+
}
|
|
3612
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CommandTelemetryPlaywrightService, decorators: [{
|
|
3613
|
+
type: Injectable
|
|
3614
|
+
}] });
|
|
3615
|
+
|
|
3616
|
+
class StreamStatusTelemetryPlaywrightService extends StreamStatusTelemetryService {
|
|
3617
|
+
init() {
|
|
3618
|
+
return;
|
|
3525
3619
|
}
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3620
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3621
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService }); }
|
|
3622
|
+
}
|
|
3623
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: StreamStatusTelemetryPlaywrightService, decorators: [{
|
|
3624
|
+
type: Injectable
|
|
3625
|
+
}] });
|
|
3626
|
+
|
|
3627
|
+
class ConsoleExtensionsPlaywrightService extends ConsoleExtensionsService {
|
|
3628
|
+
init() {
|
|
3629
|
+
return;
|
|
3531
3630
|
}
|
|
3532
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3533
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3631
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3632
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService }); }
|
|
3534
3633
|
}
|
|
3535
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
3634
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ConsoleExtensionsPlaywrightService, decorators: [{
|
|
3536
3635
|
type: Injectable
|
|
3537
|
-
}]
|
|
3636
|
+
}] });
|
|
3538
3637
|
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
ConsoleExtensionsService,
|
|
3544
|
-
InputService,
|
|
3545
|
-
VideoService,
|
|
3546
|
-
WebRtcPlayerService,
|
|
3547
|
-
RegionsPingService,
|
|
3548
|
-
FileReceiverService,
|
|
3549
|
-
FileHandlerService,
|
|
3550
|
-
AnalyticsService,
|
|
3551
|
-
{
|
|
3552
|
-
provide: StreamStatusTelemetryService,
|
|
3553
|
-
useClass: config?.playwright
|
|
3554
|
-
? StreamStatusTelemetryPlaywrightService
|
|
3555
|
-
: StreamStatusTelemetryService,
|
|
3556
|
-
},
|
|
3557
|
-
{
|
|
3558
|
-
provide: CommandTelemetryService,
|
|
3559
|
-
useClass: config?.playwright
|
|
3560
|
-
? CommandTelemetryPlaywrightService
|
|
3561
|
-
: CommandTelemetryService,
|
|
3562
|
-
},
|
|
3563
|
-
{
|
|
3564
|
-
provide: AggregatorService,
|
|
3565
|
-
useClass: config?.playwright
|
|
3566
|
-
? AggregatorPlaywrightService
|
|
3567
|
-
: AggregatorService,
|
|
3568
|
-
},
|
|
3569
|
-
{
|
|
3570
|
-
provide: FreezeFrameService,
|
|
3571
|
-
useClass: config?.playwright
|
|
3572
|
-
? FreezeFramePlaywrightService
|
|
3573
|
-
: FreezeFrameService,
|
|
3574
|
-
},
|
|
3575
|
-
{
|
|
3576
|
-
provide: UnrealCommunicatorService,
|
|
3577
|
-
useClass: config?.playwright
|
|
3578
|
-
? UnrealCommunicatorPlaywrightService
|
|
3579
|
-
: UnrealCommunicatorService,
|
|
3580
|
-
},
|
|
3581
|
-
{
|
|
3582
|
-
provide: AFKService,
|
|
3583
|
-
useClass: config?.playwright ? AfkPlaywrightService : AFKService,
|
|
3584
|
-
},
|
|
3585
|
-
{
|
|
3586
|
-
provide: SignallingService,
|
|
3587
|
-
useClass: config?.playwright
|
|
3588
|
-
? SignallingPlaywrightService
|
|
3589
|
-
: SignallingService,
|
|
3590
|
-
},
|
|
3591
|
-
{
|
|
3592
|
-
provide: ConsoleExtensionsService,
|
|
3593
|
-
useClass: config?.playwright
|
|
3594
|
-
? ConsoleExtensionsPlaywrightService
|
|
3595
|
-
: ConsoleExtensionsService,
|
|
3596
|
-
},
|
|
3597
|
-
{
|
|
3598
|
-
provide: FileReceiverService,
|
|
3599
|
-
useClass: config?.playwright
|
|
3600
|
-
? FileReceiverPlaywrightService
|
|
3601
|
-
: FileReceiverService,
|
|
3602
|
-
},
|
|
3603
|
-
provideEnvironmentInitializer(() => {
|
|
3604
|
-
inject(AggregatorService);
|
|
3605
|
-
inject(InputService);
|
|
3606
|
-
inject(StreamStatusTelemetryService);
|
|
3607
|
-
inject(ConsoleExtensionsService);
|
|
3608
|
-
inject(AFKService);
|
|
3609
|
-
inject(FreezeFrameService);
|
|
3610
|
-
inject(AnalyticsService);
|
|
3611
|
-
}),
|
|
3612
|
-
]);
|
|
3638
|
+
class FileReceiverPlaywrightService extends FileReceiverService {
|
|
3639
|
+
init() {
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3613
3642
|
}
|
|
3614
3643
|
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
ssInfo: null,
|
|
3633
|
-
ssData: null,
|
|
3634
|
-
streamResolution: { width: null, height: null },
|
|
3635
|
-
freezeFrameFromVideo: { dataUrl: null, progress: null },
|
|
3636
|
-
freezeFrame: { dataUrl: null, progress: null },
|
|
3637
|
-
disconnectReason: DisconnectReason.None,
|
|
3638
|
-
awsInstance: {
|
|
3639
|
-
wsUrl: null,
|
|
3640
|
-
instanceName: null,
|
|
3641
|
-
pollingUrl: null,
|
|
3642
|
-
progressComplete: 0,
|
|
3643
|
-
},
|
|
3644
|
-
streamConfig: {
|
|
3645
|
-
autoStart: true,
|
|
3646
|
-
warnTimeout: DEFAULT_AFK_TIMEOUT,
|
|
3647
|
-
},
|
|
3648
|
-
loaderCommands: {
|
|
3649
|
-
commandsInProgress: [],
|
|
3650
|
-
totalCommandsStarted: 0,
|
|
3651
|
-
totalCommandsCompleted: 0,
|
|
3652
|
-
},
|
|
3653
|
-
matchUrls: [],
|
|
3654
|
-
streamClientCompanyId: '',
|
|
3655
|
-
streamViewId: 'default',
|
|
3656
|
-
videoIntroSrc: null,
|
|
3657
|
-
imageIntroSrc: null,
|
|
3658
|
-
imageLoadingSrc: '',
|
|
3659
|
-
analyticsEvent: null,
|
|
3644
|
+
class InputPlaywrightService extends InputService {
|
|
3645
|
+
init() {
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3648
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3649
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService }); }
|
|
3650
|
+
}
|
|
3651
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InputPlaywrightService, decorators: [{
|
|
3652
|
+
type: Injectable
|
|
3653
|
+
}] });
|
|
3654
|
+
|
|
3655
|
+
/*
|
|
3656
|
+
* Copyright (c) 2025.
|
|
3657
|
+
* 3DSource.com. Sergii Karanda steve@3dsource.com. All Rights Reserved.
|
|
3658
|
+
*/
|
|
3659
|
+
const ReceivedMimeTypes = {
|
|
3660
|
+
ApplicationJson: 'application/json',
|
|
3660
3661
|
};
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
}
|
|
3668
|
-
return { ...state, awsInstance: { instanceName, wsUrl, pollingUrl } };
|
|
3669
|
-
}), on(setViewportReady, (state) => {
|
|
3670
|
-
return {
|
|
3671
|
-
...state,
|
|
3672
|
-
viewportReady: true,
|
|
3673
|
-
statusMessage: null,
|
|
3674
|
-
errorMessage: null,
|
|
3675
|
-
};
|
|
3676
|
-
}), on(setViewportNotReady, (state) => {
|
|
3677
|
-
return { ...state, viewportReady: false };
|
|
3678
|
-
}), on(updateCirrusInfo, (state, { ssInfo, ssData }) => {
|
|
3679
|
-
return {
|
|
3680
|
-
...state,
|
|
3681
|
-
ssInfo: ssInfo, // For back compatibility
|
|
3682
|
-
ssData: ssData, // Contains all the data from the ssInfo as an object
|
|
3683
|
-
};
|
|
3684
|
-
}), on(changeStreamResolutionSuccessAction, (state, { width, height }) => {
|
|
3685
|
-
return { ...state, streamResolution: { width: width, height: height } };
|
|
3686
|
-
}), on(setFreezeFrame, (state, freezeFrame) => {
|
|
3687
|
-
return {
|
|
3688
|
-
...state,
|
|
3689
|
-
freezeFrame: {
|
|
3690
|
-
dataUrl: freezeFrame.progress === 0 ||
|
|
3691
|
-
freezeFrame.progress === 1 ||
|
|
3692
|
-
freezeFrame.progress === null
|
|
3693
|
-
? freezeFrame.dataUrl
|
|
3694
|
-
: state.freezeFrame.dataUrl,
|
|
3695
|
-
progress: freezeFrame.progress || null,
|
|
3696
|
-
},
|
|
3697
|
-
};
|
|
3698
|
-
}), on(disconnectStream, (state, errorMessage) => {
|
|
3699
|
-
if (state.dataChannelConnected ||
|
|
3700
|
-
state.disconnectReason === DisconnectReason.Destroy) {
|
|
3701
|
-
return state;
|
|
3662
|
+
|
|
3663
|
+
class FileHandlerService {
|
|
3664
|
+
constructor() {
|
|
3665
|
+
this.fileService = inject(FileReceiverService);
|
|
3666
|
+
this.fileHandlers = {
|
|
3667
|
+
[ReceivedMimeTypes.ApplicationJson]: this.handleJsonFile.bind(this),
|
|
3668
|
+
};
|
|
3702
3669
|
}
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
};
|
|
3709
|
-
}), on(setFreezeFrameFromVideo, (state, freezeFrameFromVideo) => {
|
|
3710
|
-
return {
|
|
3711
|
-
...state,
|
|
3712
|
-
freezeFrameFromVideo: {
|
|
3713
|
-
dataUrl: freezeFrameFromVideo.dataUrl,
|
|
3714
|
-
progress: freezeFrameFromVideo.progress || null,
|
|
3715
|
-
},
|
|
3716
|
-
};
|
|
3717
|
-
}), on(setStatusMessage, (state, { statusMessage }) => {
|
|
3718
|
-
return { ...state, statusMessage };
|
|
3719
|
-
}), on(setEstablishingConnection, (state, { value }) => {
|
|
3720
|
-
return { ...state, establishingConnection: value };
|
|
3721
|
-
}), on(setStatusPercentSignallingServer, (state, { percent }) => {
|
|
3722
|
-
return { ...state, statusPercentSignallingServer: percent };
|
|
3723
|
-
}), on(dataChannelConnected, (state, { statusMessage }) => {
|
|
3724
|
-
return {
|
|
3725
|
-
...state,
|
|
3726
|
-
statusMessage,
|
|
3727
|
-
dataChannelConnected: true,
|
|
3728
|
-
establishingConnection: false,
|
|
3729
|
-
wasInitialized: true,
|
|
3730
|
-
};
|
|
3731
|
-
}), on(setConfig, startStream, (state, { config }) => {
|
|
3732
|
-
return { ...state, streamConfig: { ...state.streamConfig, ...config } };
|
|
3733
|
-
}), on(resetConfig, (state) => {
|
|
3734
|
-
return { ...state, streamConfig: initialState.streamConfig };
|
|
3735
|
-
}), on(resetWarnTimeout, (state) => {
|
|
3736
|
-
return {
|
|
3737
|
-
...state,
|
|
3738
|
-
streamConfig: { ...state.streamConfig, warnTimeout: DEFAULT_AFK_TIMEOUT },
|
|
3739
|
-
};
|
|
3740
|
-
}), on(initSignalling, (state, { resetDisconnectionReason = true }) => {
|
|
3741
|
-
return {
|
|
3742
|
-
...state,
|
|
3743
|
-
disconnectReason: resetDisconnectionReason
|
|
3744
|
-
? DisconnectReason.None
|
|
3745
|
-
: state.disconnectReason,
|
|
3746
|
-
};
|
|
3747
|
-
}), on(destroyRemoteConnections, (state, { reason }) => {
|
|
3748
|
-
if (state.disconnectReason === DisconnectReason.Destroy) {
|
|
3749
|
-
return state;
|
|
3670
|
+
handleJsonFile(data, correlationId) {
|
|
3671
|
+
const { blob } = data;
|
|
3672
|
+
return from(blob.text()).pipe(map$1((text) => JSON.parse(text)), filter(({ commandCallback }) => {
|
|
3673
|
+
return commandCallback.correlationId === correlationId;
|
|
3674
|
+
}));
|
|
3750
3675
|
}
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
}
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
}
|
|
3867
|
-
|
|
3868
|
-
})
|
|
3676
|
+
observeFileResponse(mimetype, data, sender, timeOut = 60000) {
|
|
3677
|
+
const correlationId = generateUuid();
|
|
3678
|
+
const out = { ...data, correlationId };
|
|
3679
|
+
const observable = this.fileService.fileComplete$.pipe(switchMap$1((data) => {
|
|
3680
|
+
const { valid } = data;
|
|
3681
|
+
if (valid && this.fileHandlers[mimetype]) {
|
|
3682
|
+
return this.fileHandlers[mimetype](data, correlationId).pipe(filter(Truthy));
|
|
3683
|
+
}
|
|
3684
|
+
return of(null);
|
|
3685
|
+
}), timeout$1(timeOut), catchError((e) => {
|
|
3686
|
+
Logger.error(e);
|
|
3687
|
+
return of(null);
|
|
3688
|
+
}), take(1));
|
|
3689
|
+
setTimeout(() => sender(out), 0);
|
|
3690
|
+
return observable;
|
|
3691
|
+
}
|
|
3692
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3693
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileHandlerService }); }
|
|
3694
|
+
}
|
|
3695
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileHandlerService, decorators: [{
|
|
3696
|
+
type: Injectable
|
|
3697
|
+
}] });
|
|
3698
|
+
|
|
3699
|
+
class AnalyticsService {
|
|
3700
|
+
#store = inject(Store);
|
|
3701
|
+
#destroy$ = new Subject();
|
|
3702
|
+
sendEventToMixPanel(name, params = {}) {
|
|
3703
|
+
Logger.log('Analytics Event:', name, params);
|
|
3704
|
+
this.#store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
|
|
3705
|
+
}
|
|
3706
|
+
constructor() {
|
|
3707
|
+
const abortSignal$ = this.#store
|
|
3708
|
+
.select(unrealFeature.selectViewportReady)
|
|
3709
|
+
.pipe(filter$1(Falsy), map(() => void 0));
|
|
3710
|
+
this.#store
|
|
3711
|
+
.select(unrealFeature.selectViewportReady)
|
|
3712
|
+
.pipe(distinctUntilChanged(), filter$1(Truthy), tap(() => this.monitorMouse(abortSignal$)))
|
|
3713
|
+
.subscribe();
|
|
3714
|
+
}
|
|
3715
|
+
monitorMouse(abortSignal$) {
|
|
3716
|
+
this.#destroy$.next();
|
|
3717
|
+
const stop$ = merge(this.#destroy$.asObservable(), abortSignal$);
|
|
3718
|
+
const element = document.getElementById('streamingVideo');
|
|
3719
|
+
if (!element) {
|
|
3720
|
+
return;
|
|
3721
|
+
}
|
|
3722
|
+
// Monitor mousewheel events
|
|
3723
|
+
const mousewheel$ = fromEvent(element, 'wheel').pipe(takeUntil$1(stop$), throttleTime(1000));
|
|
3724
|
+
// Monitor mouse button states
|
|
3725
|
+
const mousedown$ = fromEvent(element, 'mousedown').pipe(takeUntil$1(stop$));
|
|
3726
|
+
const touchStart$ = fromEvent(element, 'touchstart').pipe(takeUntil$1(stop$));
|
|
3727
|
+
const touchMove$ = fromEvent(element, 'touchmove').pipe(takeUntil$1(stop$), throttleTime(200));
|
|
3728
|
+
// Track mouse movement while the left or right button is pressed
|
|
3729
|
+
mousedown$
|
|
3730
|
+
.pipe(filter$1((event) => event.button === 0 || event.button === 2), tap((downEvent) => {
|
|
3731
|
+
const { offsetX, offsetY } = this.getOffset(downEvent, element);
|
|
3732
|
+
this.sendEventToMixPanel('mouse_interaction', {
|
|
3733
|
+
viewX: offsetX / element.clientWidth,
|
|
3734
|
+
viewY: offsetY / element.clientHeight,
|
|
3735
|
+
action: downEvent.button === 0 ? 'spinning' : 'panning',
|
|
3736
|
+
});
|
|
3737
|
+
}))
|
|
3738
|
+
.subscribe();
|
|
3739
|
+
// Track mouse movement while the left or right button is pressed
|
|
3740
|
+
touchStart$
|
|
3741
|
+
.pipe(tap((downEvent) => {
|
|
3742
|
+
const totalTouches = downEvent.touches.length;
|
|
3743
|
+
const touch = downEvent.touches[0];
|
|
3744
|
+
const { offsetX, offsetY } = this.getOffset(touch, element);
|
|
3745
|
+
this.sendEventToMixPanel('touch_interaction', {
|
|
3746
|
+
viewX: offsetX / element.clientWidth,
|
|
3747
|
+
viewY: offsetY / element.clientHeight,
|
|
3748
|
+
action: totalTouches === 1 ? 'spinning' : 'panning',
|
|
3749
|
+
});
|
|
3750
|
+
}))
|
|
3751
|
+
.subscribe();
|
|
3752
|
+
let initialTouchDistance = 0;
|
|
3753
|
+
// Subscribe to mousewheel events
|
|
3754
|
+
touchMove$.subscribe((event) => {
|
|
3755
|
+
const totalTouches = event.touches.length;
|
|
3756
|
+
if (totalTouches < 2) {
|
|
3757
|
+
return;
|
|
3758
|
+
}
|
|
3759
|
+
// Calculate the current distance between two touches
|
|
3760
|
+
const touch1 = event.touches[0];
|
|
3761
|
+
const touch2 = event.touches[1];
|
|
3762
|
+
const offset1 = this.getOffset(touch1, element);
|
|
3763
|
+
const offset2 = this.getOffset(touch2, element);
|
|
3764
|
+
const currentDistance = Math.sqrt(Math.pow(offset2.offsetX - offset1.offsetX, 2) +
|
|
3765
|
+
Math.pow(offset2.offsetY - offset1.offsetY, 2));
|
|
3766
|
+
// Determine if zooming in or out based on distance change
|
|
3767
|
+
const deltaDistance = currentDistance - initialTouchDistance;
|
|
3768
|
+
const isZoomingIn = deltaDistance > 0;
|
|
3769
|
+
// Only send event if there's a significant change (avoid noise)
|
|
3770
|
+
if (Math.abs(deltaDistance) > 10) {
|
|
3771
|
+
this.sendEventToMixPanel('touch_zoom_interaction', {
|
|
3772
|
+
delta: isZoomingIn ? 1 : -1,
|
|
3773
|
+
action: isZoomingIn ? 'zoomin' : 'zoomout',
|
|
3774
|
+
});
|
|
3775
|
+
// Update initial distance for next comparison
|
|
3776
|
+
initialTouchDistance = currentDistance;
|
|
3777
|
+
}
|
|
3778
|
+
});
|
|
3779
|
+
mousewheel$.subscribe((event) => {
|
|
3780
|
+
this.sendEventToMixPanel('mouse_zoom_interaction', {
|
|
3781
|
+
delta: -(event.deltaY / Math.abs(event.deltaY)),
|
|
3782
|
+
action: event.deltaY < 0 ? 'zoomin' : 'zoomout',
|
|
3783
|
+
});
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
getOffset(event, element) {
|
|
3787
|
+
const rect = element.getBoundingClientRect(); // in viewport coords
|
|
3788
|
+
const offsetX = event.clientX - rect.left; // relative to element
|
|
3789
|
+
const offsetY = event.clientY - rect.top;
|
|
3790
|
+
return { offsetX, offsetY };
|
|
3791
|
+
}
|
|
3792
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3793
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AnalyticsService }); }
|
|
3794
|
+
}
|
|
3795
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AnalyticsService, decorators: [{
|
|
3796
|
+
type: Injectable
|
|
3797
|
+
}], ctorParameters: () => [] });
|
|
3869
3798
|
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3799
|
+
/**
|
|
3800
|
+
* Adaptive FPS controller that monitors the incoming WebRTC video stream
|
|
3801
|
+
* quality and dynamically adjusts Unreal Engine's `t.MaxFPS` console variable.
|
|
3802
|
+
*
|
|
3803
|
+
* ## How it works
|
|
3804
|
+
*
|
|
3805
|
+
* The service subscribes to `VideoService.videoStats$` (emitted every 250 ms)
|
|
3806
|
+
* and collects samples of FPS, bitrate, and QP (Quantisation Parameter).
|
|
3807
|
+
* Every {@link SAMPLE_WINDOW} ticks (~2 s) it averages the collected samples
|
|
3808
|
+
* and feeds them into a set of **fuzzy-logic membership functions** that
|
|
3809
|
+
* classify each metric into overlapping quality bands.
|
|
3810
|
+
*
|
|
3811
|
+
* ### Input signals
|
|
3812
|
+
*
|
|
3813
|
+
* **1. FPS ratio** = `actualFPS / currentTargetFPS` (0..1+)
|
|
3814
|
+
*
|
|
3815
|
+
* | Band | Range | Meaning |
|
|
3816
|
+
* |--------|---------------------|-----------------------------------------------|
|
|
3817
|
+
* | low | ratio < 0.7 → 1.0, linear ramp 0.7..0.9 → 0 | Stream cannot keep up with the target |
|
|
3818
|
+
* | ok | triangle 0.8..1.0, peak at 0.95 | Stream roughly matches the target |
|
|
3819
|
+
* | high | ratio 0.9..1.0 → ramp, >1.0 → 1.0 | Stream meets or exceeds the target |
|
|
3820
|
+
*
|
|
3821
|
+
* **2. Bitrate load** = `currentBitrate / MAX_BITRATE` (0..1+)
|
|
3822
|
+
*
|
|
3823
|
+
* `MAX_BITRATE` = 20 Mbit/s — the reference ceiling for the encoder.
|
|
3824
|
+
*
|
|
3825
|
+
* | Band | Range | Meaning |
|
|
3826
|
+
* |--------|-------------------------------------------------|-----------------------------------------------|
|
|
3827
|
+
* | high | load 0.5..0.8 → ramp, >0.8 → 1.0 | Encoder is using most of the available bandwidth |
|
|
3828
|
+
* | not high | load < 0.5 → 0.0 | Bandwidth headroom is available |
|
|
3829
|
+
*
|
|
3830
|
+
* **3. Quality** — mapped from QP via `mapQpToQuality()` (see {@link Quality}):
|
|
3831
|
+
*
|
|
3832
|
+
* | Quality | QP range | Meaning |
|
|
3833
|
+
* |----------|-----------|-------------------------------------------------|
|
|
3834
|
+
* | `lime` | QP <= 26 | Good — encoder has enough headroom |
|
|
3835
|
+
* | `orange` | QP 27..35 | Fair — encoder is under moderate pressure |
|
|
3836
|
+
* | `red` | QP > 35 | Poor — heavy compression, visible artefacts |
|
|
3837
|
+
*
|
|
3838
|
+
* ### Decision rules (evaluated in priority order, first match wins)
|
|
3839
|
+
*
|
|
3840
|
+
* | # | Condition | Decision |
|
|
3841
|
+
* |---|------------------------------------------------------------|--------------|
|
|
3842
|
+
* | 1 | FPS ratio is **low** (membership > 0.5) | **decrease** |
|
|
3843
|
+
* | 2 | FPS ratio is **ok** AND quality is `red` | **decrease** |
|
|
3844
|
+
* | 3 | FPS ratio is **ok** AND bitrate **high** AND quality is `orange` | **hold** |
|
|
3845
|
+
* | 4 | FPS ratio is **high** AND quality is `lime` AND bitrate **not high** | **increase** |
|
|
3846
|
+
* | — | Everything else | **hold** |
|
|
3847
|
+
*
|
|
3848
|
+
* ## Cooldown-based throttling
|
|
3849
|
+
*
|
|
3850
|
+
* The service continuously evaluates fuzzy rules every sample window.
|
|
3851
|
+
* After each FPS step change it enforces a cooldown before the next
|
|
3852
|
+
* change can be applied. The cooldown duration depends on the
|
|
3853
|
+
* (previous decision → next decision) pair:
|
|
3854
|
+
*
|
|
3855
|
+
* | Previous | Next | Cooldown |
|
|
3856
|
+
* |-----------|-----------|-----------------------------------------------------|
|
|
3857
|
+
* | decrease | decrease | {@link COOLDOWN_DECREASE_AFTER_DECREASE_MS} (2 s) |
|
|
3858
|
+
* | decrease | increase | {@link COOLDOWN_INCREASE_AFTER_DECREASE_MS} (10 s) |
|
|
3859
|
+
* | increase | decrease | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
|
|
3860
|
+
* | increase | increase | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
|
|
3861
|
+
*
|
|
3862
|
+
* This means the system reacts quickly after an increase (2 s in any
|
|
3863
|
+
* direction) but is cautious about stepping back up after a decrease
|
|
3864
|
+
* (10 s), while still allowing consecutive decreases at 2 s intervals.
|
|
3865
|
+
*
|
|
3866
|
+
* ## FPS dispatch
|
|
3867
|
+
*
|
|
3868
|
+
* FPS changes are dispatched via the NgRx `setMaxFps` action, which triggers
|
|
3869
|
+
* the existing `UnrealEffects.setMaxFps$` effect that sends the
|
|
3870
|
+
* `t.MaxFPS <value>` console command to Unreal Engine.
|
|
3871
|
+
*/
|
|
3872
|
+
class FpsMonitorService {
|
|
3873
|
+
constructor() {
|
|
3874
|
+
this.videoService = inject(VideoService);
|
|
3875
|
+
this.store = inject(Store);
|
|
3876
|
+
/** Discrete FPS targets the controller can switch between (ascending order). */
|
|
3877
|
+
this.FPS_STEPS = [30, 40, 50, 60];
|
|
3878
|
+
/** Reference ceiling for bitrate (bits/sec) used to normalise bitrate load to 0..1. */
|
|
3879
|
+
this.MAX_BITRATE = 20_000_000;
|
|
3880
|
+
/**
|
|
3881
|
+
* Cooldown (ms) for any decision (increase or decrease) following an increase.
|
|
3882
|
+
* After an increase the system should react faster.
|
|
3883
|
+
*/
|
|
3884
|
+
this.COOLDOWN_AFTER_INCREASE_MS = 2_000;
|
|
3885
|
+
/**
|
|
3886
|
+
* Cooldown (ms) for a decrease following a previous decrease.
|
|
3887
|
+
*/
|
|
3888
|
+
this.COOLDOWN_DECREASE_AFTER_DECREASE_MS = 2_000;
|
|
3889
|
+
/**
|
|
3890
|
+
* Cooldown (ms) for an increase following a previous decrease.
|
|
3891
|
+
* The stream needs more time to stabilise before stepping back up.
|
|
3892
|
+
*/
|
|
3893
|
+
this.COOLDOWN_INCREASE_AFTER_DECREASE_MS = 10_000;
|
|
3894
|
+
/**
|
|
3895
|
+
* Number of videoStats$ ticks to accumulate before evaluating.
|
|
3896
|
+
* At 250 ms per tick this gives a ~2 s sliding evaluation window,
|
|
3897
|
+
* long enough to smooth out single-frame jitter.
|
|
3898
|
+
*/
|
|
3899
|
+
this.SAMPLE_WINDOW = 8;
|
|
3900
|
+
/** Current FPS target; starts at max (60 FPS) and adapts downward/upward. */
|
|
3901
|
+
this.currentFpsTarget = this.FPS_STEPS[this.FPS_STEPS.length - 1];
|
|
3902
|
+
this.samples = [];
|
|
3903
|
+
this.lastDecisionTime = Date.now();
|
|
3904
|
+
this.lastUpgrade = 'hold';
|
|
3905
|
+
this.init();
|
|
3906
|
+
}
|
|
3907
|
+
init() {
|
|
3908
|
+
this.videoService.videoStats$
|
|
3909
|
+
.pipe(withLatestFrom(this.store.select(unrealFeature.selectViewportReady)), filter(([, viewportReady]) => viewportReady))
|
|
3910
|
+
.subscribe(([stats]) => this.processTick(stats));
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Called on every videoStats$ emission (~250 ms).
|
|
3914
|
+
* Accumulates samples and evaluates once per SAMPLE_WINDOW.
|
|
3915
|
+
*/
|
|
3916
|
+
processTick(stats) {
|
|
3917
|
+
const { framesPerSecond, bitrate, VideoEncoderQP } = stats.aggregatedStats;
|
|
3918
|
+
this.samples.push({
|
|
3919
|
+
fps: framesPerSecond ?? 0,
|
|
3920
|
+
bitrate: bitrate ?? 0,
|
|
3921
|
+
qp: VideoEncoderQP ?? 0,
|
|
3922
|
+
});
|
|
3923
|
+
// Wait until we have enough samples for a reliable average.
|
|
3924
|
+
if (this.samples.length < this.SAMPLE_WINDOW) {
|
|
3925
|
+
return;
|
|
3926
|
+
}
|
|
3927
|
+
const avg = this.averageSamples();
|
|
3928
|
+
this.samples = [];
|
|
3929
|
+
this.handleMonitoring(avg, this.currentFpsTarget);
|
|
3930
|
+
}
|
|
3931
|
+
handleMonitoring(avg, currentTarget) {
|
|
3932
|
+
const decision = this.evaluateDecision(avg, currentTarget);
|
|
3933
|
+
this.upgradeFps(decision, avg.qp);
|
|
3934
|
+
}
|
|
3935
|
+
/**
|
|
3936
|
+
* Combines fuzzy membership values into a single discrete decision.
|
|
3937
|
+
* Rules are evaluated in priority order — first match wins.
|
|
3938
|
+
*
|
|
3939
|
+
* Inputs:
|
|
3940
|
+
* - `fpsRatio` = avgFPS / currentTargetFPS (e.g. 45/60 = 0.75)
|
|
3941
|
+
* - `bitrateLoad` = avgBitrate / 20 Mbit/s (e.g. 12M/20M = 0.6)
|
|
3942
|
+
* - `quality` = mapQpToQuality(avgQP) → 'lime' | 'orange' | 'red'
|
|
3943
|
+
*/
|
|
3944
|
+
evaluateDecision(avg, currentTarget) {
|
|
3945
|
+
const fpsRatio = currentTarget > 0 ? avg.fps / currentTarget : 1;
|
|
3946
|
+
const bitrateLoad = avg.bitrate / this.MAX_BITRATE;
|
|
3947
|
+
const fpsLow = this.membershipFpsLow(fpsRatio);
|
|
3948
|
+
const fpsOk = this.membershipFpsOk(fpsRatio);
|
|
3949
|
+
const fpsHigh = this.membershipFpsHigh(fpsRatio);
|
|
3950
|
+
const bitrateHigh = this.membershipBitrateHigh(bitrateLoad);
|
|
3951
|
+
// Quality is derived from QP: lime (QP <= 26), orange (QP 27..35), red (QP > 35).
|
|
3952
|
+
const quality = mapQpToQuality(avg.qp);
|
|
3953
|
+
// Rule 1: FPS clearly below target (ratio roughly < 0.8) → step down immediately.
|
|
3954
|
+
if (fpsLow > 0.5) {
|
|
3955
|
+
return 'decrease';
|
|
3956
|
+
}
|
|
3957
|
+
// Rule 2: FPS looks acceptable numerically, but encoder quality is 'red'
|
|
3958
|
+
// (QP > 35 — heavy compression artefacts). Reducing target FPS frees
|
|
3959
|
+
// encoder headroom and improves visual quality.
|
|
3960
|
+
if (fpsOk > 0.4 && quality === 'red') {
|
|
3961
|
+
return 'decrease';
|
|
3962
|
+
}
|
|
3963
|
+
// Rule 3: FPS is ok, but bitrate is above ~50-80% of 20 Mbit/s ceiling
|
|
3964
|
+
// and quality is 'orange' (QP 27..35). Near capacity — don't push higher.
|
|
3965
|
+
if (fpsOk > 0.4 && bitrateHigh > 0.5 && quality === 'orange') {
|
|
3966
|
+
return 'hold';
|
|
3967
|
+
}
|
|
3968
|
+
// Rule 4: FPS meets/exceeds target (ratio >= ~0.95), quality is 'lime'
|
|
3969
|
+
// (QP <= 26), and bitrate is below 50% of ceiling — safe to step up.
|
|
3970
|
+
if (fpsHigh > 0.5 &&
|
|
3971
|
+
quality === 'lime' &&
|
|
3972
|
+
bitrateHigh < 0.5 &&
|
|
3973
|
+
this.currentFpsTarget < this.FPS_STEPS[this.FPS_STEPS.length - 1]) {
|
|
3974
|
+
return 'increase';
|
|
3975
|
+
}
|
|
3976
|
+
return 'hold';
|
|
3977
|
+
}
|
|
3978
|
+
// ---------------------------------------------------------------------------
|
|
3979
|
+
// Fuzzy membership functions
|
|
3980
|
+
//
|
|
3981
|
+
// Each function maps a normalised input (0..1+) to a membership degree (0..1).
|
|
3982
|
+
// Overlapping trapezoid/triangle shapes let multiple categories be partially
|
|
3983
|
+
// active at the same time, which makes the rules less brittle than hard thresholds.
|
|
3984
|
+
// ---------------------------------------------------------------------------
|
|
3985
|
+
/**
|
|
3986
|
+
* FPS ratio membership "low".
|
|
3987
|
+
* Input: ratio = actualFPS / targetFPS (e.g. 42/60 = 0.70).
|
|
3988
|
+
*
|
|
3989
|
+
* ratio < 0.7 → 1.0 (clearly failing, e.g. 40/60)
|
|
3990
|
+
* ratio 0.7..0.9 → linear ramp down (e.g. 0.8 → 0.5)
|
|
3991
|
+
* ratio > 0.9 → 0.0 (keeping up)
|
|
3992
|
+
*/
|
|
3993
|
+
membershipFpsLow(ratio) {
|
|
3994
|
+
if (ratio < 0.7)
|
|
3995
|
+
return 1.0;
|
|
3996
|
+
if (ratio > 0.9)
|
|
3997
|
+
return 0.0;
|
|
3998
|
+
return (0.9 - ratio) / 0.2;
|
|
3999
|
+
}
|
|
4000
|
+
/**
|
|
4001
|
+
* FPS ratio membership "ok" — triangle shape.
|
|
4002
|
+
* Input: ratio = actualFPS / targetFPS.
|
|
4003
|
+
*
|
|
4004
|
+
* ratio < 0.8 → 0.0
|
|
4005
|
+
* ratio 0.8..0.95 → ramp up, peak at 0.95 (e.g. 57/60)
|
|
4006
|
+
* ratio 0.95..1.0 → ramp down
|
|
4007
|
+
* ratio > 1.0 → 0.0
|
|
4008
|
+
*/
|
|
4009
|
+
membershipFpsOk(ratio) {
|
|
4010
|
+
if (ratio < 0.8)
|
|
4011
|
+
return 0.0;
|
|
4012
|
+
if (ratio < 0.95)
|
|
4013
|
+
return (ratio - 0.8) / 0.15;
|
|
4014
|
+
if (ratio <= 1.0)
|
|
4015
|
+
return (1.0 - ratio) / 0.05;
|
|
4016
|
+
return 0.0;
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* FPS ratio membership "high".
|
|
4020
|
+
* Input: ratio = actualFPS / targetFPS.
|
|
4021
|
+
*
|
|
4022
|
+
* ratio < 0.9 → 0.0
|
|
4023
|
+
* ratio 0.9..1.0 → linear ramp up (e.g. 0.95 → 0.5)
|
|
4024
|
+
* ratio >= 1.0 → 1.0 (meeting or exceeding target, e.g. 60/60)
|
|
4025
|
+
*/
|
|
4026
|
+
membershipFpsHigh(ratio) {
|
|
4027
|
+
if (ratio < 0.9)
|
|
4028
|
+
return 0.0;
|
|
4029
|
+
if (ratio > 1.0)
|
|
4030
|
+
return 1.0;
|
|
4031
|
+
return (ratio - 0.9) / 0.1;
|
|
4032
|
+
}
|
|
4033
|
+
/**
|
|
4034
|
+
* Bitrate load membership "high".
|
|
4035
|
+
* Input: load = currentBitrate / MAX_BITRATE (20 Mbit/s).
|
|
4036
|
+
*
|
|
4037
|
+
* load < 0.5 → 0.0 (below 10 Mbit/s — plenty of headroom)
|
|
4038
|
+
* load 0.5..0.8 → linear ramp (e.g. 13M/20M = 0.65 → 0.5)
|
|
4039
|
+
* load > 0.8 → 1.0 (above 16 Mbit/s — near capacity)
|
|
4040
|
+
*/
|
|
4041
|
+
membershipBitrateHigh(load) {
|
|
4042
|
+
if (load < 0.5)
|
|
4043
|
+
return 0.0;
|
|
4044
|
+
if (load > 0.8)
|
|
4045
|
+
return 1.0;
|
|
4046
|
+
return (load - 0.5) / 0.3;
|
|
4047
|
+
}
|
|
4048
|
+
// ---------------------------------------------------------------------------
|
|
4049
|
+
// Step actions
|
|
4050
|
+
// ---------------------------------------------------------------------------
|
|
4051
|
+
upgradeFps(decision, qp) {
|
|
4052
|
+
if (decision === 'hold')
|
|
4053
|
+
return;
|
|
4054
|
+
const now = Date.now();
|
|
4055
|
+
// Throttle: cooldown depends on (previous decision → current decision) pair.
|
|
4056
|
+
const isStepUp = decision === 'increase';
|
|
4057
|
+
const cooldown = this.lastUpgrade === 'decrease'
|
|
4058
|
+
? isStepUp
|
|
4059
|
+
? this.COOLDOWN_INCREASE_AFTER_DECREASE_MS
|
|
4060
|
+
: this.COOLDOWN_DECREASE_AFTER_DECREASE_MS
|
|
4061
|
+
: this.COOLDOWN_AFTER_INCREASE_MS;
|
|
4062
|
+
if (now - this.lastDecisionTime < cooldown) {
|
|
4063
|
+
return;
|
|
4064
|
+
}
|
|
4065
|
+
this.lastUpgrade = decision;
|
|
4066
|
+
const quality = mapQpToQuality(qp);
|
|
4067
|
+
const newTarget = this.getNextFpsTarget(this.currentFpsTarget, decision);
|
|
4068
|
+
Logger.info(`[FpsMonitor] Decision: ${decision} → FPS: ${newTarget}, Quality: ${quality}`);
|
|
4069
|
+
if (newTarget !== this.currentFpsTarget) {
|
|
4070
|
+
this.currentFpsTarget = newTarget;
|
|
4071
|
+
this.store.dispatch(setMaxFps({ maxFps: newTarget }));
|
|
4072
|
+
}
|
|
4073
|
+
this.lastDecisionTime = now;
|
|
4074
|
+
this.samples = [];
|
|
4075
|
+
}
|
|
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: () => [] });
|
|
3874
4126
|
|
|
3875
4127
|
class UnrealErrorModalComponent {
|
|
3876
4128
|
constructor() {
|
|
@@ -4615,7 +4867,7 @@ class LatencyTimings {
|
|
|
4615
4867
|
OnAllLatencyTimingsReady(_) { }
|
|
4616
4868
|
}
|
|
4617
4869
|
|
|
4618
|
-
|
|
4870
|
+
const mapQpToQuality = (VideoEncoderQP) => {
|
|
4619
4871
|
const orangeQP = 26;
|
|
4620
4872
|
const redQP = 35;
|
|
4621
4873
|
let quality = 'lime';
|
|
@@ -4626,17 +4878,17 @@ function mapQpToQuality(VideoEncoderQP) {
|
|
|
4626
4878
|
quality = 'orange';
|
|
4627
4879
|
}
|
|
4628
4880
|
return quality;
|
|
4629
|
-
}
|
|
4881
|
+
};
|
|
4630
4882
|
|
|
4631
4883
|
const removeExileCommands = (state, idToRemove) => {
|
|
4632
|
-
const
|
|
4884
|
+
const exileTimeout = 10000;
|
|
4633
4885
|
const out = { ...state };
|
|
4634
4886
|
out.commandsInProgress = out.commandsInProgress.filter(whereNot({ id: idToRemove }));
|
|
4635
4887
|
out.totalCommandsCompleted++;
|
|
4636
4888
|
const time = new Date().getTime();
|
|
4637
4889
|
const markForDelete = [];
|
|
4638
4890
|
Object.entries(out.commandsInProgress).forEach(([, { id, timeStamp }]) => {
|
|
4639
|
-
if (time - timeStamp >
|
|
4891
|
+
if (time - timeStamp > exileTimeout) {
|
|
4640
4892
|
markForDelete.push(id);
|
|
4641
4893
|
}
|
|
4642
4894
|
});
|
|
@@ -5585,9 +5837,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
5585
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"] }]
|
|
5586
5838
|
}] });
|
|
5587
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
|
+
|
|
5588
5918
|
/**
|
|
5589
5919
|
* Generated bundle index. Do not edit.
|
|
5590
5920
|
*/
|
|
5591
5921
|
|
|
5592
|
-
export { AFKService, AfkPlaywrightService, AggregatorPlaywrightService, AggregatorService, AnalyticsService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, ClickableOverlayComponent, CommandTelemetryPlaywrightService, CommandTelemetryService, ConfigHandler, ConsoleExtensionsPlaywrightService, ConsoleExtensionsService, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DEFAULT_RECONNECT_DELAY_MS, DEFAULT_RECONNECT_ENABLED, DEFAULT_RECONNECT_MAX_ATTEMPTS, DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE, DEFAULT_RECONNECT_ON_ICE_FAILURE, DataFlowMonitor, DevModeService, DisconnectReason, EControlSchemeType, EMessageType, EToClientMessageType, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverPlaywrightService, FileReceiverService, FilterSettingsComponent, FreezeFrameComponent, FreezeFramePlaywrightService, FreezeFrameService, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputPlaywrightService, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, KalmanFilter1D, LatencyTimings, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RegionsPingService, ResetTelemetry, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_VIDEO_ID, SafePipe, SignallingPlaywrightService, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryPlaywrightService, StreamStatusTelemetryService, SubService, TelemetryStart, TelemetryStop, UNREAL_CONFIG, UnrealCommunicatorPlaywrightService, UnrealCommunicatorService, UnrealEffects, UnrealInternalSignalEvents, UnrealSceneComponent, UnrealStatusMessage, VideoRecorder, 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_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, changeLowBandwidth, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, floatToSmoothPercents, forceResizeUnrealVideo, fromResizeObserver, fromSignal, fromUnrealCallBackSignal, getActiveUrl, getImageFromVideoStream, getRtcErrorMessage, iceConnectionFailed, initSignalling, initialState, isLoaderScreenVisible, keepMaxUntilReset, mapQpToQuality, observeCommandResponse, provideAngularUnrealModule, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, removeExileCommands, resetAfk, resetAfkAction, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetWarnTimeout, saveAnalyticsEvent, selectClientAndViewIds, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsVideoPlayingAndDataChannelConnected, selectLastCommandInProgress, selectLoaderCommands, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, sendSignal, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setDataChannelConnected, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setViewportNotReady, setViewportReady, showPopupWithoutAutoStart, showUnrealErrorMessage, smoothTransition, startStream, trackMixpanelEvent, unrealFeature, unrealReducer, updateCirrusInfo };
|
|
5922
|
+
export { AFKService, AfkPlaywrightService, AggregatorPlaywrightService, AggregatorService, AnalyticsService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, ClickableOverlayComponent, CommandTelemetryPlaywrightService, CommandTelemetryService, ConfigHandler, ConsoleExtensionsPlaywrightService, ConsoleExtensionsService, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DEFAULT_RECONNECT_DELAY_MS, DEFAULT_RECONNECT_ENABLED, DEFAULT_RECONNECT_MAX_ATTEMPTS, DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE, DEFAULT_RECONNECT_ON_ICE_FAILURE, DataFlowMonitor, DevModeService, DisconnectReason, EControlSchemeType, EMessageType, EToClientMessageType, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverPlaywrightService, FileReceiverService, FilterSettingsComponent, FpsMonitorService, FreezeFrameComponent, FreezeFramePlaywrightService, FreezeFrameService, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputPlaywrightService, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, KalmanFilter1D, LatencyTimings, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RegionsPingService, ResetTelemetry, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_VIDEO_ID, SafePipe, SignallingPlaywrightService, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryPlaywrightService, StreamStatusTelemetryService, SubService, TelemetryStart, TelemetryStop, UNREAL_CONFIG, UnrealCommunicatorPlaywrightService, UnrealCommunicatorService, UnrealEffects, UnrealInternalSignalEvents, UnrealSceneComponent, UnrealStatusMessage, VideoRecorder, 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_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, changeLowBandwidth, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, floatToSmoothPercents, forceResizeUnrealVideo, fromResizeObserver, fromSignal, fromUnrealCallBackSignal, getActiveUrl, getImageFromVideoStream, getRtcErrorMessage, iceConnectionFailed, initSignalling, initialState, isLoaderScreenVisible, keepMaxUntilReset, mapQpToQuality, observeCommandResponse, provideAngularUnrealModule, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, removeExileCommands, resetAfk, resetAfkAction, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetWarnTimeout, saveAnalyticsEvent, selectClientAndViewIds, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsVideoPlayingAndDataChannelConnected, selectLastCommandInProgress, selectLoaderCommands, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, sendSignal, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setDataChannelConnected, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setViewportNotReady, setViewportReady, showPopupWithoutAutoStart, showUnrealErrorMessage, smoothTransition, startStream, trackMixpanelEvent, unrealFeature, unrealReducer, updateCirrusInfo };
|
|
5593
5923
|
//# sourceMappingURL=3dsource-angular-unreal-module.mjs.map
|