@3dsource/angular-unreal-module 0.0.146-dev.0 → 0.0.146-dev.1
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,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, DestroyRef, ChangeDetectionStrategy, Component, Injectable, signal, effect, untracked, computed, Pipe, input, ElementRef, viewChild, afterNextRender, HostListener, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
|
|
3
3
|
import { SourceButtonComponent, SourceLoadingComponent } from '@3dsource/source-ui-native';
|
|
4
|
-
import { Subject, skip, share, merge, map, scan, timestamp, startWith, pairwise, concatMap, of, animationFrameScheduler, interval, Observable, timer, take, defer, switchMap, from, throwError, EMPTY, retry, timeout as timeout$1, repeat, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, shareReplay, NEVER, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, Subscription, bufferTime, combineLatestWith, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
|
|
4
|
+
import { Subject, skip, share, merge, map, scan, timestamp, startWith, pairwise, concatMap, of, animationFrameScheduler, interval, Observable, timer, take, defer, switchMap, from, throwError, EMPTY, retry, timeout as timeout$1, repeat, BehaviorSubject, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, ignoreElements, shareReplay, NEVER, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, Subscription, bufferTime, combineLatestWith, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
|
|
5
5
|
import { createAction, props, createReducer, on, createFeature, Store, createSelector, provideState } from '@ngrx/store';
|
|
6
6
|
import { whereNot, Falsy, Logger, calculateMedian, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, generateUuid, Truthy, tapLog, Semaphore, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
|
|
7
|
-
import { filter, map as map$1, distinctUntilChanged, takeWhile, takeUntil, catchError, tap, timeout, finalize, first, switchMap as switchMap$1, withLatestFrom, exhaustMap, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
|
|
7
|
+
import { filter, map as map$1, distinctUntilChanged, takeWhile, takeUntil, catchError, tap, timeout, finalize, first, switchMap as switchMap$1, last, withLatestFrom, exhaustMap, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
|
|
8
8
|
import { Actions, ofType, createEffect, provideEffects } from '@ngrx/effects';
|
|
9
9
|
import { concatLatestFrom } from '@ngrx/operators';
|
|
10
10
|
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
@@ -643,10 +643,11 @@ const unrealReducer = createReducer(initialState, on(changeStatusMainVideoOnScen
|
|
|
643
643
|
return { ...state, analyticsEvent: event };
|
|
644
644
|
}), on(setCirrusConnected, (state) => {
|
|
645
645
|
return { ...state, cirrusConnected: true };
|
|
646
|
-
}), on(streamAdopted, (state, { machineId }) => {
|
|
646
|
+
}), on(streamAdopted, (state, { machineId, ssData }) => {
|
|
647
647
|
return {
|
|
648
648
|
...state,
|
|
649
649
|
cirrusConnected: true,
|
|
650
|
+
ssData: ssData ?? state.ssData,
|
|
650
651
|
awsInstance: {
|
|
651
652
|
...state.awsInstance,
|
|
652
653
|
wsUrl: machineId,
|
|
@@ -1314,13 +1315,18 @@ const selectTotalProgress = createSelector(unrealFeature.selectStatusPercentSign
|
|
|
1314
1315
|
const SIGNALLING_MAX = SIGNALLING_PERCENT_VALUE / 100;
|
|
1315
1316
|
const ORCHESTRATION_WS_PATTERN = /^wss:\/\/orchestration-websocket/i;
|
|
1316
1317
|
const isNewOrchestration = ORCHESTRATION_WS_PATTERN.test(awsInstance?.wsUrl ?? '');
|
|
1317
|
-
//
|
|
1318
|
-
//
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1318
|
+
// Once the data channel is connected, signaling is fully complete (1.0) —
|
|
1319
|
+
// regardless of orchestration type. This is what makes an ADOPTED stream
|
|
1320
|
+
// (whose wsUrl is the machine host, not the orchestration-websocket URL,
|
|
1321
|
+
// and which never set `statusPercentSignallingServer`) correctly show the
|
|
1322
|
+
// whole first phase done instead of stalling near the command-only value.
|
|
1323
|
+
// Otherwise: new orchestration reports 0–1 progress; legacy uses the
|
|
1324
|
+
// polling percent.
|
|
1325
|
+
const signalingProgress = dataChannelConnected
|
|
1326
|
+
? 1
|
|
1327
|
+
: isNewOrchestration
|
|
1328
|
+
? (awsInstance?.progressComplete ?? 0)
|
|
1329
|
+
: (statusPercentSignallingServer ?? 0) / 100;
|
|
1324
1330
|
// +1% bump once WS URL is known (visual step for WS handshake)
|
|
1325
1331
|
const splitPoint = awsInstance?.wsUrl
|
|
1326
1332
|
? SIGNALLING_MAX + 0.01
|
|
@@ -2573,7 +2579,13 @@ class VideoService extends SubService {
|
|
|
2573
2579
|
this.audio = null;
|
|
2574
2580
|
this.container = null;
|
|
2575
2581
|
this.latencyTestTimings = new LatencyTimings();
|
|
2576
|
-
|
|
2582
|
+
// BehaviorSubject (not Subject) so a late subscriber to `videoStats$` still
|
|
2583
|
+
// gets the current track. Adoption (`WebRtcPlayerService.adopt`) calls
|
|
2584
|
+
// `attachVideoStream` synchronously/early — before the resize and app-init
|
|
2585
|
+
// (`initAppByDataChannelReady$`) subscribers attach — and a plain Subject
|
|
2586
|
+
// would drop that emission, so the stats interval (and `setViewportReady`,
|
|
2587
|
+
// gated on `framesPerSecond`) would never start.
|
|
2588
|
+
this.videoTrack$ = new BehaviorSubject(null);
|
|
2577
2589
|
this.VideoEncoderQP = 0;
|
|
2578
2590
|
this.aggregatedStats = {};
|
|
2579
2591
|
this.kalmanFilter1D = new KalmanFilter1D(FilterModel.initialBitrateEstimate, FilterModel.initialErrorCovariance, FilterModel.processNoise, FilterModel.measurementNoise);
|
|
@@ -3566,6 +3578,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
3566
3578
|
args: [{ providedIn: 'root' }]
|
|
3567
3579
|
}], ctorParameters: () => [] });
|
|
3568
3580
|
|
|
3581
|
+
// How often to re-check `window.__stream.state` while a prefetch is connecting.
|
|
3582
|
+
const ADOPT_POLL_MS = 100;
|
|
3569
3583
|
/**
|
|
3570
3584
|
* Adopts a WebRTC connection prefetched by the `stream-prefetch.js` head-script
|
|
3571
3585
|
* and parked on `window.__stream`, instead of running `connectToSignaling` from
|
|
@@ -3579,6 +3593,32 @@ class StreamAdoptionService {
|
|
|
3579
3593
|
this.signalling = inject(SignallingService);
|
|
3580
3594
|
this.webRtc = inject(WebRtcPlayerService);
|
|
3581
3595
|
}
|
|
3596
|
+
/**
|
|
3597
|
+
* Decide adoption, WAITING for an in-flight prefetch instead of racing it.
|
|
3598
|
+
*
|
|
3599
|
+
* - nothing parked → emit `false` at once (caller connects normally).
|
|
3600
|
+
* - already settled (`ready`/`failed`/`adopted`) → adopt-if-ready at once.
|
|
3601
|
+
* - still `connecting` → poll until it settles, then adopt-if-ready. Bounded
|
|
3602
|
+
* by `timeoutMs` so a stuck prefetch can't block the fallback forever.
|
|
3603
|
+
*
|
|
3604
|
+
* Emits exactly once. Cancel by unsubscribing (the effect does this on
|
|
3605
|
+
* teardown via `takeUntil`) — no adoption runs after cancellation, which is
|
|
3606
|
+
* what stops a fast route-switch from starting overlapping connections.
|
|
3607
|
+
*/
|
|
3608
|
+
awaitAndAdopt$(timeoutMs) {
|
|
3609
|
+
const parked = window.__stream;
|
|
3610
|
+
if (!parked) {
|
|
3611
|
+
return of(false);
|
|
3612
|
+
}
|
|
3613
|
+
if (parked.state !== 'connecting') {
|
|
3614
|
+
return of(this.tryAdopt());
|
|
3615
|
+
}
|
|
3616
|
+
Logger.info('[Adoption] Prefetch in progress — waiting before connecting');
|
|
3617
|
+
return timer(0, ADOPT_POLL_MS).pipe(map$1(() => window.__stream?.state ?? 'failed'),
|
|
3618
|
+
// Keep polling while still connecting AND within budget; the `true`
|
|
3619
|
+
// (inclusive) emits the settling value (or the last one at the deadline).
|
|
3620
|
+
takeWhile((state, i) => state === 'connecting' && i * ADOPT_POLL_MS < timeoutMs, true), last(), map$1(() => this.tryAdopt()));
|
|
3621
|
+
}
|
|
3582
3622
|
/**
|
|
3583
3623
|
* @returns `true` if a ready parked stream was adopted (caller must NOT
|
|
3584
3624
|
* start a fresh connection), `false` otherwise.
|
|
@@ -3601,11 +3641,15 @@ class StreamAdoptionService {
|
|
|
3601
3641
|
// Stop the script's watchdog and mark the handoff complete.
|
|
3602
3642
|
parked.adopt();
|
|
3603
3643
|
// Flag cirrus connected (so a later `initSignalling` won't open a 2nd
|
|
3604
|
-
// connection)
|
|
3605
|
-
//
|
|
3606
|
-
//
|
|
3607
|
-
//
|
|
3608
|
-
|
|
3644
|
+
// connection), record the adopted machine, and hydrate the streamer info
|
|
3645
|
+
// (`ssData`) the prefetch captured from the `ssInfo` message. NOTE:
|
|
3646
|
+
// clientId/viewId are left untouched — the host app (metabox) already set
|
|
3647
|
+
// them authoritatively from its own data before `initSignalling`; the
|
|
3648
|
+
// script's ids are only used internally to establish the connection.
|
|
3649
|
+
this.store.dispatch(streamAdopted({
|
|
3650
|
+
machineId: parked.machineId ?? null,
|
|
3651
|
+
ssData: parked.ssData ?? null,
|
|
3652
|
+
}));
|
|
3609
3653
|
// Hand the live socket over without re-sending `requestStream`, reusing the
|
|
3610
3654
|
// script's correlationId (the one `requestStream` was already sent with).
|
|
3611
3655
|
this.signalling.adoptSocket(parked.ws, parked.ids.correlationId);
|
|
@@ -4148,12 +4192,21 @@ class UnrealEffects {
|
|
|
4148
4192
|
}), map(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
|
|
4149
4193
|
// Ensure a single in-flight request per attempt.
|
|
4150
4194
|
exhaustMap((urls) =>
|
|
4151
|
-
//
|
|
4152
|
-
//
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4195
|
+
// If a prefetched `window.__stream` is ready — or still connecting —
|
|
4196
|
+
// adopt it (waiting for it to finish) instead of racing a second
|
|
4197
|
+
// connection; otherwise fall back to a fresh signalling connection.
|
|
4198
|
+
// `takeUntil(abort)` cancels the wait on teardown / route change so a
|
|
4199
|
+
// fast switch can't leave overlapping connections.
|
|
4200
|
+
this.streamAdoptionService
|
|
4201
|
+
.awaitAndAdopt$(this.dataChannelConnectionTimeout)
|
|
4202
|
+
.pipe(switchMap((adopted) => adopted ? EMPTY : this.signallingService.connectToSignaling(urls)), takeUntil(this.actions$.pipe(ofType(abortEstablishingConnection))),
|
|
4203
|
+
// The adopt/connect stream is side-effect-only (its values aren't
|
|
4204
|
+
// actions) — swallow them, and emit `setEstablishingConnection`
|
|
4205
|
+
// first so the reconnect/resume popup stays hidden during the
|
|
4206
|
+
// (possibly deferred) prefetch-adoption wait. Adoption otherwise
|
|
4207
|
+
// defers `connectToSignaling`, which is what normally sets this.
|
|
4208
|
+
ignoreElements(), startWith(setEstablishingConnection({ value: true })))));
|
|
4209
|
+
});
|
|
4157
4210
|
}
|
|
4158
4211
|
canPeerReconnect() {
|
|
4159
4212
|
return RECONNECT_CONFIG.enabled && this.signallingService.isConnected();
|