@3dsource/angular-unreal-module 0.0.197 → 0.0.200-dev.0
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 p=/metabox-configurator\/modular\/([^/?#]+)/i,m="features",g="3dsource:region-ping:v2",I="__3dsourceRegionPing",w="assets/config.json",y="assets/features.json",T="metaboxModularConfigurator",k=new Set(["insecure-endpoint","no-endpoint","no-config"]),R="42e01f";function S(a){return a.replace("http://","ws://").replace("https://","wss://")}const l="[stream-prefetch]",r=(...a)=>console.log(l,...a),c=(...a)=>console.warn(l,...a),d=(...a)=>console.error(l,...a);class O{constructor(){this.readOptions(),this.ids={clientId:"",viewId:"",correlationId:"",streamRequestId:""},this.configuratorId="",this.region="",this.newOrchestration=!1,this.environmentId="",this.controlWs=null,this.handedOverControlWs=null,this.orchestrationRetryId=null,this.instanceReadyTimeoutId=null,this.streamReadyTimeoutId=null,this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.settled=!1,this.settledCbs=[]}readOptions(){const e=document.currentScript||(()=>{const n=document.getElementsByTagName("script");return n[n.length-1]})(),t=e&&e.dataset||{};this.configUrl=t.configUrl||w,this.featuresUrl=t.featuresUrl||y,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")}run(){if(!(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))){if(window.__stream){r("skip \u2014 this page already has a stream owner",{owner:window.__stream.owner||"prefetch"});return}if(this.configuratorId=this.resolveConfiguratorId(),!this.configuratorId){r("skip \u2014 not a metabox-configurator/modular route");return}this.ids.correlationId=this.configuratorId,r("start",{configuratorId:this.configuratorId}),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}}connect(){return this.mark("prefetch_start"),Promise.all([this.fetchConfig(),this.fetchFeatures()]).then(([e,t])=>this.withConfig(e,t))}withConfig(e,t){if(this.newOrchestration=this.isNewOrchestrationEnabled(t),r("orchestration mode",{newOrchestration:this.newOrchestration}),!e){this.fail("no-config");return}return this.fetchConfigurator(e).then(i=>this.withConfigurator(e,i))}withConfigurator(e,t){if(!t){this.fail("no-configurator");return}if(this.deriveIdentity(t),!this.ids.clientId||!this.ids.viewId){this.fail("no-identity");return}r("identity from configurator cache",{clientId:this.ids.clientId,viewId:this.ids.viewId});const i=this.chooseEndpoint(e,t);if(!i||!i.url){this.fail("no-endpoint");return}if(!this.isAllowedEndpoint(i.url)){r("skip \u2014 endpoint is not encrypted",i.url),this.fail("insecure-endpoint");return}return r("endpoint chosen",i),i.type==="orchestration"?this.runOrchestration(i.url):this.runLegacy(i.url)}resolveConfiguratorId(){const e=p.exec(window.location.href);if(!e)return"";try{return decodeURIComponent(e[1])}catch{return e[1]}}fetchConfig(){return this.fetchJson(this.configUrl)}fetchFeatures(){return this.fetchJson(this.featuresUrl)}fetchJson(e){const t=new URL(e,window.location.origin).href;return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(i=>i.ok?i.json().catch(()=>null):null).catch(()=>null)}isNewOrchestrationEnabled(e){const t=e&&typeof e=="object"?e:{},i=this.readFeatureOverrides();return("newOrchestration"in i?i.newOrchestration:t.newOrchestration)===!0}readFeatureOverrides(){try{const e=localStorage.getItem(m);if(!e)return{};const t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}fetchConfigurator(e){const t=e&&e.cdn;if(!t)return c("app config has no `cdn` \u2014 cannot read the configurator identity"),Promise.resolve(null);const i=`${String(t).replace(/\/+$/,"")}/cache/configurator/${encodeURIComponent(this.configuratorId)}.json`;return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(n=>n.ok?n.json().catch(()=>null):null).then(n=>{const s=n&&n.data&&n.data.modularConfigurator;return s||(c("configurator cache response has no modularConfigurator",i),null)}).catch(()=>null)}deriveIdentity(e){const t=e.client&&e.client.id;this.ids.clientId=t?`${t}_configurator`:"",this.ids.viewId=e.id||""}chooseEndpoint(e,t){const i=this.newOrchestration?e.newSignalling:e.signalling;if(!i)return null;this.environmentId=i.environmentId||"";const n=this.versionKey(t),s=i.versions,o=n&&s&&s[n]||"";return o||c("no endpoint for Unreal version key",n||"(none)"),{type:this.newOrchestration?"orchestration":"polling",url:o}}versionKey(e){const i=(Array.isArray(e.components)?e.components.slice().sort((o,h)=>(o&&o.position)-(h&&h.position)):[])[0],n=i&&i.product&&i.product.model?i.product.model.unrealVersion:"";if(!n)return"";const s=String(n).split(".");return s.length<2?"":[s[0],s[1],"*"].join(".")}isAllowedEndpoint(e){if(typeof e!="string")return!1;let t;try{t=new URL(e,window.location.href)}catch{return!1}return t.protocol==="https:"||t.protocol==="wss:"?!0:t.protocol!=="http:"&&t.protocol!=="ws:"?!1:t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]"}resolveRegion(){const e=this.readRegion()||this.pickFromRegionPing()||{region:"",pingsMs:[],source:"none"};this.region=e.region,this.parked.region=e,r("region:",e.region||"(none, geo-IP)",e.source)}pickFromRegionPing(){try{const e=window[I];return e&&e.version===1?e.pick():null}catch{return null}}readRegion(){try{const e=localStorage.getItem(g);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:{region:t.region_code,pingsMs:Array.isArray(t.result)?t.result.filter(i=>typeof i=="number"):[],source:"cache",probes:t.probes||void 0,cacheAgeMs:Date.now()-t.ts}}catch{return null}}adaptUrlToRegion(e){return this.region&&e.indexOf(":region")===-1?e.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):e}runLegacy(e){this.resolveRegion();const t=this.adaptUrlToRegion(e);return this.pollOrchestration(t).then(i=>{if(!i){this.fail("no-ws-url");return}return this.parked.machineId=i,this.parked.pollingUrl=t,this.parked.kind="polling",r("opening socket",i),this.openSocket(i)})}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||"",this.parked.instanceName=i.signallingServer,S(`${window.location.protocol}//${i.signallingServer}`))).catch(()=>null)}runOrchestration(e){if(!this.environmentId)return this.fail("no-environment-id"),Promise.resolve();this.resolveRegion();const t=this.adaptUrlToRegion(e);this.parked.pollingUrl=t;const i=new WebSocket(t);return this.controlWs=i,this.wsTimeoutId=setTimeout(()=>{i.readyState!==WebSocket.OPEN&&this.fail("control-ws-timeout")},this.config.ws_timeout),i.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.mark("control_ws_open"),r("control socket open \u2014 requesting stream"),this.instanceReadyTimeoutId=setTimeout(()=>this.fail("instance-ready-timeout"),18e4),this.sendRequestStream()},i.onmessage=n=>this.guard("control-message-failed",()=>this.onControlMessage(n)),i.onerror=n=>{d("control WS error",n),this.fail("control-ws-error")},i.onclose=()=>{!this.settled&&!this.ws&&(c("control WS closed before instanceReady"),this.fail("control-ws-closed"))},Promise.resolve()}sendRequestStream(){this.sendControl({type:"requestStream",correlationId:this.ids.correlationId,payload:{...this.region&&{region:this.region},environmentId:this.environmentId,context:{id:T,data:{configuratorId:this.configuratorId}}}})}sendControl(e){const t=this.controlWs;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch{}}onControlMessage(e){var i;let t;try{const n=typeof e.data=="string"?e.data:null;if(n===null)return;t=JSON.parse(n)}catch{return}switch(r("control \u2190",t.type),t.type){case"instanceReserved":this.parked.eta=(i=t.payload&&t.payload.eta)!=null?i:null;break;case"instanceReady":this.onInstanceReady(t.payload||{});break;case"error":this.onOrchestrationError(t.payload||{});break;default:break}}onInstanceReady(e){if(this.ws)return;const t=e.originInstanceUri;if(!t){this.fail("no-instance-uri");return}clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,this.ids.streamRequestId=e.streamRequestId||"",this.parked.machineId=t,this.parked.kind="socket",this.parked.instanceName=e.originInstanceId||null,this.mark("instance_ready"),r("instance ready \u2014 opening Cirrus socket",t),this.handOverControlWs(),this.openSocket(t)}onOrchestrationError(e){const t=e.code;if(t===4001||t===4002){c("orchestration refused the request, retrying",t),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=setTimeout(()=>this.sendRequestStream(),this.config.poll_timeout);return}t!==4003&&this.fail("orchestration-error",e.message||String(t))}handOverControlWs(){const e=this.controlWs;e&&(this.controlWs=null,this.handedOverControlWs=e,this.parked.controlWs=e,e.onmessage=null,e.onerror=null,e.onclose=null,r("control socket handed over \u2014 left open for orchestration"))}closeControlWs(){const e=[this.controlWs,this.handedOverControlWs];this.controlWs=null,this.handedOverControlWs=null;for(const t of e)if(t){t.onopen=null,t.onmessage=null,t.onerror=null,t.onclose=null;try{t.close()}catch{}}}openSocket(e){const t=this.ids.streamRequestId,i=t?e+(e.indexOf("?")===-1?"?":"&")+"streamRequestId="+encodeURIComponent(t):e,n=new WebSocket(i);n.binaryType="arraybuffer",this.ws=n,this.parked.ws=n,this.wsTimeoutId=setTimeout(()=>{n.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),n.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.streamReadyTimeoutId=setTimeout(()=>this.fail("stream-ready-timeout"),6e4),this.mark("ws_open"),r("WS open \u2014 waiting for config",{region:this.region,streamRequestId:t||"(none)"})},n.onmessage=s=>this.guard("message-failed",()=>this.onWsMessage(s)),n.onerror=s=>{d("WS error",s),this.fail("ws-error")},n.onclose=s=>{!this.settled&&this.parked.state!=="ready"&&(c("WS closed before ready",{code:s&&s.code}),this.fail("ws-closed"))}}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{}}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(r("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;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,r("PeerConnection created from config"),i.onicecandidate=n=>{n.candidate&&this.send({type:"iceCandidate",candidate:n.candidate})},i.ondatachannel=n=>this.wireDataChannel(n.channel),i.ontrack=n=>{n.track&&n.track.kind==="video"&&n.streams[0]&&(r("video track received"),this.stream=n.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(()=>{r("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=()=>{r("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,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.mark("ready"),this.parked.state="ready",r("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}mungeSDP(e){let t=this.prioritizeH264Profile(e,R);t=t.replace(/(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,`$1;x-google-start-bitrate=10000;x-google-max-bitrate=100000\r
|
|
1
|
+
"use strict";(()=>{"use strict";const g=/metabox-configurator\/modular\/([^/?#]+)/i,I="features",T="3dsource:region-ping:v2",w="__3dsourceRegionPing",y="assets/config.json",k="assets/features.json",R="metaboxModularConfigurator",S=new Set(["insecure-endpoint","no-endpoint","no-config"]),_="42e01f";function O(s){return s.replace("http://","ws://").replace("https://","wss://")}function f(s){try{const e=new URL(s,window.location.href);return`${e.host}${e.pathname}`}catch{return""}}function E(s){const e=[s.signallingServer?"ready":"waiting"],t=s.info&&s.info.percent;typeof t=="number"&&e.push(`${t}%`);const i=s.mm_message||s.error;return i&&e.push(String(i)),e.join(" ")}function p(s){const e=s&&s.code,t=s&&s.reason;return[e,t].filter(i=>i!==void 0&&i!=="").join(" ")}function C(s){return s&&s.message||String(s)}const l="[stream-prefetch]",o=(...s)=>console.log(l,...s),c=(...s)=>console.warn(l,...s),d=(...s)=>console.error(l,...s);class v{constructor(){this.readOptions(),this.ids={clientId:"",viewId:"",correlationId:"",streamRequestId:""},this.configuratorId="",this.region="",this.newOrchestration=!1,this.environmentId="",this.controlWs=null,this.handedOverControlWs=null,this.orchestrationRetryId=null,this.instanceReadyTimeoutId=null,this.streamReadyTimeoutId=null,this.pc=null,this.dc=null,this.stream=null,this.ws=null,this.remoteDescriptionSet=!1,this.iceBuffer=[],this.wsTimeoutId=null,this.linkTimeoutId=null,this.dcTimeoutId=null,this.iceCandidatesIn=0,this.settled=!1,this.settledCbs=[]}readOptions(){const e=document.currentScript||(()=>{const n=document.getElementsByTagName("script");return n[n.length-1]})(),t=e&&e.dataset||{};this.configUrl=t.configUrl||y,this.featuresUrl=t.featuresUrl||k,this.config={ws_timeout:this.numberOr(t.wsTimeout,6e3),poll_timeout:this.numberOr(t.pollTimeout,4e3),dc_timeout:this.numberOr(t.dcTimeout,8e3)};const i=new URLSearchParams(window.location.search);this.forceTURN=i.has("ForceTURN"),this.forceMaxBundle=i.has("ForceMaxBundle"),this.forceMonoAudio=i.has("forceMono")}run(){if(!(typeof window=="undefined"||!("WebSocket"in window)||!("RTCPeerConnection"in window))){if(window.__stream){o("skip \u2014 this page already has a stream owner",{owner:window.__stream.owner||"prefetch"});return}if(this.configuratorId=this.resolveConfiguratorId(),!this.configuratorId){o("skip \u2014 not a metabox-configurator/modular route");return}this.ids.correlationId=this.configuratorId,o("start",{configuratorId:this.configuratorId}),this.publishParked("connecting"),this.connect().catch(e=>this.fail("exception",e))}}connect(){return this.mark("prefetch_start"),Promise.all([this.fetchConfig(),this.fetchFeatures()]).then(([e,t])=>this.withConfig(e,t))}withConfig(e,t){if(this.newOrchestration=this.isNewOrchestrationEnabled(t),o("orchestration mode",{newOrchestration:this.newOrchestration}),!e){this.fail("no-config");return}return this.fetchConfigurator(e).then(i=>this.withConfigurator(e,i))}withConfigurator(e,t){if(!t){this.fail("no-configurator");return}if(this.deriveIdentity(t),!this.ids.clientId||!this.ids.viewId){this.fail("no-identity");return}o("identity from configurator cache",{clientId:this.ids.clientId,viewId:this.ids.viewId});const i=this.chooseEndpoint(e,t);if(!i||!i.url){this.fail("no-endpoint");return}if(!this.isAllowedEndpoint(i.url)){o("skip \u2014 endpoint is not encrypted",i.url),this.fail("insecure-endpoint");return}return o("endpoint chosen",i),this.note("endpoint",`${i.type} ${f(i.url)}`),i.type==="orchestration"?this.runOrchestration(i.url):this.runLegacy(i.url)}resolveConfiguratorId(){const e=g.exec(window.location.href);if(!e)return"";try{return decodeURIComponent(e[1])}catch{return e[1]}}fetchConfig(){return this.fetchJson(this.configUrl,"config")}fetchFeatures(){return this.fetchJson(this.featuresUrl,"features")}fetchJson(e,t){const i=new URL(e,window.location.origin).href,n=performance.now();return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(r=>(this.noteHttp(t,r,n),r.ok?r.json().catch(()=>null):null)).catch(r=>(this.noteHttpError(t,r,n),null))}isNewOrchestrationEnabled(e){const t=e&&typeof e=="object"?e:{},i=this.readFeatureOverrides();return("newOrchestration"in i?i.newOrchestration:t.newOrchestration)===!0}readFeatureOverrides(){try{const e=localStorage.getItem(I);if(!e)return{};const t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}fetchConfigurator(e){const t=e&&e.cdn;if(!t)return c("app config has no `cdn` \u2014 cannot read the configurator identity"),Promise.resolve(null);const i=`${String(t).replace(/\/+$/,"")}/cache/configurator/${encodeURIComponent(this.configuratorId)}.json`,n=performance.now();return this.fetchWithTimeout(i,{credentials:"omit"},this.config.poll_timeout).then(r=>(this.noteHttp("configurator",r,n),r.ok?r.json().catch(()=>null):null)).then(r=>{const a=r&&r.data&&r.data.modularConfigurator;return a||(c("configurator cache response has no modularConfigurator",i),null)}).catch(r=>(this.noteHttpError("configurator",r,n),null))}deriveIdentity(e){const t=e.client&&e.client.id;this.ids.clientId=t?`${t}_configurator`:"",this.ids.viewId=e.id||""}chooseEndpoint(e,t){const i=this.newOrchestration?e.newSignalling:e.signalling;if(!i)return null;this.environmentId=i.environmentId||"";const n=this.versionKey(t),r=i.versions,a=n&&r&&r[n]||"";return a||c("no endpoint for Unreal version key",n||"(none)"),{type:this.newOrchestration?"orchestration":"polling",url:a}}versionKey(e){const i=(Array.isArray(e.components)?e.components.slice().sort((a,h)=>(a&&a.position)-(h&&h.position)):[])[0],n=i&&i.product&&i.product.model?i.product.model.unrealVersion:"";if(!n)return"";const r=String(n).split(".");return r.length<2?"":[r[0],r[1],"*"].join(".")}isAllowedEndpoint(e){if(typeof e!="string")return!1;let t;try{t=new URL(e,window.location.href)}catch{return!1}return t.protocol==="https:"||t.protocol==="wss:"?!0:t.protocol!=="http:"&&t.protocol!=="ws:"?!1:t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]"}resolveRegion(){const e=this.readRegion()||this.pickFromRegionPing()||{region:"",pingsMs:[],source:"none"};this.region=e.region,this.parked.region=e,o("region:",e.region||"(none, geo-IP)",e.source),this.note("region",`${e.region||"none"} ${e.source}`)}pickFromRegionPing(){try{const e=window[w];return e&&e.version===1?e.pick():null}catch{return null}}readRegion(){try{const e=localStorage.getItem(T);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:{region:t.region_code,pingsMs:Array.isArray(t.result)?t.result.filter(i=>typeof i=="number"):[],source:"cache",probes:t.probes||void 0,cacheAgeMs:Date.now()-t.ts}}catch{return null}}adaptUrlToRegion(e){return this.region&&e.indexOf(":region")===-1?e.replace(/signallingserver/gi,`signallingserver/region:${this.region}`):e}runLegacy(e){this.resolveRegion();const t=this.adaptUrlToRegion(e);return this.pollOrchestration(t).then(i=>{if(!i){this.fail("no-ws-url");return}return this.parked.machineId=i,this.parked.pollingUrl=t,this.parked.kind="polling",o("opening socket",i),this.openSocket(i)})}pollOrchestration(e){const t=`${e}${this.ids.clientId}/${this.ids.viewId}`,i=performance.now();return this.fetchWithTimeout(t,{credentials:"omit"},this.config.poll_timeout).then(n=>(this.noteHttp("poll",n,i),n.ok?n.json().catch(()=>({})):null)).then(n=>(n&&this.note("poll-answer",E(n)),!n||!n.signallingServer?null:(this.ids.streamRequestId=n.streamRequestId||"",this.parked.instanceName=n.signallingServer,this.note("instance",n.signallingServer),O(`${window.location.protocol}//${n.signallingServer}`)))).catch(n=>(this.noteHttpError("poll",n,i),null))}runOrchestration(e){if(!this.environmentId)return this.fail("no-environment-id"),Promise.resolve();this.resolveRegion();const t=this.adaptUrlToRegion(e);this.parked.pollingUrl=t;const i=new WebSocket(t);return this.controlWs=i,this.wsTimeoutId=setTimeout(()=>{i.readyState!==WebSocket.OPEN&&this.fail("control-ws-timeout")},this.config.ws_timeout),i.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.mark("control_ws_open"),o("control socket open \u2014 requesting stream"),this.instanceReadyTimeoutId=setTimeout(()=>this.fail("instance-ready-timeout"),18e4),this.sendRequestStream()},i.onmessage=n=>this.guard("control-message-failed",()=>this.onControlMessage(n)),i.onerror=n=>{d("control WS error",n),this.note("control-ws-error"),this.fail("control-ws-error")},i.onclose=n=>{this.note("control-ws-close",p(n)),!this.settled&&!this.ws&&(c("control WS closed before instanceReady"),this.fail("control-ws-closed"))},Promise.resolve()}sendRequestStream(){this.sendControl({type:"requestStream",correlationId:this.ids.correlationId,payload:{...this.region&&{region:this.region},environmentId:this.environmentId,context:{id:R,data:{configuratorId:this.configuratorId}}}})}sendControl(e){const t=this.controlWs;if(t&&t.readyState===WebSocket.OPEN)try{t.send(JSON.stringify(e))}catch{}}onControlMessage(e){var i;let t;try{const n=typeof e.data=="string"?e.data:null;if(n===null)return;t=JSON.parse(n)}catch{return}switch(o("control \u2190",t.type),t.type!=="instanceReady"&&t.type!=="error"&&this.note(`control:${t.type}`),t.type){case"instanceReserved":this.parked.eta=(i=t.payload&&t.payload.eta)!=null?i:null,this.note("instance-reserved",`eta=${this.parked.eta}`);break;case"instanceReady":this.onInstanceReady(t.payload||{});break;case"error":this.onOrchestrationError(t.payload||{});break;default:break}}onInstanceReady(e){if(this.ws)return;const t=e.originInstanceUri;if(!t){this.fail("no-instance-uri");return}clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,this.ids.streamRequestId=e.streamRequestId||"",this.parked.machineId=t,this.parked.kind="socket",this.parked.instanceName=e.originInstanceId||null,this.note("instance",`${this.parked.instanceName||""} ${f(t)}`),this.mark("instance_ready"),o("instance ready \u2014 opening Cirrus socket",t),this.handOverControlWs(),this.openSocket(t)}onOrchestrationError(e){const t=e.code;if(this.note("orchestration-error",`${t} ${e.message||""}`),t===4001||t===4002){c("orchestration refused the request, retrying",t),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=setTimeout(()=>this.sendRequestStream(),this.config.poll_timeout);return}t!==4003&&this.fail("orchestration-error",e.message||String(t))}handOverControlWs(){const e=this.controlWs;e&&(this.controlWs=null,this.handedOverControlWs=e,this.parked.controlWs=e,e.onmessage=null,e.onerror=null,e.onclose=null,o("control socket handed over \u2014 left open for orchestration"))}closeControlWs(){const e=[this.controlWs,this.handedOverControlWs];this.controlWs=null,this.handedOverControlWs=null;for(const t of e)if(t){t.onopen=null,t.onmessage=null,t.onerror=null,t.onclose=null;try{t.close()}catch{}}}openSocket(e){const t=this.ids.streamRequestId,i=t?e+(e.indexOf("?")===-1?"?":"&")+"streamRequestId="+encodeURIComponent(t):e,n=new WebSocket(i);n.binaryType="arraybuffer",this.ws=n,this.parked.ws=n,this.wsTimeoutId=setTimeout(()=>{n.readyState!==WebSocket.OPEN&&this.fail("ws-timeout")},this.config.ws_timeout),n.onopen=()=>{clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null,this.streamReadyTimeoutId=setTimeout(()=>this.fail("stream-ready-timeout"),6e4),this.linkTimeoutId=setTimeout(()=>this.fail("no-streamer"),2e4),this.mark("ws_open"),o("WS open \u2014 waiting for config",{region:this.region,streamRequestId:t||"(none)"})},n.onmessage=r=>this.guard("message-failed",()=>this.onWsMessage(r)),n.onerror=r=>{d("WS error",r),this.note("cirrus-ws-error"),this.fail("ws-error")},n.onclose=r=>{if(this.note("cirrus-close",p(r)),!this.settled&&this.parked.state!=="ready"){c("WS closed before ready",{code:r&&r.code}),this.fail("ws-closed");return}this.markDead("ws-closed")}}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{}}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(o("WS \u2190",t.type),this.noteCirrusMessage(t),t.type){case"ssInfo":this.parked.ssData=t.data,this.onStreamerLinked();break;case"config":this.onConfig(t);break;case"offer":this.clearLinkTimeout(),this.onOffer(t);break;case"answer":this.onAnswer(t);break;case"iceCandidate":this.onIceCandidate(t.candidate);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,o("PeerConnection created from config"),i.onicecandidate=n=>{n.candidate&&this.send({type:"iceCandidate",candidate:n.candidate})},i.onconnectionstatechange=()=>this.onPcState(i),i.oniceconnectionstatechange=()=>this.note("ice-state",i.iceConnectionState),i.ondatachannel=n=>this.wireDataChannel(n.channel),i.ontrack=n=>{n.track&&n.track.kind==="video"&&n.streams[0]&&(o("video track received"),this.stream=n.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(()=>{o("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=()=>{o("DataChannel open"),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null,this.mark("dc_open"),this.maybeReady()},e.onclose=()=>{if(this.note("dc-close"),!this.settled&&this.parked.state!=="ready"){c("DataChannel closed before ready"),this.fail("dc-closed");return}this.markDead("dc-closed")},e.readyState==="open"&&this.maybeReady()}maybeReady(){this.settled||!this.dc||this.dc.readyState!=="open"||this.stream&&(this.settled=!0,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.clearLinkTimeout(),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null,this.mark("ready"),this.parked.state="ready",o("READY \u2014 parked on window.__stream",this.parked.timings),this.fireSettled())}mungeSDP(e){let t=this.prioritizeH264Profile(e,_);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
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
|
-
`),n=[];for(const
|
|
4
|
-
`)}publishParked(e){const t=this;this.parked={state:e,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,pollingUrl:void 0,kind:void 0,instanceName:void 0,eta:void 0,region:void 0,controlWs: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 n=this.state;Promise.resolve().then(()=>i(n));return}t.settledCbs.push(i)}}},window.__stream=this.parked}fireSettled(){const e=this.settledCbs;this.settledCbs=[];for(const t of e)try{t(this.parked.state)}catch{}}mark(e){this.parked&&(this.parked.timings[e]=performance.now())}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null}fail(e,t){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){(
|
|
3
|
+
`),n=[];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&&n.push(a)}return n.length===0?e:i.map(r=>{if(!r.startsWith("m=video"))return r;const a=r.split(" "),h=a.slice(0,3),m=a.slice(3),A=[...n.filter(u=>m.includes(u)),...m.filter(u=>!n.includes(u))];return[...h,...A].join(" ")}).join(`\r
|
|
4
|
+
`)}publishParked(e){const t=this;this.parked={state:e,pc:null,dc:null,stream:null,ws:null,ssData:void 0,machineId:void 0,pollingUrl:void 0,kind:void 0,instanceName:void 0,eta:void 0,region:void 0,controlWs:void 0,config:void 0,ids:this.ids,dcBuffer:[],timings:{},trail:[],deadReason:void 0,adopt(){return t.clearTimers(),this.state="adopted",this},onSettled(i){if(typeof i=="function"){if(this.state!=="connecting"){const n=this.state;Promise.resolve().then(()=>i(n));return}t.settledCbs.push(i)}}},window.__stream=this.parked}fireSettled(){const e=this.settledCbs;this.settledCbs=[];for(const t of e)try{t(this.parked.state)}catch{}}mark(e){this.parked&&(this.parked.timings[e]=performance.now(),this.note(e))}note(e,t){const i=this.parked&&this.parked.trail;if(!i)return;const n={t:Math.round(performance.now()),step:e};t!=null&&t!==""&&(n.info=String(t).slice(0,160)),i.length>=60&&i.splice(20,1),i.push(n)}noteHttp(e,t,i){var r;const n=(r=t.status)!=null?r:t.ok?200:"error";this.note(e,`${n} ${Math.round(performance.now()-i)}ms`)}noteHttpError(e,t,i){const n=t&&t.name==="AbortError"?"timeout":"network";this.note(e,`${n} ${Math.round(performance.now()-i)}ms`)}noteCirrusMessage(e){if(e.type==="iceCandidate"&&++this.iceCandidatesIn>1)return;const t=e.type==="streamerList"?e.ids:null;this.note(`cirrus:${e.type}`,Array.isArray(t)?`streamers=${t.length}`:void 0)}onPcState(e){const t=e.connectionState;if(this.note("pc-state",t),t==="failed"){if(!this.settled){this.fail("webrtc-failed");return}this.markDead("webrtc-failed")}}markDead(e){const t=this.parked;!t||t.state!=="ready"||t.deadReason||(t.deadReason=e,this.note("dead-while-parked",e))}clearTimers(){this.wsTimeoutId&&(clearTimeout(this.wsTimeoutId),this.wsTimeoutId=null),clearTimeout(this.orchestrationRetryId),this.orchestrationRetryId=null,clearTimeout(this.instanceReadyTimeoutId),this.instanceReadyTimeoutId=null,clearTimeout(this.streamReadyTimeoutId),this.streamReadyTimeoutId=null,this.clearLinkTimeout(),clearTimeout(this.dcTimeoutId),this.dcTimeoutId=null}clearLinkTimeout(){clearTimeout(this.linkTimeoutId),this.linkTimeoutId=null}onStreamerLinked(){this.clearLinkTimeout(),!(this.dcTimeoutId||this.dc&&this.dc.readyState==="open")&&(this.dcTimeoutId=setTimeout(()=>this.fail("dc-timeout"),this.config.dc_timeout))}fail(e,t){if(!(this.parked&&(this.parked.state==="adopted"||this.parked.state==="failed"))){(S.has(e)?o:d)("FAILED:",e,t||""),this.note("fail",t?`${e}: ${C(t)}`:e),this.settled=!0,this.clearTimers(),this.closeControlWs();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())}}guard(e,t){try{t()}catch(i){this.fail(e,i)}}fetchWithTimeout(e,t,i){const n=new AbortController;t.signal=n.signal;const r=setTimeout(()=>n.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}}new v().run()})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { DestroyRef, ElementRef, PipeTransform, Signal, TemplateRef } from "@angular/core";
|
|
3
3
|
import { UNREAL_CONFIG, UnrealInitialConfig, UnrealMode } from "@3dsource/angular-unreal-module/config";
|
|
4
|
-
import { Observable, OperatorFunction, Subject, Subscription } from "rxjs";
|
|
4
|
+
import { BehaviorSubject, Observable, OperatorFunction, Subject, Subscription } from "rxjs";
|
|
5
5
|
import { Store } from "@ngrx/store";
|
|
6
6
|
import { Actions } from "@ngrx/effects";
|
|
7
7
|
import { FCameraChangedCallback, FOnChangeSequenceChangedCallback, FOnFinishSequenceChangedCallback, FOnFocusObjectCallback, FOnObjectTransformChangedCallback, FOnSceneStateCallback, MetaBoxCallbackList, MetaBoxCommandList, MetaBoxCommandPacket } from "@3dsource/types-unreal";
|
|
@@ -627,7 +627,7 @@ type Quality = 'lime' | 'red' | 'orange';
|
|
|
627
627
|
* every id added here needs a paired vocabulary row in the viewer
|
|
628
628
|
* (`telemetry-vocabulary.ts`).
|
|
629
629
|
*/
|
|
630
|
-
type EventId = 'region-resolving' | 'signaling-connection' | 'webrtc-negotiation' | 'unreal-communication' | 'region-ping-fetch-start' | 'region-ping-fetch-end' | 'region-resolved' | 'polling-started' | 'polling-ended' | 'aws-instance-resolved' | 'signaling-ws-connected' | 'instance-reserved' | 'instance-ready' | 'config-received' | 'ssinfo-received' | 'offer-received' | 'answer-sent' | 'answer-received' | 'first-ice-candidate' | 'last-ice-candidate' | 'ice-checking' | 'ice-gathering-complete' | 'ice-connected' | 'ice-failed' | 'datachannel-connected' | 'first-command-started' | 'loopback-sent' | 'loopback-received' | 'viewport-ready' | 'polling-to-instance' | 'signaling-handshake' | 'ice-connectivity-checks' | 'stabilization-warmup';
|
|
630
|
+
type EventId = 'region-resolving' | 'signaling-connection' | 'webrtc-negotiation' | 'unreal-communication' | 'region-ping-fetch-start' | 'region-ping-fetch-end' | 'prefetch-wait-start' | 'region-resolved' | 'polling-started' | 'polling-ended' | 'request-stream-sent' | 'aws-instance-resolved' | 'signaling-ws-connected' | 'instance-reserved' | 'instance-ready' | 'config-received' | 'ssinfo-received' | 'offer-received' | 'answer-sent' | 'answer-received' | 'first-ice-candidate' | 'last-ice-candidate' | 'ice-checking' | 'ice-gathering-complete' | 'ice-connected' | 'ice-failed' | 'datachannel-connected' | 'first-command-started' | 'loopback-sent' | 'loopback-received' | 'viewport-ready' | 'polling-to-instance' | 'signaling-handshake' | 'ice-connectivity-checks' | 'stabilization-warmup';
|
|
631
631
|
/**
|
|
632
632
|
* High-level cause of `stream.session_ended`.
|
|
633
633
|
* - `disconnected` — Cirrus/WebRTC dropped us mid-stream (granular cause
|
|
@@ -871,6 +871,8 @@ interface SessionEndedData extends TelemetryLifecycleEnvelope {
|
|
|
871
871
|
/** Free-text message paired with {@link disconnectReason}, only when the
|
|
872
872
|
* originating action carried one (e.g. `"Manual Console Drop"`). */
|
|
873
873
|
readonly disconnectMessage?: string;
|
|
874
|
+
readonly closeCode?: number;
|
|
875
|
+
readonly closeReason?: string;
|
|
874
876
|
readonly timestamp: string;
|
|
875
877
|
}
|
|
876
878
|
/** `stream.sample` — every 5 s, window-aggregated. `seq` is monotonic per
|
|
@@ -946,6 +948,23 @@ interface SignalingRetryData extends TelemetryEventEnvelope {
|
|
|
946
948
|
readonly reason: string;
|
|
947
949
|
readonly kind: SignalingRetryKind;
|
|
948
950
|
}
|
|
951
|
+
/** `stream.prefetch` — once per session that met a prefetched stream: how it
|
|
952
|
+
* ended for the module, and the script's step log. */
|
|
953
|
+
interface PrefetchData extends TelemetryEventEnvelope {
|
|
954
|
+
readonly outcome: PrefetchOutcome;
|
|
955
|
+
/** Script `failReason`, or why a ready stream was dead (`ws-closed`, …). */
|
|
956
|
+
readonly failReason?: string;
|
|
957
|
+
readonly failError?: string;
|
|
958
|
+
/** How long the module waited on the in-flight prefetch, ms. */
|
|
959
|
+
readonly waitMs?: number;
|
|
960
|
+
readonly kind?: CirrusRequestKind;
|
|
961
|
+
readonly instanceName?: string;
|
|
962
|
+
readonly streamRequestId?: string;
|
|
963
|
+
/** Steps, oldest first; `t` is ms since the session anchor (may be < 0:
|
|
964
|
+
* the script starts before the session does). */
|
|
965
|
+
readonly trail: readonly PrefetchTrailEntry[];
|
|
966
|
+
readonly timestamp: string;
|
|
967
|
+
}
|
|
949
968
|
/** A command with ≥ 1 pak, send → `commandCallback` (by `correlationId`).
|
|
950
969
|
* Browser and Unreal clocks are never mixed. */
|
|
951
970
|
interface PakTimingItem {
|
|
@@ -980,6 +999,7 @@ interface StreamTelemetryEventMap {
|
|
|
980
999
|
'stream.fps_low': FpsLowData;
|
|
981
1000
|
'stream.signaling_retry': SignalingRetryData;
|
|
982
1001
|
'stream.pak_timing': PakTimingData;
|
|
1002
|
+
'stream.prefetch': PrefetchData;
|
|
983
1003
|
}
|
|
984
1004
|
type StreamTelemetryEventName = keyof StreamTelemetryEventMap;
|
|
985
1005
|
/** Names emitted via `emitLifecycle` — the three `duration`-carrying events. */
|
|
@@ -1106,6 +1126,13 @@ export interface ParkedStream {
|
|
|
1106
1126
|
timings: Record<string, number>;
|
|
1107
1127
|
failReason?: string;
|
|
1108
1128
|
failError?: string;
|
|
1129
|
+
/** Step log for telemetry, oldest first. Absent from scripts older than it. */
|
|
1130
|
+
trail?: PrefetchTrailEntry[];
|
|
1131
|
+
/**
|
|
1132
|
+
* Set when the transport of a `ready` stream died before adoption
|
|
1133
|
+
* (`ws-closed` / `dc-closed` / `webrtc-failed`). Such a stream is not adopted.
|
|
1134
|
+
*/
|
|
1135
|
+
deadReason?: string;
|
|
1109
1136
|
/** Stops the script's watchdog and flips state to `adopted`. */
|
|
1110
1137
|
adopt: () => ParkedStream;
|
|
1111
1138
|
/**
|
|
@@ -1114,6 +1141,32 @@ export interface ParkedStream {
|
|
|
1114
1141
|
*/
|
|
1115
1142
|
onSettled: (cb: (state: ParkedStream['state']) => void) => void;
|
|
1116
1143
|
}
|
|
1144
|
+
/** One step of the prefetch: `t` is `performance.now()` at that moment. */
|
|
1145
|
+
export interface PrefetchTrailEntry {
|
|
1146
|
+
t: number;
|
|
1147
|
+
step: string;
|
|
1148
|
+
info?: string;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* How a prefetch ended for the module: `adopted`; `failed` (the script gave
|
|
1152
|
+
* up); `dead` (ready, but its transport died before adoption); `waiting` (the
|
|
1153
|
+
* session ended while the module still waited); `abandoned` (the wait was
|
|
1154
|
+
* cancelled).
|
|
1155
|
+
*/
|
|
1156
|
+
export type PrefetchOutcome = 'adopted' | 'failed' | 'dead' | 'waiting' | 'abandoned';
|
|
1157
|
+
/** What {@link StreamAdoptionService} knew about one prefetch, for telemetry. */
|
|
1158
|
+
export interface PrefetchReport {
|
|
1159
|
+
outcome: PrefetchOutcome;
|
|
1160
|
+
/** `failReason` of the script, or the `deadReason` of a dead stream. */
|
|
1161
|
+
failReason: string | null;
|
|
1162
|
+
failError: string | null;
|
|
1163
|
+
/** How long the module waited on the in-flight prefetch; `null` = no wait. */
|
|
1164
|
+
waitMs: number | null;
|
|
1165
|
+
kind: CirrusRequestKind | null;
|
|
1166
|
+
instanceName: string | null;
|
|
1167
|
+
streamRequestId: string | null;
|
|
1168
|
+
trail: readonly PrefetchTrailEntry[];
|
|
1169
|
+
}
|
|
1117
1170
|
/** Marker the service writes when it finds the slot free and connects itself. */
|
|
1118
1171
|
export interface AppOwnedStream {
|
|
1119
1172
|
owner: 'app';
|
|
@@ -1693,6 +1746,16 @@ export declare class SignallingService extends SubService {
|
|
|
1693
1746
|
streamRequestId: string;
|
|
1694
1747
|
originInstanceUri: string;
|
|
1695
1748
|
}>;
|
|
1749
|
+
onRequestStreamSent$: Subject<string>;
|
|
1750
|
+
onOrchestrationError$: Subject<{
|
|
1751
|
+
code: number;
|
|
1752
|
+
message: string;
|
|
1753
|
+
}>;
|
|
1754
|
+
onCirrusClose$: Subject<{
|
|
1755
|
+
code: number;
|
|
1756
|
+
reason: string;
|
|
1757
|
+
}>;
|
|
1758
|
+
onPollResponse$: Subject<PollResponse>;
|
|
1696
1759
|
abort$: Observable<import("@ngrx/store").Action<string>>;
|
|
1697
1760
|
private ws;
|
|
1698
1761
|
private controlWs;
|
|
@@ -1861,6 +1924,16 @@ export declare class SignallingService extends SubService {
|
|
|
1861
1924
|
static ɵfac: i0.ɵɵFactoryDeclaration<SignallingService, never>;
|
|
1862
1925
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
1863
1926
|
}
|
|
1927
|
+
/** One orchestration poll, as telemetry sees it. */
|
|
1928
|
+
export interface PollResponse {
|
|
1929
|
+
status: number | 'timeout' | 'network';
|
|
1930
|
+
ms: number;
|
|
1931
|
+
/** The answer named a machine. */
|
|
1932
|
+
ready: boolean;
|
|
1933
|
+
percent: number | null;
|
|
1934
|
+
/** `mm_message`, else `error` (the backend's progress text). */
|
|
1935
|
+
message: string | null;
|
|
1936
|
+
}
|
|
1864
1937
|
/**
|
|
1865
1938
|
* Adopts a WebRTC connection prefetched by the `stream-prefetch.js` head-script
|
|
1866
1939
|
* and parked on `window.__stream`, instead of running `connectToSignaling` from
|
|
@@ -1872,6 +1945,13 @@ export declare class StreamAdoptionService {
|
|
|
1872
1945
|
private readonly store;
|
|
1873
1946
|
private readonly signalling;
|
|
1874
1947
|
private readonly webRtc;
|
|
1948
|
+
/** One report per prefetch the module settled on — read by telemetry. */
|
|
1949
|
+
readonly report$: Subject<PrefetchReport>;
|
|
1950
|
+
/** The module started waiting for an in-flight prefetch. */
|
|
1951
|
+
readonly waitStarted$: Subject<void>;
|
|
1952
|
+
private waiting;
|
|
1953
|
+
/** Snapshot of the prefetch still being waited on, or `null`. */
|
|
1954
|
+
waitingReport(): PrefetchReport | null;
|
|
1875
1955
|
/**
|
|
1876
1956
|
* Decide adoption, WAITING for an in-flight prefetch instead of racing it:
|
|
1877
1957
|
* nothing parked → `false`; settled → adopt-if-ready; still `connecting` →
|
|
@@ -1893,6 +1973,11 @@ export declare class StreamAdoptionService {
|
|
|
1893
1973
|
* cancel can't trigger a stray adoption.
|
|
1894
1974
|
*/
|
|
1895
1975
|
private awaitSettled$;
|
|
1976
|
+
/**
|
|
1977
|
+
* Adopt-or-connect for a settled prefetch, and report how it ended. A prefetch
|
|
1978
|
+
* already adopted on an earlier pass is not reported again.
|
|
1979
|
+
*/
|
|
1980
|
+
private settle;
|
|
1896
1981
|
/** Every `false` here means we are about to connect ourselves — take the slot. */
|
|
1897
1982
|
private tryAdoptOrTakeSlot;
|
|
1898
1983
|
/** Mark the slot as the app's, replacing a spent prefetch object. */
|
|
@@ -1928,6 +2013,7 @@ export declare class StreamStatusTelemetryService2 {
|
|
|
1928
2013
|
private readonly videoService;
|
|
1929
2014
|
private readonly signallingService;
|
|
1930
2015
|
private readonly webRtcPlayer;
|
|
2016
|
+
private readonly adoption;
|
|
1931
2017
|
private readonly unrealConfig;
|
|
1932
2018
|
private readonly destroyRef;
|
|
1933
2019
|
private readonly destroy$;
|
|
@@ -2066,7 +2152,7 @@ export declare class StreamStatusTelemetryService2 {
|
|
|
2066
2152
|
* `ssinfo-received` closer's shape (client / instanceName / …ssData /
|
|
2067
2153
|
* connectionId). For an adopted stream `ssData` is the one the prefetch
|
|
2068
2154
|
* captured and `streamAdopted` hydrated into the store. */
|
|
2069
|
-
private
|
|
2155
|
+
private signalingParams;
|
|
2070
2156
|
private handleWindow;
|
|
2071
2157
|
private evaluateAnomalies;
|
|
2072
2158
|
/** Ship the pak commands finished this window as one `stream.pak_timing`;
|
|
@@ -2120,6 +2206,11 @@ export declare class StreamStatusTelemetryService2 {
|
|
|
2120
2206
|
* stamped here so every event of a session shares a single id.
|
|
2121
2207
|
*/
|
|
2122
2208
|
private track;
|
|
2209
|
+
/**
|
|
2210
|
+
* `stream.prefetch`, once per session: how the prefetch ended and its step
|
|
2211
|
+
* log, with `t` moved onto the session anchor like every other milestone.
|
|
2212
|
+
*/
|
|
2213
|
+
private trackPrefetch;
|
|
2123
2214
|
private emit;
|
|
2124
2215
|
static ɵfac: i0.ɵɵFactoryDeclaration<StreamStatusTelemetryService2, never>;
|
|
2125
2216
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
@@ -2157,7 +2248,10 @@ export declare class UnrealCommunicatorService extends SubService {
|
|
|
2157
2248
|
}
|
|
2158
2249
|
export declare class VideoService extends SubService {
|
|
2159
2250
|
private readonly isVideoPlaying;
|
|
2160
|
-
video
|
|
2251
|
+
/** The current streaming `<video>`; replays the latest one to late subscribers. */
|
|
2252
|
+
readonly video$: BehaviorSubject<HTMLVideoElement | null>;
|
|
2253
|
+
get video(): HTMLVideoElement | null;
|
|
2254
|
+
set video(video: HTMLVideoElement | null);
|
|
2161
2255
|
audio: HTMLAudioElement | null;
|
|
2162
2256
|
private container;
|
|
2163
2257
|
private latencyTestTimings;
|
|
@@ -2232,26 +2326,6 @@ export declare class VideoService extends SubService {
|
|
|
2232
2326
|
static ɵfac: i0.ɵɵFactoryDeclaration<VideoService, never>;
|
|
2233
2327
|
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
2234
2328
|
}
|
|
2235
|
-
declare function finite(value: unknown): number | undefined;
|
|
2236
|
-
/** The `local-candidate` / `remote-candidate` fields read here — the DOM lib
|
|
2237
|
-
* in use does not declare RTCIceCandidateStats. */
|
|
2238
|
-
interface IceCandidateStats {
|
|
2239
|
-
candidateType?: string;
|
|
2240
|
-
protocol?: string;
|
|
2241
|
-
address?: string | null;
|
|
2242
|
-
port?: number;
|
|
2243
|
-
url?: string;
|
|
2244
|
-
relayProtocol?: string;
|
|
2245
|
-
}
|
|
2246
|
-
/**
|
|
2247
|
-
* The route-relevant fields of one candidate. The address is uploaded for a
|
|
2248
|
-
* relay candidate only — host / srflx addresses identify the user, a relay
|
|
2249
|
-
* address identifies the TURN provider's region.
|
|
2250
|
-
*/
|
|
2251
|
-
declare function candidateInfo(candidate: IceCandidateStats | undefined, isLocal: boolean): IceCandidateInfo;
|
|
2252
|
-
/** Id of the selected candidate pair: the transport's pick, or Firefox's
|
|
2253
|
-
* pre-standard `selected` flag when there is no `transport` report. */
|
|
2254
|
-
declare function selectedPairId(stats: RTCStatsReport): string | undefined;
|
|
2255
2329
|
/** Candidates per side by type, pairs by check state, and the selected pair. */
|
|
2256
2330
|
interface IceSnapshot {
|
|
2257
2331
|
readonly local: Readonly<Record<string, number>>;
|
|
@@ -2260,7 +2334,6 @@ interface IceSnapshot {
|
|
|
2260
2334
|
readonly selected: IceRoute | null;
|
|
2261
2335
|
readonly selectedRttMs?: number;
|
|
2262
2336
|
}
|
|
2263
|
-
declare function iceSnapshot(stats: RTCStatsReport): IceSnapshot;
|
|
2264
2337
|
export declare class WebRtcPlayerService extends SubService {
|
|
2265
2338
|
onDataChannelMessage$: Subject<ArrayBuffer>;
|
|
2266
2339
|
/** ICE connection / gathering state changes of the current peer connection. */
|
|
@@ -2308,6 +2381,8 @@ export declare class WebRtcPlayerService extends SubService {
|
|
|
2308
2381
|
* them at the correct point depending on the negotiation flow.
|
|
2309
2382
|
*/
|
|
2310
2383
|
private setupPeerConnection;
|
|
2384
|
+
/** Takes a peer connection over — a freshly created one or an adopted one. */
|
|
2385
|
+
private ownPeerConnection;
|
|
2311
2386
|
private createDataChannel;
|
|
2312
2387
|
private handleCandidateFromServer;
|
|
2313
2388
|
/**
|
|
@@ -2384,15 +2459,21 @@ export declare class WebRtcPlayerService extends SubService {
|
|
|
2384
2459
|
* of running the handshake. The head-script attached property-based handlers
|
|
2385
2460
|
* (`pc.onicecandidate`, `dc.onmessage`, …) and buffered inbound DC messages;
|
|
2386
2461
|
* here we detach those, wire our own (addEventListener-based) handlers, replay
|
|
2387
|
-
* the buffer in order, then
|
|
2388
|
-
*
|
|
2389
|
-
* adopted channel deliberately does not subscribe to it.
|
|
2462
|
+
* the buffer in order, then wire the channel through the same
|
|
2463
|
+
* `ownDataChannel` as the live path, which announces it.
|
|
2390
2464
|
*/
|
|
2391
2465
|
adopt(pc: RTCPeerConnection, dc: RTCDataChannel, stream: MediaStream, config: RTCConfiguration, dcBuffer: ArrayBuffer[]): void;
|
|
2392
2466
|
private setConfig;
|
|
2393
2467
|
private handlePeerConnectionEvents;
|
|
2394
2468
|
private setupTransceiversAsync;
|
|
2395
|
-
|
|
2469
|
+
/**
|
|
2470
|
+
* Takes a DataChannel over — created, server-sent or adopted. Ownership
|
|
2471
|
+
* comes first: the announcement below can run synchronously (an already
|
|
2472
|
+
* open channel with a <video> in place) and its listeners send through
|
|
2473
|
+
* `canSend()` in the same dispatch. `replay` is delivered after the
|
|
2474
|
+
* listeners are wired and before the announcement.
|
|
2475
|
+
*/
|
|
2476
|
+
private ownDataChannel;
|
|
2396
2477
|
private onDataChannelConnected;
|
|
2397
2478
|
/**
|
|
2398
2479
|
* Creates an SDP offer, applies SDP munging, sets it as a local description,
|
|
@@ -3180,6 +3261,7 @@ export declare const streamAdopted: import("@ngrx/store").ActionCreator<string,
|
|
|
3180
3261
|
* same `kind` the service's own flow would have set.
|
|
3181
3262
|
*/
|
|
3182
3263
|
kind: CirrusRequestKind | null;
|
|
3264
|
+
correlationId?: string | null;
|
|
3183
3265
|
/**
|
|
3184
3266
|
* The machine's own id (`originInstanceId` / `signallingServer`). `null`
|
|
3185
3267
|
* from a script that predates this field — the reducer then falls back to a
|
|
@@ -3216,6 +3298,7 @@ export declare const streamAdopted: import("@ngrx/store").ActionCreator<string,
|
|
|
3216
3298
|
* same `kind` the service's own flow would have set.
|
|
3217
3299
|
*/
|
|
3218
3300
|
kind: CirrusRequestKind | null;
|
|
3301
|
+
correlationId?: string | null;
|
|
3219
3302
|
/**
|
|
3220
3303
|
* The machine's own id (`originInstanceId` / `signallingServer`). `null`
|
|
3221
3304
|
* from a script that predates this field — the reducer then falls back to a
|
|
@@ -3771,4 +3854,4 @@ export declare class Semaphore {
|
|
|
3771
3854
|
release(): void;
|
|
3772
3855
|
}
|
|
3773
3856
|
export declare const tapLog: <T>(text: string, ...args: unknown[]) => ((source: Observable<T>) => Observable<T>);
|
|
3774
|
-
export { ALL_KEYBOARD_KEYS, type AggregatedStat, type Area, type AwsInstance, type BootstrapInfo, type CirrusRequestKind, type CloseReason, type Coords, type EmittedMilestone, type EventId, type FpsLowData, type FreezeFrameMessage, type IKeyCode, type IKeyCodeName, type IKeyName, IME_PROCESS_KEY, type IceCandidateInfo, type IceRoute, type ImageOutputValues, type InboundVideoStats, type InputProps, MODIFIER_KEY_CODES, type PakTimingData, type PakTimingItem, type PhaseEndedData, type PhaseOverlay, type PhaseParam, type Quality, type QualityAnomalyData, type Rect, type RegionPick, type RegionProbes, type RegionSource, type ResourceBucket, type ResourceTotals, type RouteSegment, type SampleData, type SelectedIceCandidatePair, type SessionEndReason, type SessionEndedData, type SessionStartedData, type SignalingData, type SignalingRetryData, type SignalingRetryKind, type Size, type SlowResource, type StreamConfig, type StreamResolutionProps, type StreamTelemetryEvent, type StreamTelemetryEventMap, type StreamTelemetryEventName, type StreamTelemetryLifecycleData, type StreamTelemetryLifecycleEventName, type StreamTelemetryTrackData, type TelemetryEventEnvelope, type TelemetryLifecycleEnvelope, UNREAL_CONFIG, type UnrealAfkSlotContext, type UnrealInitialConfig, UnrealKeyCode, type UnrealMode, type UnrealResumeSlotContext, type UnrealSlotTemplateContext, type UnrealStatusSlotContext, type UnrealUiClasses, type UnrealUiPart, type WindowAggregate };
|
|
3857
|
+
export { ALL_KEYBOARD_KEYS, type AggregatedStat, type Area, type AwsInstance, type BootstrapInfo, type CirrusRequestKind, type CloseReason, type Coords, type EmittedMilestone, type EventId, type FpsLowData, type FreezeFrameMessage, type IKeyCode, type IKeyCodeName, type IKeyName, IME_PROCESS_KEY, type IceCandidateInfo, type IceRoute, type ImageOutputValues, type InboundVideoStats, type InputProps, MODIFIER_KEY_CODES, type PakTimingData, type PakTimingItem, type PhaseEndedData, type PhaseOverlay, type PhaseParam, type PrefetchData, type Quality, type QualityAnomalyData, type Rect, type RegionPick, type RegionProbes, type RegionSource, type ResourceBucket, type ResourceTotals, type RouteSegment, type SampleData, type SelectedIceCandidatePair, type SessionEndReason, type SessionEndedData, type SessionStartedData, type SignalingData, type SignalingRetryData, type SignalingRetryKind, type Size, type SlowResource, type StreamConfig, type StreamResolutionProps, type StreamTelemetryEvent, type StreamTelemetryEventMap, type StreamTelemetryEventName, type StreamTelemetryLifecycleData, type StreamTelemetryLifecycleEventName, type StreamTelemetryTrackData, type TelemetryEventEnvelope, type TelemetryLifecycleEnvelope, UNREAL_CONFIG, type UnrealAfkSlotContext, type UnrealInitialConfig, UnrealKeyCode, type UnrealMode, type UnrealResumeSlotContext, type UnrealSlotTemplateContext, type UnrealStatusSlotContext, type UnrealUiClasses, type UnrealUiPart, type WindowAggregate };
|