@3dsource/angular-unreal-module
Version:
Angular Unreal module for connect with unreal engine stream
1,304 lines (1,225 loc) • 87 kB
TypeScript
import * as i0 from '@angular/core';
import { AfterViewInit, ElementRef, OnInit, InjectionToken, PipeTransform } from '@angular/core';
import { DialogRef } from '@angular/cdk/dialog';
import * as _ngrx_store from '@ngrx/store';
import { Store } from '@ngrx/store';
import * as rxjs from 'rxjs';
import { Observable, Subject } from 'rxjs';
import * as _3dsource_angular_unreal_module from '@3dsource/angular-unreal-module';
import { MetaBoxCommandPacket, MetaBoxCommandList } from '@3dsource/types-unreal';
import { IKeyCode, ImageOutputValues } from '@3dsource/utils';
import { SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
import * as _ngrx_effects from '@ngrx/effects';
declare class ClickableOverlayComponent {
state: i0.Signal<{
message: string;
className: string;
isActivityDetected: boolean;
onOverlayClick: () => void;
} | null | undefined>;
static ɵfac: i0.ɵɵFactoryDeclaration<ClickableOverlayComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<ClickableOverlayComponent, "app-clickable-overlay", never, {}, {}, never, never, true, never>;
}
declare class FreezeFrameComponent {
private store;
freezeFrameProgressMessageFromVideo: i0.Signal<number | null | undefined>;
combinedFreeze: i0.Signal<string | null | undefined>;
static ɵfac: i0.ɵɵFactoryDeclaration<FreezeFrameComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<FreezeFrameComponent, "app-freeze-frame", never, {}, {}, never, never, true, never>;
}
declare class LowBandwidthModalComponent {
dialogRef: DialogRef<any, any>;
close(value?: boolean): void;
static ɵfac: i0.ɵɵFactoryDeclaration<LowBandwidthModalComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<LowBandwidthModalComponent, "app-low-bandwidth-modal", never, {}, {}, never, never, true, never>;
}
declare class ImageLoadingSrcComponent {
store: Store<any>;
isLoaderScreenVisible: i0.Signal<boolean>;
imageLoadingSrc: i0.Signal<string>;
static ɵfac: i0.ɵɵFactoryDeclaration<ImageLoadingSrcComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<ImageLoadingSrcComponent, "app-image-loading-src", never, {}, {}, never, never, true, never>;
}
declare class StatGraphComponent implements AfterViewInit {
private el;
graph: ElementRef;
private canvas;
private ctx;
readonly color: i0.InputSignal<string>;
readonly tickStep: i0.InputSignal<number>;
readonly label: i0.InputSignal<string>;
private scaleY;
set dataTick(data: {
value: number;
hash: number;
} | null);
private data;
max: number;
min: number;
current: number;
ngAfterViewInit(): void;
resize(): void;
private draw;
private getColor;
private pushData;
static ɵfac: i0.ɵɵFactoryDeclaration<StatGraphComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<StatGraphComponent, "app-stat-graph", never, { "color": { "alias": "color"; "required": false; "isSignal": true; }; "tickStep": { "alias": "tickStep"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "dataTick": { "alias": "dataTick"; "required": false; }; }, {}, never, never, true, never>;
}
declare class UnrealSceneComponent {
isStudio: i0.InputSignal<boolean>;
useContainerAsSizeProvider: i0.InputSignal<boolean>;
studioResolutionSize: i0.InputSignal<{
width: number;
height: number;
}>;
studioResolutionValues: rxjs.Observable<{
width: number;
height: number;
}>;
readonly videoElement: i0.Signal<ElementRef<HTMLDivElement> | undefined>;
changeMouseOverScene: i0.OutputEmitterRef<boolean>;
private width;
private height;
private store;
private isExistMatchUrls;
readonly isDevMode: boolean;
readonly lightMode: i0.Signal<boolean>;
private isFreezeFrameLoading;
private commandsSender;
private videoService;
private element;
private destroyRef;
private get resizeValues();
private get pixelRatio();
onMouseOver(): void;
onMouseOut(): void;
constructor();
private listenResizeValues;
private adaptVideo;
/**
* This method calculates the dimensions for resizing a layout in a configurator stream.
* It takes into account the pixel ratio of the device's screen and the orientation of the layout.
* The method uses the `fitIntoRectangle` function to calculate the dimensions that would allow the layout to fit into the maximum space while maintaining its aspect ratio.
* It also sets the maximum limits for the width and height based on the layout's orientation.
* If the scale of the fitted dimensions is less than 1, it assigns the fitted dimensions to the output. Otherwise, it sets the scale of the output to 1.
* @returns {Area} An object of type `Area` that contains the calculated dimensions for resizing the layout.
*/
private getResizeValuesUniversal;
private getResizeValues;
private sendResize;
private makeEven;
static ɵfac: i0.ɵɵFactoryDeclaration<UnrealSceneComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<UnrealSceneComponent, "app-unreal-scene", never, { "isStudio": { "alias": "isStudio"; "required": false; "isSignal": true; }; "useContainerAsSizeProvider": { "alias": "useContainerAsSizeProvider"; "required": false; "isSignal": true; }; "studioResolutionSize": { "alias": "studioResolutionSize"; "required": false; "isSignal": true; }; }, { "changeMouseOverScene": "changeMouseOverScene"; }, never, ["app-pdf", "app-show-case", "app-low-bandwidth-detector"], true, never>;
}
declare class IntroSrcComponent {
private videoElement;
private store;
protected imageIntroSrc: i0.Signal<string | null>;
protected videoIntroSrc: i0.Signal<string | null>;
private viewportReady;
constructor();
private createVideo;
static ɵfac: i0.ɵɵFactoryDeclaration<IntroSrcComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<IntroSrcComponent, "app-intro-src", never, {}, {}, never, never, true, never>;
}
declare class VideoStatsComponent implements OnInit {
collapsed: boolean;
private store;
private videoService;
private isDevMode;
private obs$;
viewportReady: i0.Signal<boolean | undefined>;
ssInfo: i0.Signal<string | undefined>;
fpsTick: i0.Signal<any>;
fpsAvgTick: i0.Signal<any>;
bitrateTick: i0.Signal<any>;
videoQP: i0.Signal<any>;
bitrateAverageTick: i0.Signal<any>;
videoStatus: i0.Signal<{
key: string;
value: any;
}[] | undefined>;
fields: string[];
graphList: any[];
graphList$: Observable<any>;
private idKey;
private elementsToShow;
private trigger$;
constructor();
ngOnInit(): void;
updateGraph(): void;
toggle(): void;
toggleGraph($event: MouseEvent, el: string, value?: any): void;
private getFields;
private writeFields;
static ɵfac: i0.ɵɵFactoryDeclaration<VideoStatsComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<VideoStatsComponent, "app-video-stats", never, {}, {}, never, never, true, never>;
}
declare class WebrtcErrorModalComponent {
private dialogRef;
private store;
close(): void;
closeModalWithCirrusDisconnect(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<WebrtcErrorModalComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<WebrtcErrorModalComponent, "app-webrtc-error-modal", never, {}, {}, never, never, true, never>;
}
declare const WSCloseCode_NORMAL_CLOSURE = 3000;
declare const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
declare const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
declare const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
declare const WSCloseCode_UNKNOWN = 1000;
declare const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
declare const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
declare const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
declare const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
declare const WSCloseCodes: readonly [3000, 3001, 3002, 3003, 1000, 1001, 1006, 1013, 1011];
type WSCloseCodesValues = (typeof WSCloseCodes)[number];
declare const DisconnectReason: {
readonly Afk: "Afk";
readonly None: "None";
readonly Destroy: "Destroy";
readonly DataChannelClosed: "DataChannelClosed";
readonly DataChannelTimeout: "DataChannelTimeout";
readonly WebRTCError: "WebRTCError";
readonly WebSocketError: "WebSocketError";
readonly WebSocketClose: "WebSocketClose";
readonly DropConnection: "DropConnection";
readonly OrchestrationPlayerDisconnected: "OrchestrationPlayerDisconnected";
readonly OrchestrationStreamerDisconnected: "OrchestrationStreamerDisconnected";
};
type DisconnectReasonType = (typeof DisconnectReason)[keyof typeof DisconnectReason];
interface ConnectionError {
reason: DisconnectReasonType;
message: string;
}
interface CloseReason {
code: WSCloseCodesValues;
reason: DisconnectReasonType;
}
interface FreezeFrameMessage {
dataUrl: string | null;
progress: number | null;
}
interface UnrealInitialConfig {
customErrorsEndpoint?: string;
commandTelemetryReceiver?: string;
regionsPingUrl?: string;
screenLockerContainerId?: string;
dataChannelConnectionTimeout?: number;
playwright?: boolean;
}
declare const SpecialKeyCodes: {
BackSpace: number;
Shift: number;
Control: number;
Alt: number;
RightShift: number;
RightControl: number;
RightAlt: number;
};
declare const MouseButton: {
MainButton: number;
AuxiliaryButton: number;
SecondaryButton: number;
FourthButton: number;
FifthButton: number;
};
declare const MouseButtonsMask: {
PrimaryButton: number;
SecondaryButton: number;
AuxiliaryButton: number;
FourthButton: number;
FifthButton: number;
};
declare const EControlSchemeType: {
readonly LockedMouse: 0;
readonly HoveringMouse: 1;
readonly Default: 2;
};
type EControlSchemeTypeValues = (typeof EControlSchemeType)[keyof typeof EControlSchemeType];
declare const EMessageType: {
readonly IFrameRequest: 0;
readonly RequestQualityControl: 1;
readonly MaxFpsRequest: 2;
readonly AverageBitrateRequest: 3;
readonly StartStreaming: 4;
readonly StopStreaming: 5;
readonly LatencyTest: 6;
readonly RequestInitialSettings: 7;
readonly UIInteraction: 50;
readonly Command: 51;
readonly KeyDown: 60;
readonly KeyUp: 61;
readonly KeyPress: 62;
readonly MouseEnter: 70;
readonly MouseLeave: 71;
readonly MouseDown: 72;
readonly MouseUp: 73;
readonly MouseMove: 74;
readonly MouseWheel: 75;
readonly TouchStart: 80;
readonly TouchEnd: 81;
readonly TouchMove: 82;
};
type EMessageTypeValues = (typeof EMessageType)[keyof typeof EMessageType];
declare const EToClientMessageType: {
readonly QualityControlOwnership: 0;
readonly Response: 1;
readonly Command: 2;
readonly FreezeFrame: 3;
readonly UnfreezeFrame: 4;
readonly VideoEncoderAvgQP: 5;
readonly LatencyTest: 6;
readonly InitialSettings: 7;
readonly FileExtension: 8;
readonly FileMimeType: 9;
readonly FileContents: 10;
readonly TestEcho: 11;
readonly InputControlOwnership: 12;
readonly GamepadResponse: 13;
readonly Protocol: 255;
};
declare const UNREAL_CONFIG: InjectionToken<UnrealInitialConfig>;
declare const InputOptions: InputProps;
declare const UnrealStatusMessage: {
readonly CONNECTING_TO_SESSION: "Connecting to session.";
readonly STARTING_YOUR_SESSION: "Starting your session";
};
declare const DEBOUNCE_TO_MANY_RESIZE_CALLS = 100;
declare const SAME_SIZE_THRESHOLD = 1.01;
declare const MINIMAL_FPS = 6;
declare const STREAMING_VIDEO_ID = "streamingVideo";
declare const CONSOLE_COMMAND_ENABLE_MESSAGES = "EnableAllScreenMessages";
declare const CONSOLE_COMMAND_DISABLE_MESSAGES = "DisableAllScreenMessages";
declare const CONSOLE_COMMAND_PIXEL_QUALITY = "PixelStreaming.FreezeFrameQuality 95";
declare const FULL_HD_WIDTH = 1920;
declare const FULL_HD_HEIGHT = 1080;
declare const WS_TIMEOUT = 2000;
declare const POLLING_TIME = 4000;
declare const WS_OPEN_STATE = 1;
declare const DEFAULT_AFK_TIMEOUT_PERIOD = 15;
declare const DEFAULT_AFK_TIMEOUT = 120;
declare const DATA_CHANNEL_CONNECTION_TIMEOUT = 8000;
declare const SIGNALLING_PERCENT_VALUE = 56;
declare const SCREEN_LOCKER_CONTAINER_ID = "3dsource_start_screen";
declare function provideAngularUnrealModule(config?: {
playwright: boolean;
}): i0.EnvironmentProviders;
interface NormalizeAndQuantizeUnsignedValue {
inRange: boolean;
x: number;
y: number;
}
interface UnquantizeAndDenormalizeUnsignedValue {
x: number;
y: number;
}
interface NormalizeAndQuantizeSignedValue {
x: number;
y: number;
}
interface InputProps {
controlScheme: EControlSchemeTypeValues;
suppressBrowserKeys: boolean;
fakeMouseWithTouches: boolean;
}
declare class SubService {
store: Store<any>;
disconnect$: rxjs.Observable<false>;
}
declare class AFKService extends SubService {
private enabled;
private readonly closeTimeout;
private active;
private warnTimer;
private countdown;
private countdownTimer;
private selectWarnTimeout;
private isViewportReady;
constructor();
protected init(): void;
private hideOverlay;
/**
* Start a timer which when elapsed will warn the user they are inactive.
*/
private startAfkWarningTimer;
/**
* If the user interacts, then reset the warning timer.
*/
private resetAfkWarningTimer;
/**
* Update the count-down spans number for the overlay
* @param countdown the count down number to be inserted into the span for updating
*/
private updateCountDown;
/**
* Update the text overlays inner text
* @param message the update text to be inserted into the overlay
*/
private dispatchMessage;
private clearTimers;
private stop;
private reset;
private showAfkOverlay;
static ɵfac: i0.ɵɵFactoryDeclaration<AFKService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AFKService>;
}
declare class AggregatorService extends SubService {
private readonly selectStreamConfig;
private readonly videoService;
private readonly webrtcPlayer;
private readonly freezeFrame;
private readonly fileReceiver;
private readonly unrealInitialConfig;
private readonly responseEventListeners;
/**
* Never called for now
*/
private readonly destroy$;
constructor();
protected init(): void;
protected listenWebRTC(): void;
protected initialize(): void;
private addResponseEventListener;
private showOnScreenKeyboard;
private removeLoadScreen;
private startListenCallbacks;
private showMessageFromUnreal;
private resetResponseList;
private dataChannelMessageHandler;
static ɵfac: i0.ɵɵFactoryDeclaration<AggregatorService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AggregatorService>;
}
declare class CommandTelemetryService {
private readonly appId;
private commandsSent;
private exileTimeout;
private pollingTime;
private commandSequenceNumber;
/**
* The time the telemetry service was started.
* Initialized at a moment when connectToSignaling() was called.
* @private
*/
private startTime;
private lastTime;
private uuid;
private viewId;
private sessionId;
private userId;
private payloads;
private readonly httpClient;
private readonly unrealInitialConfig;
constructor();
protected init(): void;
private get timeNow();
private get sessionTime();
private get canSkipSending();
decorate(funcToDecorate: IToBeDecorated): (data: any) => void;
protected start(): void;
private reset;
private trackStartCommand;
private trackStopCommand;
private pushStatToBuffer;
private trackTime;
private send;
/**
* Listens for Unreal Engine callbacks.
* Subscribes to the Unreal Engine event loop back and filters out events that do not have a tracking ID.
* When a callback with a tracking ID is received,
* it stops tracking the time for that command and filters out commands
* that have exceeded the timeout limit.
*/
protected listenCallbacks(): void;
private pushData;
/**
* Removes commands that have exceeded the timeout limit.
* Iterates over all the commands sent and checks if the difference between the current time
* and the time the command was sent is greater than the timeout limit.
* If it is, the command is marked for deletion.
* After checking all commands, those marked for deletion are removed from the commands sent.
* This method is used to ensure that commands that are not responded to within a certain time frame
* do not remain in the commandsSent object indefinitely, which could lead to memory leaks over time.
*/
private removeExileCommands;
/**
* Wraps the provided function with telemetry tracking.
* Generates a unique tracking ID and adds it to the data object.
* Pushes the command sent to the payloads with the tracking ID.
* Starts tracking the time for the command.
* Finally, calls the provided function with the modified data object.
*
* @param {IToBeDecorated} funcToDecorate - The function to be decorated with telemetry tracking.
* @param {any} data - The data object to be passed to the function. It will be augmented with a unique tracking ID.
*/
private wrapper;
static ɵfac: i0.ɵɵFactoryDeclaration<CommandTelemetryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<CommandTelemetryService>;
}
type IToBeDecorated = (data: any) => unknown;
declare function TelemetryStart(externalId: string): void;
declare function TelemetryStop(externalId: string, payload?: any): void;
declare function ResetTelemetry(): void;
declare class ConsoleExtensionsService extends SubService {
#private;
constructor();
protected init(): void;
private unrealHelp;
static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleExtensionsService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<ConsoleExtensionsService>;
}
declare class DevModeService {
get isDevMode(): boolean;
setDevMode(value: boolean): void;
static ɵfac: i0.ɵɵFactoryDeclaration<DevModeService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<DevModeService>;
}
declare class FreezeFrameService extends SubService {
receiving: boolean;
private size;
private jpeg;
private freezeFrameOverlay;
constructor();
protected init(): void;
setData(view: Uint8Array): void;
private dispatchInProgress;
start(view: Uint8Array): void;
invalidate(): void;
private showFreezeFrame;
static ɵfac: i0.ɵɵFactoryDeclaration<FreezeFrameService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FreezeFrameService>;
}
declare class InputService extends SubService {
private commandsSender;
private isDevMode;
private videoService;
readonly defaultKeys: (17 | 37 | 38 | 39 | 40 | 46 | 65 | 68 | 83 | 87 | 107 | 109 | 187 | 189)[];
private availableKeys;
private options;
private reInit$;
private deactivatedVideoTrigger$;
private normalizeAndQuantizeUnsigned;
private unQuantizeAndDenormalizeUnsigned;
private normalizeAndQuantizeSigned;
videoBoundingRect: DOMRect;
fingerIds: Map<any, any>;
maxByteValue: number;
fingers: number[];
private offsetParams;
constructor();
protected init(): void;
useKeyboardKeys(keys: IKeyCode[]): void;
get video(): HTMLVideoElement | null;
private setup;
/**
* We use `object-fit: cover` on the video element. Since the video can have
* a different aspect ratio from its container (and potentially be larger),
* we need to calculate offsets and scale factors to map container (e.g., mouse)
* coordinates back to the actual video coordinates.
*
* @param videoElement The video element whose scaling and positioning we need
* @returns Object containing offset and scale info for coordinate normalization
* @private
*/
private getObjectFitOffset;
private setupNormalizeAndQuantize;
private sendInputData;
private registerInputs;
private getTouchPositionWithOffset;
private getMousePositionWithOffset;
private registerTouchEvents;
focusWindow(): void;
emitMouseMove(x: number, y: number, deltaX: number, deltaY: number): void;
emitMouseDown(button: number, x: number, y: number): void;
emitMouseUp(button: number, x: number, y: number): void;
emitMouseWheel(delta: number, x: number, y: number): void;
private releaseMouseButtons;
private pressMouseButtons;
private registerHoveringMouseEvents;
private registerMouseEnterAndLeaveEvents;
private isKeyCodeBrowserKey;
private getKeyCode;
private registerKeyboardEvents;
private startListenKeys;
}
declare class RegionsPingService {
private httpClient;
private unrealInitialConfig;
config: {
ping_count: number;
ping_timeout: number;
max_parallel_requests: number;
bust_cache: boolean;
};
constructor();
getFastest(regionListUrl?: string): Observable<string | undefined>;
getPingResult(providers: ProvidersData): Observable<PingResult | null>;
getProviders(regionList?: string): Observable<ProvidersData>;
private startPinging;
private checkLatency;
private fetchWithTimeout;
static ɵfac: i0.ɵɵFactoryDeclaration<RegionsPingService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<RegionsPingService>;
}
interface ProvidersData {
timeout?: number;
regions: Region[];
}
interface Region {
region_code: string;
url: string;
timeout?: number;
}
interface PingResult {
region_code: string | undefined;
result: number[];
}
declare class SignallingService extends SubService {
private readonly action$;
private readonly httpClient;
private readonly regionsPingService;
readonly region: i0.WritableSignal<string>;
readonly selectClientAndViewIds: i0.Signal<{
clientId: string | null;
viewId: string | null;
}>;
readonly environmentId: i0.Signal<string | null>;
readonly streamRequestContext: i0.Signal<_3dsource_angular_unreal_module.StreamRequestContext | null>;
onOffer$: Subject<RTCSessionDescriptionInit>;
onConfig$: Subject<ConfigMessage>;
onWebRtcIce$: Subject<RTCIceCandidate>;
onWebRtcAnswer$: Subject<RTCSessionDescriptionInit | null>;
abort$: Observable<_ngrx_store.Action<string>>;
private ws;
private wsMsgHandlers;
protected correlationId: string;
constructor();
protected init(): void;
connectToSignaling(urlList: string[]): Observable<WebSocket>;
protected startEstablishingConnection(): void;
private adaptUrlsToRegion;
/**
* Resolves an active AWS *signaling* instance and returns a WebSocket endpoint.
*
* The observable produced by this method is **cold** and executes inside a `defer`,
* so each subscription (including those triggered by `retry`) pulls the **next**
* URL candidate from a generator created by `getActiveUrl(urlsPool)`.
*
* ### Execution flow
*
* 1. **Telemetry** — Starts a `getSignaling` span for end-to-end timing.
* 2. **Candidate URL selection** — A stateful generator (`urlGen`) is created
* from `urlsPool`. Each resubscription (e.g. via `retry`) advances to the next URL.
* 3. **Short-circuit for WS URLs** — If the picked candidate already matches
* `ws://` or `wss://`, emit `{ wsUrl, pollingUrl: null, instanceName: null }`
* immediately and complete (no HTTP request).
* 4. **Await client/view identifiers** — `this.store.select(selectClientAndViewIds)`
* is tapped for logging (missing IDs) and then filtered to require both `clientId`
* and `viewId` to be truthy.
* 5. **Single in-flight HTTP orchestration call** — Uses `exhaustMap` to issue
* `GET {signalingUrl}{clientId}/{viewId}`. New `{clientId, viewId}` emissions
* while the request is in flight are **ignored** until completion, preventing overlap.
* 6. **Per-attempt timeout** — `timeout(WS_TIMEOUT)` caps how long we wait for a response.
* On timeout, the attempt errors and the outer `retry` schedules the next URL.
* 7. **Response validation & progress reporting** — A `filter`:
* - Calls `TelemetryStop('getSignaling', { ...data, multi: true })` on receipt.
* - If `data.signallingServer === ''` or `data.error`:
* shows a status message, optionally dispatches `setStatusPercentSignallingServer`
* with `data.info.percent`, and **throws** to fail the attempt (triggers `retry`).
* 8. **Mapping to `AwsInstance`** — Converts the validated orchestration payload into:
* - `wsUrl`: via `httpUrlToWs(\`\${location.protocol}//\${data.signallingServer}\`)`
* - `pollingUrl`: the base `signalingUrl` that succeeded
* - `instanceName`: the (validated) `data.signallingServer`
* 9. **Retry policy** — On *any* upstream error (timeout, HTTP error, invalid payload),
* `retry({ delay })`:
* - Shows a “connecting” status,
* - Logs the attempt number,
* - Waits `WS_TIMEOUT` ms (via `timer`) and **resubscribes**, advancing `urlGen`.
*
* ### Concurrency semantics
*
* - `exhaustMap` guarantees a **single** HTTP request per attempt; subsequent
* `{clientId, viewId}` emissions are ignored until the current request completes.
*
* ### Telemetry semantics
*
* - `TelemetryStart('getSignaling')` begins before work.
* - `TelemetryStop('getSignaling', {...})` runs on receipt of an orchestration response.
* If an attempt fails (e.g., timeout), stopping the span is the responsibility of
* your telemetry layer or a `finalize` elsewhere if desired.
*
* ### Error & retry semantics
*
* - Missing/never-emitted IDs → timeout → retry with next URL.
* - HTTP/network error → retry with next URL.
* - Invalid orchestration payload (`error` set or empty `signallingServer`) → throw in
* validation filter → retry with next URL.
* - Short-circuit WS path **does not** retry (emits once, completes).
*
* @param urlsPool - Ordered list of base URLs to probe. Retries advance through this list
* until a working signaling server is found. A candidate may also be a direct `ws://`/`wss://`
* URL to short-circuit HTTP orchestration.
*
* @returns Observable that emits exactly one {@link AwsInstance} on success and then completes.
* On failure, the stream errors; the built-in `retry` operator re-subscribes after `WS_TIMEOUT`
* and advances to the next URL candidate.
*
* @throws Emits an error within the observable chain (not a thrown synchronous exception) when:
* - The HTTP request fails or times out.
* - The orchestration response indicates an error or an empty `signallingServer`.
* - Any other operator in the chain surfaces an error.
*
* @remarks
* - The URL generator (`urlGen`) is created **outside** `defer`, so resubscriptions
* advance the pool. Creating it inside `defer` would restart from the first URL each retry.
* - `location.protocol` is used to derive `ws` vs `wss`. If this may run in non-browser
* contexts (e.g., SSR), guard or abstract this logic.
* - Consider capping retries with `count` in `retry({ count, delay })` when appropriate.
*
* @example
* ```ts
* getAwsInstance(['https://signaling.example.com/', 'wss://direct.example.com'])
* .subscribe({
* next: ({ wsUrl }) => connect(wsUrl),
* error: (e) => console.error('Unable to establish signaling:', e),
* });
* ```
*
* @see httpUrlToWs
* @see selectClientAndViewIds
* @see setStatusPercentSignallingServer
*/
private getAwsInstance;
private connectToCirrus;
addWsHandlers(ws: WebSocket): void;
showStatusMessage(statusMessage: string | null): void;
/**
* Close the connection with the signaling server
* @param data
*/
close(data: CloseReason): void;
send(data: AnySignalingMessage): void;
private clearWs;
protected handleMessage(msg: MessageBase): void;
private setHandlersFromStream;
protected sendRequestStream(): void;
private getRegion;
static ɵfac: i0.ɵɵFactoryDeclaration<SignallingService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<SignallingService>;
}
declare class StreamStatusTelemetryService {
#private;
constructor();
protected init(): void;
private mapEventData;
private trackEventToMixPanel;
static ɵfac: i0.ɵɵFactoryDeclaration<StreamStatusTelemetryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<StreamStatusTelemetryService>;
}
declare class UnrealCommunicatorService {
private store;
private commandTelemetryService;
private freezeFrameService;
private webRtcPlayerService;
private videoService;
private destroy$;
private cirrusConnected;
constructor();
protected init(): void;
destroy(): void;
sendCommandToUnreal(data: MetaBoxCommandPacket): void;
emitUIInteraction(descriptor: MetaBoxCommandPacket): void;
/**
A build-in command can be sent to the UE4 client. The commands are defined by a
JSON descriptor and will be executed automatically.
The currently supported commands are:
1. A command to run any console command:
"{ ConsoleCommand: <string> }"
2. A command to change the resolution to the given width and height.
"{ Resolution.Width: <value>, Resolution.Height: <value> }"
*/
emitCommand(descriptor: MetaBoxCommandPacket): void;
sendInputData(data: ArrayBuffer): void;
private emitDescriptor;
private requestInitialSettings;
private requestQualityControl;
protected listenVideo(): void;
private prepareScreen;
private showPlayOverlay;
static ɵfac: i0.ɵɵFactoryDeclaration<UnrealCommunicatorService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<UnrealCommunicatorService>;
}
declare class VideoService extends SubService {
video: HTMLVideoElement | null;
audio: HTMLAudioElement | null;
private container;
private latencyTestTimings;
private videoTrack$;
private VideoEncoderQP;
private aggregatedStats;
private kalmanFilter1D;
/**
* Aggregate video stats and emit it as videoStats$
*/
videoStats$: rxjs.Observable<IAggregatedStat>;
constructor();
protected init(): void;
setContainer(container?: HTMLDivElement | null): void;
setLatencyTimings(latencyTimings: string): void;
setEncoder(data: any): void;
create(): void;
attachVideoStream(stream: MediaStream, pcClient: RTCPeerConnection): void;
attachAudioStream(stream: MediaStream): void;
play(): void;
safePlay(video: HTMLVideoElement | null): Promise<void>;
private getStats;
private generateAggregatedStatsFunction;
private onAggregatedStats;
private destroy;
private createWebRtcVideo;
private createWebRtcAudio;
private playAudio;
static ɵfac: i0.ɵɵFactoryDeclaration<VideoService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<VideoService>;
}
declare class WebRtcPlayerService extends SubService {
onDataChannelMessage$: Subject<ArrayBuffer>;
private pcClient;
private signallingSrv;
private videoService;
private cfg;
private useMic;
private forceTURN;
private forceMonoAudio;
private dcClient;
constructor();
protected init(): void;
private createWebRtcOffer;
private createDataChannel;
private receiveAnswer;
private handleCandidateFromServer;
private mungeSDP;
/**
* Closes existing PeerConnection
*/
closePC(): void;
canSend(): boolean | null;
send(data: ArrayBuffer): void;
private setConfig;
private createPeerConnection;
private handlePeerConnectionEvents;
private setupTransceiversAsync;
private setupDataChannelCallbacks;
private onDataChannelConnected;
private createOffer;
static ɵfac: i0.ɵɵFactoryDeclaration<WebRtcPlayerService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<WebRtcPlayerService>;
}
declare class AfkPlaywrightService extends AFKService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<AfkPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AfkPlaywrightService>;
}
declare class SignallingPlaywrightService extends SignallingService {
constructor();
init(): void;
connectToSignaling(): rxjs.Observable<never>;
static ɵfac: i0.ɵɵFactoryDeclaration<SignallingPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<SignallingPlaywrightService>;
}
declare class UnrealCommunicatorPlaywrightService extends UnrealCommunicatorService {
init(): void;
sendCommandToUnreal(): void;
sendInputData(): void;
listenVideo(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<UnrealCommunicatorPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<UnrealCommunicatorPlaywrightService>;
}
declare class AggregatorPlaywrightService extends AggregatorService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<AggregatorPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AggregatorPlaywrightService>;
}
declare class FreezeFramePlaywrightService extends FreezeFrameService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<FreezeFramePlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FreezeFramePlaywrightService>;
}
declare class CommandTelemetryPlaywrightService extends CommandTelemetryService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<CommandTelemetryPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<CommandTelemetryPlaywrightService>;
}
declare class StreamStatusTelemetryPlaywrightService extends StreamStatusTelemetryService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<StreamStatusTelemetryPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<StreamStatusTelemetryPlaywrightService>;
}
declare class ConsoleExtensionsPlaywrightService extends ConsoleExtensionsService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleExtensionsPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<ConsoleExtensionsPlaywrightService>;
}
declare const ReceivedMimeTypes: {
readonly ApplicationJson: "application/json";
};
type ReceivedMimeType = (typeof ReceivedMimeTypes)[keyof typeof ReceivedMimeTypes];
declare class FileReceiverService extends SubService {
fileComplete$: Subject<ReceivedFile>;
private valid;
private chunks;
private mimetype;
private receiving;
private extension;
private timestampStart;
private data;
constructor();
protected init(): void;
reset(): void;
private resetOnStart;
/**
* Processes a file extension when received over data channel
* @param view - the file extension data
*/
setExtensionFromBytes(view: Uint8Array): void;
/**
* Processes a file mime type when received over data channel
* @param view - the file mime type data
*/
setMimeTypeFromBytes(view: Uint8Array): void;
setContentsFromBytes(view: Uint8Array): void;
}
interface ReceivedFile {
blob: Blob;
mimetype: ReceivedMimeType;
extension: string;
valid: boolean;
}
declare class FileReceiverPlaywrightService extends FileReceiverService {
init(): void;
}
declare class InputPlaywrightService extends InputService {
init(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<InputPlaywrightService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<InputPlaywrightService>;
}
declare class FileHandlerService {
private fileService;
private fileHandlers;
private handleJsonFile;
observeFileResponse<K extends keyof MetaBoxCommandList, T extends MetaBoxCommandList[K] = MetaBoxCommandList[K]>(mimetype: ReceivedMimeType, data: T, sender: (data: T) => void, timeOut?: number): Observable<{
commandCallback: ObservedCallbackResponse<T>;
} | never>;
static ɵfac: i0.ɵɵFactoryDeclaration<FileHandlerService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FileHandlerService>;
}
declare function AnswerHandler(this: SignallingService, msg: RTCSessionDescriptionInit): void;
declare function ConfigHandler(this: SignallingService, msg: ConfigMessage): void;
declare function IceCandidateHandler(this: SignallingService, msg: {
candidate: RTCIceCandidate;
}): void;
declare function InstanceReadyHandler(this: SignallingService): void;
declare function InstanceReservedHandler(this: SignallingService, msg: InstanceReserved): void;
declare function PingHandler(this: SignallingService): void;
declare function PlayerCountHandler(this: SignallingService, msg: PlayerCountMessage): void;
declare function SSInfoHandler(this: SignallingService, msg: SSInfo): null;
declare function OnCloseHandler(this: SignallingService, e: CloseEvent): void;
declare function OnErrorHandler(this: SignallingService, e: Event): void;
declare function OnMessageHandler(this: SignallingService, e: MessageEvent<string | Blob>): void;
declare function OnOpenHandler(this: SignallingService): void;
/**
* Keeps the max of numbers seen so far; resets to 0 whenever `reset$` emits.
* Emits `0` immediately on reset.
*/
declare const keepMaxUntilReset: (reset$: Observable<unknown>) => ((source: Observable<number>) => Observable<number>);
declare function observeCommandResponse<K extends keyof MetaBoxCommandList, T extends MetaBoxCommandList[K] = MetaBoxCommandList[K]>(data: T, sender: (data: T) => void, timeOut?: number, dispatchOnTimeout?: boolean): Observable<ObservedCallbackResponse<T> | never>;
interface ObservedCallbackResponse<T extends MetaBoxCommandPacket> {
json: {
commandCallback: T;
};
}
declare function decodeData(anyData: any): string;
declare const dispatchResize: () => void;
declare const forceResizeUnrealVideo: () => void;
declare function getRtcErrorMessage(code: number | null): string | undefined;
type Descriptor = MetaBoxCommandList & SignalDescriptor;
/**
* UnrealInternalSignalEvents commands list wrapper listener
* @param event
*/
declare function fromSignal<K extends keyof Descriptor>(event: K): Observable<Descriptor[K]>;
type ArgumentType<K extends keyof Descriptor> = Descriptor[K] extends void ? never : Descriptor[K];
/**
* UnrealInternalSignalEvents commands list wrapper sender
* @param event
* @param args
*/
declare function sendSignal<K extends keyof Descriptor>(event: K, ...args: ArgumentType<K> extends never ? [] : [ArgumentType<K>]): void;
declare function fromUnrealCallBackSignal<K extends keyof Descriptor>(command: K): Observable<Descriptor[K]>;
declare const floatToSmoothPercents: () => ((source: Observable<number>) => Observable<number>);
/**
* Creates an observable that emits values transitioning smoothly from the start value to the end value over a specified duration.
* The transition is performed using linear interpolation (lerp) and clamped to the range [0, 1].
*
* @param {number} start - The starting value of the transition.
* @param {number} end - The ending value of the transition.
* @param {number} duration - The duration of the transition in milliseconds.
* @returns {Observable<number>} An observable that emits the interpolated values from start to end over the specified duration.
*/
declare const smoothTransition: (start: number, end: number, duration: number) => Observable<number>;
declare const getImageFromVideoStream: (takeSizeFrom?: "video" | "constant", imageOutput?: ImageOutputValues, sizes?: StreamResolutionProps) => string | null;
declare function getActiveUrl(urls: string[]): Generator<string, void, unknown>;
declare class KalmanFilter1D {
private estimate;
private uncertainty;
private processNoise;
private measurementNoise;
/**
* @param initialEstimate Initial guess of the state.
* @param initialUncertainty Initial uncertainty in the guess.
* @param processNoise Process variance (Q) representing model uncertainty.
* @param measurementNoise Measurement variance (R) representing noise in measurements.
*/
constructor(initialEstimate: number, initialUncertainty: number, processNoise: number, measurementNoise: number);
/**
* Incorporates a new measurement and returns the updated estimate.
* @param measurement The new measurement value.
*/
update(measurement: number): number;
config(data: FilterSettings): void;
}
declare class LatencyTimings {
TestStartTimeMs: number | null;
UEReceiptTimeMs: number | null;
UEEncodeMs: number | null;
UECaptureToSendMs: number | null;
UETransmissionTimeMs: number | null;
BrowserReceiptTimeMs: number | null;
FrameDisplayDeltaTimeMs: number | null;
Reset(): void;
SetUETimings(UETimings: any): void;
SetFrameDisplayDeltaTime(DeltaTimeMs: number): void;
OnAllLatencyTimingsReady(_: any): void;
}
declare function mapQpToQuality(VideoEncoderQP: number): Quality;
declare const trackMixpanelEvent: _ngrx_store.ActionCreator<string, (props: {
event: string;
data?: unknown;
}) => {
event: string;
data?: unknown;
} & _ngrx_store.Action<string>>;
declare const changeLowBandwidth: _ngrx_store.ActionCreator<string, (props: {
lowBandwidth: boolean;
stats?: LBMStats;
}) => {
lowBandwidth: boolean;
stats?: LBMStats;
} & _ngrx_store.Action<string>>;
declare const setMaxFps: _ngrx_store.ActionCreator<string, (props: {
maxFps: number;
}) => {
maxFps: number;
} & _ngrx_store.Action<string>>;
declare const destroyRemoteConnections: _ngrx_store.ActionCreator<string, (props: {
reason: DisconnectReasonType;
}) => {
reason: DisconnectReasonType;
} & _ngrx_store.Action<string>>;
declare const destroyUnrealScene: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setCirrusConnected: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setCirrusDisconnected: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const changeStatusMainVideoOnScene: _ngrx_store.ActionCreator<string, (props: {
isVideoPlaying: boolean;
}) => {
isVideoPlaying: boolean;
} & _ngrx_store.Action<string>>;
declare const setAwsInstance: _ngrx_store.ActionCreator<string, (props: {
wsUrl: string | null;
pollingUrl: string | null;
instanceName: string | null;
}) => {
wsUrl: string | null;
pollingUrl: string | null;
instanceName: string | null;
} & _ngrx_store.Action<string>>;
declare const setStatusMessage: _ngrx_store.ActionCreator<string, (props: {
statusMessage: string | null;
}) => {
statusMessage: string | null;
} & _ngrx_store.Action<string>>;
declare const setStatusPercentSignallingServer: _ngrx_store.ActionCreator<string, (props: {
percent: number | null;
}) => {
percent: number | null;
} & _ngrx_store.Action<string>>;
declare const setFreezeFrame: _ngrx_store.ActionCreator<string, (props: {
dataUrl: string | null;
progress: number | null;
}) => {
dataUrl: string | null;
progress: number | null;
} & _ngrx_store.Action<string>>;
declare const setStreamClientCompanyId: _ngrx_store.ActionCreator<string, (props: {
id: string;
}) => {
id: string;
} & _ngrx_store.Action<string>>;
declare const setStreamViewId: _ngrx_store.ActionCreator<string, (props: {
id: string;
}) => {
id: string;
} & _ngrx_store.Action<string>>;
declare const setIntroImageSrc: _ngrx_store.ActionCreator<string, (props: {
src: string;
}) => {
src: string;
} & _ngrx_store.Action<string>>;
declare const setLoadingImageSrc: _ngrx_store.ActionCreator<string, (props: {
src: string;
}) => {
src: string;
} & _ngrx_store.Action<string>>;
declare const setIntroVideoSrc: _ngrx_store.ActionCreator<string, (props: {
src: string;
}) => {
src: string;
} & _ngrx_store.Action<string>>;
declare const resetIntroSrc: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setFreezeFrameFromVideo: _ngrx_store.ActionCreator<string, (props: {
dataUrl: string | null;
progress: number | null;
}) => {
dataUrl: string | null;
progress: number | null;
} & _ngrx_store.Action<string>>;
declare const setEstablishingConnection: _ngrx_store.ActionCreator<string, (props: {
value: boolean;
}) => {
value: boolean;
} & _ngrx_store.Action<string>>;
declare const setDataChannelConnected: _ngrx_store.ActionCreator<string, (props: {
statusMessage: string;
}) => {
statusMessage: string;
} & _ngrx_store.Action<string>>;
declare const setConfig: _ngrx_store.ActionCreator<string, (props: {
config: Partial<StreamConfig>;
}) => {
config: Partial<StreamConfig>;
} & _ngrx_store.Action<string>>;
declare const disconnectStream: _ngrx_store.ActionCreator<string, (props: {
reason: DisconnectReasonType;
message: string;
}) => {
reason: DisconnectReasonType;
message: string;
} & _ngrx_store.Action<string>>;
declare const dropConnection: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setViewportReady: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setViewportNotReady: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const changeStreamResolutionAction: _ngrx_store.ActionCreator<string, (props: {
width: number | null;
height: number | null;
}) => {
width: number | null;
height: number | null;
} & _ngrx_store.Action<string>>;
declare const changeStreamResolutionSuccessAction: _ngrx_store.ActionCreator<string, (props: {
width: number | null;
height: number | null;
}) => {
width: number | null;
height: number | null;
} & _ngrx_store.Action<string>>;
declare const setSignalingName: _ngrx_store.ActionCreator<string, (props: {
instanceName: string;
}) => {
instanceName: string;
} & _ngrx_store.Action<string>>;
declare const setOrchestrationParameters: _ngrx_store.ActionCreator<string, (props: {
instanceName: string;
streamRequestId: string;
eta: number;
}) => {
instanceName: string;
streamRequestId: string;
eta: number;
} & _ngrx_store.Action<string>>;
declare const setOrchestrationMessage: _ngrx_store.ActionCreator<string, (props: {
message: string;
}) => {
message: string;
} & _ngrx_store.Action<string>>;
declare const setOrchestrationProgress: _ngrx_store.ActionCreator<string, (props: {
progressComplete: number;
}) => {
progressComplete: number;
} & _ngrx_store.Action<string>>;
declare const setOrchestrationContext: _ngrx_store.ActionCreator<string, (props: {
urls: string[];
environmentId: string;
streamRequestContext: StreamRequestContext;
}) => {
urls: string[];
environmentId: string;
streamRequestContext: StreamRequestContext;
} & _ngrx_store.Action<string>>;
declare const updateCirrusInfo: _ngrx_store.ActionCreator<string, (props: {
ssInfo: string;
ssData: SignalingData;
}) => {
ssInfo: string;
ssData: SignalingData;
} & _ngrx_store.Action<string>>;
declare const commandStarted: _ngrx_store.ActionCreator<string, (props: {
id: string;
command: string;
}) => {
id: string;
command: string;
} & _ngrx_store.Action<string>>;
declare const commandCompleted: _ngrx_store.ActionCreator<string, (props: {
id: string;
}) => {
id: string;
} & _ngrx_store.Action<string>>;
declare const setLoopBackCommandIsCompleted: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setAfkTimerVisible: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setAfkTimerHide: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const showUnrealErrorMessage: _ngrx_store.ActionCreator<string, (props: {
code: number | null;
error?: string;
}) => {
code: number | null;
error?: string;
} & _ngrx_store.Action<string>>;
declare const initSignalling: _ngrx_store.FunctionWithParametersType<[data?: {
resetDisconnectionReason: boolean;
} | undefined], {
resetDisconnectionReason: boolean;
} & _ngrx_store.Action<string>> & _ngrx_store.Action<string>;
declare const startStream: _ngrx_store.ActionCreator<string, (props: {
config: Partial<StreamConfig>;
}) => {
config: Partial<StreamConfig>;
} & _ngrx_store.Action<string>>;
declare const resetConfig: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const resetAfkAction: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const resetWarnTimeout: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const abortEstablishingConnection: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare const setUnrealPlaywrightConfig: _ngrx_store.ActionCreator<string, () => _ngrx_store.Action<string>>;
declare class UnrealEffects {
private actions$;
private dialog;
private http;
private store;
private isDevMode;
private scrollStrategy;
private unrealInitialConfig;
private webRtcPlayerService;
private commandsSender;
private signallingService;
private videoService;
private dataChannelConnectionTimeout;
private connectionCompl