@3dsource/angular-unreal-module 0.0.162 → 0.0.163
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.
package/js/stream-prefetch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{"use strict";const g=/metabox-configurator\/(modular|basic)\//i,m="features",k="3dsource:region-ping",w="assets/config.json",f="https://datacenter.3dsource.com/signallingserver",y="42e01f";function l(){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{const
|
|
2
|
-
`);let i="maxaveragebitrate=510000;";return i+=this.forceMonoAudio?"stereo=0;":"stereo=1;",i+="useinbandfec=1",
|
|
3
|
-
`),s=[];for(const r of i)if(r.startsWith("a=fmtp:")&&r.toLowerCase().includes(`profile-level-id=${
|
|
4
|
-
`)}send(
|
|
1
|
+
"use strict";(()=>{"use strict";const g=/metabox-configurator\/(modular|basic)\//i,m="features",k="3dsource:region-ping",w="assets/config.json",f="https://datacenter.3dsource.com/signallingserver",y="42e01f";function l(){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{const e=Math.random()*16|0;return(o==="x"?e:e&3|8).toString(16)})}function b(o){return o.replace("http://","ws://").replace("https://","wss://")}const h="[stream-prefetch]",n=(...o)=>console.log(h,...o),c=(...o)=>console.warn(h,...o),u=(...o)=>console.error(h,...o);class T{constructor(){const e=document.currentScript||(()=>{const s=document.getElementsByTagName("script");return s[s.length-1]})(),t=e&&e.dataset||{};this.configUrl=t.configUrl||w,this.fallbackPollingUrl=t.fallbackUrl||"",this.skipRouteCheck=this.boolOr(t.skipRouteCheck,!1),this.config={ws_timeout:this.numberOr(t.wsTimeout,6e3),poll_timeout:this.numberOr(t.pollTimeout,4e3)};const i=new URLSearchParams(window.location.search);this.forceTURN=i.has("ForceTURN"),this.forceMaxBundle=i.has("ForceMaxBundle"),this.forceMonoAudio=i.has("forceMono"),this.ids={clientId:l(),viewId:l(),correlationId:l(),streamRequestId:""},this.region="",this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.settled=!1}run(){if(!(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))){if(this.isNewOrchestrationEnabled()){n("skip \u2014 features.newOrchestration is enabled");return}if(this.skipRouteCheck){c("skipRouteCheck=true \u2014 ignoring URL gate; using fallback polling"),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e));return}if(!g.test(window.location.href)){n("skip \u2014 not a metabox-configurator/{modular|basic} route");return}n("start",{ids:this.ids}),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}}connect(){return this.mark("prefetch_start"),this.region=this.readRegion()||"",n("region from cache:",this.region||"(none)"),(this.skipRouteCheck?Promise.resolve(null):this.fetchConfig()).then(t=>{let i;if(t){if(i=this.chooseEndpoint(t),i&&!this.isAllowedEndpoint(i.url)){n("skip \u2014 signalling URL not sanctioned for prefetch",i.url),this.fail("endpoint-not-allowed");return}}else if(this.fallbackPollingUrl)c("no app config \u2014 falling back to polling",this.fallbackPollingUrl),i={type:"polling",url:this.fallbackPollingUrl};else{this.fail("no-config");return}if(!i){this.fail("no-endpoint");return}n("endpoint chosen",{type:i.type,url:i.url});const s=this.adaptUrlToRegion(i.url);return this.pollOrchestration(s).then(r=>{if(!r){this.fail("no-ws-url");return}return this.parked.machineId=r,n("opening socket",r),this.openSocket(r)})})}fetchConfig(){const e=new URL(this.configUrl,window.location.origin).href;return this.fetchWithTimeout(e,{credentials:"omit"},this.config.poll_timeout).then(t=>t.ok?t.json().catch(()=>null):null).catch(()=>null)}chooseEndpoint(e){const t=e.signalling,i=this.firstVersionUrl(t&&t.versions);return i?{type:"polling",url:i}:null}isAllowedEndpoint(e){if(typeof e!="string")return!1;const t=e.replace(/\/+$/,"");return t===f||t.startsWith(f+"/")}firstVersionUrl(e){if(!e||typeof e!="object")return"";for(const t of Object.keys(e))if(e[t])return e[t];return""}pollOrchestration(e){const t=`${e}${this.ids.clientId}/${this.ids.viewId}`;return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(i=>i.ok?i.json().catch(()=>({})):null).then(i=>!i||!i.signallingServer?null:(this.ids.streamRequestId=i.streamRequestId||"",b(`${window.location.protocol}//${i.signallingServer}`))).catch(()=>null)}adaptUrlToRegion(e){return this.region&&e.indexOf(":region")===-1?e.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):e}openSocket(e){const t=this.ids.streamRequestId,i=t?e+(e.indexOf("?")===-1?"?":"&")+"streamRequestId="+encodeURIComponent(t):e,s=new WebSocket(i);s.binaryType="arraybuffer",this.ws=s,this.parked.ws=s,this.wsTimeoutId=setTimeout(()=>{s.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),s.onopen=()=>{clearTimeout(this.wsTimeoutId),this.mark("ws_open"),n("WS open \u2014 waiting for config",{region:this.region,streamRequestId:t||"(none)"})},s.onmessage=r=>this.onWsMessage(r),s.onerror=r=>{u("WS error",r),this.fail("ws-error")},s.onclose=r=>{!this.settled&&this.parked.state!=="ready"&&(c("WS closed before ready",{code:r&&r.code}),this.fail("ws-closed"))}}onWsMessage(e){let t;try{const i=typeof e.data=="string"?e.data:null;if(i===null)return;t=JSON.parse(i)}catch{return}switch(n("WS \u2190",t.type),t.type){case"ssInfo":this.parked.ssData=t.data;break;case"config":this.onConfig(t);break;case"offer":this.onOffer(t);break;case"answer":this.onAnswer(t);break;case"iceCandidate":this.onIceCandidate(t.candidate);break;case"ping":this.send({type:"pong",source:"front"});break;case"streamerDisconnected":case"playerDisconnected":this.fail("streamer-disconnected");break;case"error":this.fail("orchestration-error");break;default:break}}onConfig(e){if(this.pc)return;const t=Object.assign({},e.peerConnectionOptions);this.forceTURN&&(t.iceTransportPolicy="relay"),t.bundlePolicy=this.forceMaxBundle?"max-bundle":"balanced",t.iceCandidatePoolSize=1,this.parked.config=t;const i=new RTCPeerConnection(t);this.pc=i,this.parked.pc=i,n("PeerConnection created from config"),i.onicecandidate=s=>{s.candidate&&this.send({type:"iceCandidate",candidate:s.candidate})},i.ondatachannel=s=>this.wireDataChannel(s.channel),i.ontrack=s=>{s.track&&s.track.kind==="video"&&s.streams[0]&&(n("video track received"),this.stream=s.streams[0],this.parked.stream=this.stream,this.maybeReady())}}onOffer(e){return this.pc?(this.remoteDescriptionSet=!1,this.iceBuffer.length=0,this.pc.setRemoteDescription(e).then(()=>(this.remoteDescriptionSet=!0,this.flushIce(),this.pc.createAnswer())).then(t=>(t.sdp=this.mungeSDP(t.sdp||""),this.pc.setLocalDescription(t).then(()=>{n("WS \u2192 answer sent"),this.send(t)}))).catch(t=>this.fail("offer-failed",t))):Promise.resolve()}onAnswer(e){return this.pc?this.pc.setRemoteDescription(e).then(()=>{this.remoteDescriptionSet=!0,this.flushIce()}).catch(t=>this.fail("answer-failed",t)):Promise.resolve()}onIceCandidate(e){e&&(this.forceTURN&&String(e.candidate).indexOf("relay")<0||(this.remoteDescriptionSet&&this.pc?this.pc.addIceCandidate(e).catch(()=>{}):this.iceBuffer.push(e)))}flushIce(){for(const e of this.iceBuffer)this.pc.addIceCandidate(e).catch(()=>{});this.iceBuffer.length=0}wireDataChannel(e){e.binaryType="arraybuffer",this.dc=e,this.parked.dc=e,e.onmessage=t=>{this.parked.dcBuffer.push(t.data)},e.onopen=()=>{n("DataChannel open"),this.mark("dc_open"),this.maybeReady()},e.onclose=()=>{!this.settled&&this.parked.state!=="ready"&&(c("DataChannel closed before ready"),this.fail("dc-closed"))},e.readyState==="open"&&this.maybeReady()}maybeReady(){this.settled||!this.dc||this.dc.readyState!=="open"||this.stream&&(this.settled=!0,this.mark("ready"),this.parked.state="ready",n("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}publishParked(e){const t=this;this.parked={state:e,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,config:void 0,ids:this.ids,dcBuffer:[],timings:{},adopt(){return t.clearTimers(),this.state="adopted",this},onSettled(i){if(typeof i=="function"){if(this.state!=="connecting"){const s=this.state;Promise.resolve().then(()=>i(s));return}t.settledCb=i}}},window.__stream=this.parked}fireSettled(){const e=this.settledCb;if(e){this.settledCb=null;try{e(this.parked.state)}catch{}}}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null)}fail(e,t){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){u("FAILED:",e,t||""),this.settled=!0,this.clearTimers();try{this.dc&&this.dc.close()}catch{}try{this.pc&&this.pc.close()}catch{}try{this.ws&&this.ws.close()}catch{}this.parked&&(this.parked.state="failed",this.parked.failReason=e,t&&(this.parked.failError=String(t&&t.message||t)),this.fireSettled())}}mungeSDP(e){let t=this.prioritizeH264Profile(e,y);t=t.replace(/(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,`$1;x-google-start-bitrate=10000;x-google-max-bitrate=100000\r
|
|
2
|
+
`);let i="maxaveragebitrate=510000;";return i+=this.forceMonoAudio?"stereo=0;":"stereo=1;",i+="useinbandfec=1",t=t.replace("useinbandfec=1",i),t}prioritizeH264Profile(e,t){const i=e.split(`\r
|
|
3
|
+
`),s=[];for(const r of i)if(r.startsWith("a=fmtp:")&&r.toLowerCase().includes(`profile-level-id=${t}`)){const a=r.split(":")[1]&&r.split(":")[1].split(" ")[0];a&&s.push(a)}return s.length===0?e:i.map(r=>{if(!r.startsWith("m=video"))return r;const a=r.split(" "),x=a.slice(0,3),p=a.slice(3),S=[...s.filter(d=>p.includes(d)),...p.filter(d=>!s.includes(d))];return[...x,...S].join(" ")}).join(`\r
|
|
4
|
+
`)}send(e){const t=typeof e=="string"?e:JSON.stringify(e);if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(t)}catch{}}mark(e){this.parked&&(this.parked.timings[e]=performance.now())}isNewOrchestrationEnabled(){try{const e=localStorage.getItem(m);if(!e)return!1;const t=JSON.parse(e);return!!(t&&t.newOrchestration===!0)}catch{return!1}}readRegion(){try{const e=localStorage.getItem(k);if(!e)return null;const t=JSON.parse(e);return!t||typeof t.region_code!="string"||typeof t.ts!="number"||Date.now()-t.ts>=2592e6?null:t.region_code}catch{return null}}fetchWithTimeout(e,t,i){const s=new AbortController;t.signal=s.signal;const r=setTimeout(()=>s.abort(),i);return fetch(e,t).finally(()=>clearTimeout(r))}numberOr(e,t){const i=Number(e);return e!=null&&e!==""&&!Number.isNaN(i)?i:t}boolOr(e,t){return e==="true"||e===""?!0:e==="false"?!1:t}}new T().run()})();
|
package/package.json
CHANGED
|
@@ -338,11 +338,17 @@ interface ParkedStream {
|
|
|
338
338
|
machineId?: string;
|
|
339
339
|
/** `peerConnectionOptions` the machine sent in its `config` message */
|
|
340
340
|
config?: RTCConfiguration;
|
|
341
|
-
/**
|
|
341
|
+
/**
|
|
342
|
+
* ids the script generated; Angular reuses them (no regeneration).
|
|
343
|
+
* `streamRequestId` is the exception — it is NOT generated but read from the
|
|
344
|
+
* polling response and passed to Cirrus on the upgrade URL; empty when the
|
|
345
|
+
* backend didn't issue one.
|
|
346
|
+
*/
|
|
342
347
|
ids: {
|
|
343
348
|
clientId: string;
|
|
344
349
|
viewId: string;
|
|
345
350
|
correlationId: string;
|
|
351
|
+
streamRequestId?: string;
|
|
346
352
|
};
|
|
347
353
|
/** DataChannel messages received before adoption, replayed on adopt */
|
|
348
354
|
dcBuffer: ArrayBuffer[];
|
|
@@ -554,6 +560,12 @@ type SignalingMessageHandler<T extends keyof SignalingMessageMap> = (msg: Signal
|
|
|
554
560
|
interface PollingOrchestrationMessage {
|
|
555
561
|
mm_message?: string;
|
|
556
562
|
signallingServer?: string;
|
|
563
|
+
/**
|
|
564
|
+
* Connection id issued by the orchestration for this stream request. Kept
|
|
565
|
+
* internally as `connectionId` and handed to Cirrus on the WS upgrade URL.
|
|
566
|
+
* Absent on older backends — Cirrus then generates one itself.
|
|
567
|
+
*/
|
|
568
|
+
streamRequestId?: string;
|
|
557
569
|
error?: string;
|
|
558
570
|
info?: {
|
|
559
571
|
age: number | null;
|
|
@@ -1410,8 +1422,15 @@ declare class SignallingService extends SubService {
|
|
|
1410
1422
|
*
|
|
1411
1423
|
* "Just connect" — nothing is sent on open (the machine is already reserved
|
|
1412
1424
|
* for this session): Cirrus pushes `config` and Unreal sends the `offer`.
|
|
1425
|
+
*
|
|
1426
|
+
* The orchestration-issued `streamRequestId` (from the same `instanceReady`
|
|
1427
|
+
* payload) rides on this upgrade URL: in the new flow THIS is the Cirrus
|
|
1428
|
+
* socket, so it is the one that must carry the id (the first socket is
|
|
1429
|
+
* orchestration-only control and needs nothing). Without it the stream
|
|
1430
|
+
* machine's Cirrus falls back to its own `guid()` and the session can no
|
|
1431
|
+
* longer be correlated. Absent id → URL unchanged.
|
|
1413
1432
|
*/
|
|
1414
|
-
connectToStreamMachine(originInstanceUri: string): void;
|
|
1433
|
+
connectToStreamMachine(originInstanceUri: string, streamRequestId?: string | null): void;
|
|
1415
1434
|
/**
|
|
1416
1435
|
* The stream-machine (Cirrus) connection failed. Re-request a machine over
|
|
1417
1436
|
* the still-open orchestration socket: orchestration issues a new
|
|
@@ -2568,10 +2587,24 @@ declare const setAwsInstance: _ngrx_store.ActionCreator<string, (props: {
|
|
|
2568
2587
|
wsUrl: string | null;
|
|
2569
2588
|
pollingUrl: string | null;
|
|
2570
2589
|
instanceName: string | null;
|
|
2590
|
+
/**
|
|
2591
|
+
* Connection id issued by the orchestration in the polling response. Kept
|
|
2592
|
+
* internally as `connectionId` (see `selectCirrusConnectionId`). Undefined
|
|
2593
|
+
* in the new-orchestration flow, where it arrives later via
|
|
2594
|
+
* `instanceReserved` → {@link setOrchestrationParameters}.
|
|
2595
|
+
*/
|
|
2596
|
+
streamRequestId?: string;
|
|
2571
2597
|
}) => {
|
|
2572
2598
|
wsUrl: string | null;
|
|
2573
2599
|
pollingUrl: string | null;
|
|
2574
2600
|
instanceName: string | null;
|
|
2601
|
+
/**
|
|
2602
|
+
* Connection id issued by the orchestration in the polling response. Kept
|
|
2603
|
+
* internally as `connectionId` (see `selectCirrusConnectionId`). Undefined
|
|
2604
|
+
* in the new-orchestration flow, where it arrives later via
|
|
2605
|
+
* `instanceReserved` → {@link setOrchestrationParameters}.
|
|
2606
|
+
*/
|
|
2607
|
+
streamRequestId?: string;
|
|
2575
2608
|
} & _ngrx_store.Action<string>>;
|
|
2576
2609
|
declare const setStatusMessage: _ngrx_store.ActionCreator<string, (props: {
|
|
2577
2610
|
statusMessage: string | null;
|
|
@@ -2811,6 +2844,12 @@ declare const initSignalling: _ngrx_store.ActionCreator<string, () => _ngrx_stor
|
|
|
2811
2844
|
declare const streamAdopted: _ngrx_store.ActionCreator<string, (props: {
|
|
2812
2845
|
machineId: string | null;
|
|
2813
2846
|
ssData: SignalingData | null;
|
|
2847
|
+
/**
|
|
2848
|
+
* Orchestration-issued connection id the prefetch script read from the
|
|
2849
|
+
* polling response and already passed to Cirrus. Kept internally as
|
|
2850
|
+
* `connectionId` (see `selectCirrusConnectionId`).
|
|
2851
|
+
*/
|
|
2852
|
+
streamRequestId: string | null;
|
|
2814
2853
|
/**
|
|
2815
2854
|
* `performance.now()` marks the prefetch script recorded while it
|
|
2816
2855
|
* established the connection (`prefetch_start` / `ws_open` / `dc_open` /
|
|
@@ -2824,6 +2863,12 @@ declare const streamAdopted: _ngrx_store.ActionCreator<string, (props: {
|
|
|
2824
2863
|
}) => {
|
|
2825
2864
|
machineId: string | null;
|
|
2826
2865
|
ssData: SignalingData | null;
|
|
2866
|
+
/**
|
|
2867
|
+
* Orchestration-issued connection id the prefetch script read from the
|
|
2868
|
+
* polling response and already passed to Cirrus. Kept internally as
|
|
2869
|
+
* `connectionId` (see `selectCirrusConnectionId`).
|
|
2870
|
+
*/
|
|
2871
|
+
streamRequestId: string | null;
|
|
2827
2872
|
/**
|
|
2828
2873
|
* `performance.now()` marks the prefetch script recorded while it
|
|
2829
2874
|
* established the connection (`prefetch_start` / `ws_open` / `dc_open` /
|
|
@@ -3220,8 +3265,19 @@ declare const selectIsLoaderScreenVisible: _ngrx_store.MemoizedSelector<Record<s
|
|
|
3220
3265
|
* producing a small visual step that indicates the WS handshake succeeded.
|
|
3221
3266
|
*/
|
|
3222
3267
|
declare const selectTotalProgress: _ngrx_store.MemoizedSelector<Record<string, any>, number, (s1: number | null, s2: number, s3: _3dsource_angular_unreal_module.AwsInstance, s4: boolean) => number>;
|
|
3223
|
-
/**
|
|
3224
|
-
|
|
3268
|
+
/**
|
|
3269
|
+
* The connection id of the current stream session.
|
|
3270
|
+
*
|
|
3271
|
+
* Primary source is the orchestration, which issues it as `streamRequestId` —
|
|
3272
|
+
* in the polling response (legacy flow) or in `instanceReserved`/`instanceReady`
|
|
3273
|
+
* (new orchestration) — and the browser hands it to Cirrus on the WS upgrade
|
|
3274
|
+
* URL. `ssData.connectionId` (the `ssInfo` message) is the fallback for
|
|
3275
|
+
* backends that don't issue one yet: Cirrus then generates it itself and
|
|
3276
|
+
* reports it back here.
|
|
3277
|
+
*
|
|
3278
|
+
* Used by external consumers (e.g., metabox). Do not remove.
|
|
3279
|
+
*/
|
|
3280
|
+
declare const selectCirrusConnectionId: _ngrx_store.MemoizedSelector<Record<string, any>, string | undefined, (s1: _3dsource_angular_unreal_module.AwsInstance, s2: _3dsource_angular_unreal_module.SignalingData | null) => string | undefined>;
|
|
3225
3281
|
/** Used by external consumers (e.g., metabox). Do not remove. */
|
|
3226
3282
|
declare const selectUserInactivityDetected: _ngrx_store.MemoizedSelector<Record<string, any>, {
|
|
3227
3283
|
remainingSecondsToDisconnect: number;
|