@arcware-cloud/pixelstreaming-websdk 1.3.3 → 1.3.6

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/index.esm.js CHANGED
@@ -23235,27 +23235,28 @@ class ArcwareApplication extends _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5
23235
23235
  });
23236
23236
  }
23237
23237
  applicationResponse(response) {
23238
- var _a, _b;
23239
- let obj = null;
23238
+ var _a, _b, _c, _d;
23239
+ let obj;
23240
23240
  try {
23241
23241
  obj = (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.parseUnknownToObject)(response, { allowJsonish: true });
23242
23242
  }
23243
- catch (_c) {
23244
- // If not parseable, fall through to generic callback
23243
+ catch (_e) {
23244
+ // not parseable just send to generic handler and bail out
23245
+ (_a = this.responseCallback) === null || _a === void 0 ? void 0 : _a.call(this, response);
23246
+ return;
23245
23247
  }
23246
23248
  const t = (0,_features_common__WEBPACK_IMPORTED_MODULE_10__.normalizeType)(obj["type"]);
23247
23249
  // route to exactly one callback
23248
23250
  if (t === "event") {
23249
- (_a = this.analyticsEventCallback) === null || _a === void 0 ? void 0 : _a.call(this, response);
23251
+ (_b = this.analyticsEventCallback) === null || _b === void 0 ? void 0 : _b.call(this, response);
23250
23252
  return;
23251
23253
  }
23252
23254
  if (t === "filetransfer" || t === "createscreenshot") {
23253
- (_b = this.fileDownloadCallback) === null || _b === void 0 ? void 0 : _b.call(this, response);
23255
+ (_c = this.fileDownloadCallback) === null || _c === void 0 ? void 0 : _c.call(this, response);
23254
23256
  return;
23255
23257
  }
23256
- if (this.responseCallback) {
23257
- this.responseCallback(response);
23258
- }
23258
+ // fallback: unknown type → generic callback
23259
+ (_d = this.responseCallback) === null || _d === void 0 ? void 0 : _d.call(this, response);
23259
23260
  }
23260
23261
  applyArcwareStyles() {
23261
23262
  const PixelStreamingApplicationStyles = new _epicgames_ps_lib_pixelstreamingfrontend_ui_ue5_5__WEBPACK_IMPORTED_MODULE_11__.PixelStreamingApplicationStyle(_styles_ArcwarePixelStreamingApplicationStyles__WEBPACK_IMPORTED_MODULE_0__.ArcwareStyles);
@@ -23427,7 +23428,7 @@ class ArcwareConfig extends _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBP
23427
23428
  if (!config.initialSettings.ss)
23428
23429
  config.initialSettings.ss = DefaultUrl;
23429
23430
  super(config);
23430
- this.VERSION = "1.3.3";
23431
+ this.VERSION = "1.3.6";
23431
23432
  this.settings = settings;
23432
23433
  this.session = new _domain_Session__WEBPACK_IMPORTED_MODULE_0__.Session();
23433
23434
  this._initialSettings = config.initialSettings;
@@ -23598,8 +23599,19 @@ __webpack_require__.r(__webpack_exports__);
23598
23599
 
23599
23600
 
23600
23601
  class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_4__.PixelStreaming {
23602
+ resetInitGuardsAndHooks() {
23603
+ this.videoInitializedSent = false;
23604
+ this._postInitSideEffectsDone = false;
23605
+ this.cancelFirstRenderedFrameHook(); // just in case
23606
+ this.attachFirstRenderedFrameOnce(); // re-arm rVFC/resize on current video element
23607
+ this.bindTransportEvents(); // ensure listeners are bound to current transport
23608
+ }
23609
+ reconnect() {
23610
+ super.reconnect();
23611
+ this.resetInitGuardsAndHooks();
23612
+ }
23601
23613
  bindTransportEvents() {
23602
- var _a, _b, _c, _d, _f, _g, _h;
23614
+ var _a, _b, _c, _d, _e, _f, _g;
23603
23615
  const current = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23604
23616
  if (!current || current === this._boundTransport)
23605
23617
  return;
@@ -23608,13 +23620,24 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23608
23620
  if (this._onWsOpen)
23609
23621
  (_c = (_b = this._boundTransport).removeEventListener) === null || _c === void 0 ? void 0 : _c.call(_b, "open", this._onWsOpen);
23610
23622
  if (this._onWsClose)
23611
- (_f = (_d = this._boundTransport).removeEventListener) === null || _f === void 0 ? void 0 : _f.call(_d, "close", this._onWsClose);
23623
+ (_e = (_d = this._boundTransport).removeEventListener) === null || _e === void 0 ? void 0 : _e.call(_d, "close", this._onWsClose);
23612
23624
  }
23613
23625
  // Define (or reuse) callbacks
23614
23626
  if (!this._onWsOpen) {
23615
23627
  this._onWsOpen = () => {
23616
23628
  this.flushOutbox();
23617
- this.startVideoInitWatchdog(); // only start, no send
23629
+ this.attachFirstRenderedFrameOnce();
23630
+ // no watchdog anymore
23631
+ setTimeout(() => {
23632
+ var _a, _b, _c;
23633
+ if (!this.videoInitializedSent) {
23634
+ const v = (_c = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23635
+ if (v && v.videoWidth > 0 && v.videoHeight > 0 && v.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
23636
+ this.sendVideoInitializedOnce();
23637
+ this.runPostInitSideEffectsOnce();
23638
+ }
23639
+ }
23640
+ }, 1200);
23618
23641
  };
23619
23642
  }
23620
23643
  if (!this._onWsClose) {
@@ -23624,85 +23647,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23624
23647
  };
23625
23648
  }
23626
23649
  // Bind to current
23627
- (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "open", this._onWsOpen);
23628
- (_h = current.addEventListener) === null || _h === void 0 ? void 0 : _h.call(current, "close", this._onWsClose);
23650
+ (_f = current.addEventListener) === null || _f === void 0 ? void 0 : _f.call(current, "open", this._onWsOpen);
23651
+ (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "close", this._onWsClose);
23629
23652
  this._boundTransport = current;
23630
23653
  }
23631
- // conservative media-evidence check (no stats involved)
23632
- hasMediaEvidence() {
23633
- var _a, _b, _c;
23634
- const videoEl = (_c = (_b = (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23635
- if (!videoEl)
23636
- return false;
23637
- const dimensions = videoEl.videoWidth > 0 && videoEl.videoHeight > 0;
23638
- const ready = videoEl.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // >= 2
23639
- const activelyPlaying = !videoEl.paused && !videoEl.ended;
23640
- // Require at least two signals to avoid false positives on metadata-only loads
23641
- const strong = (dimensions && ready) || (dimensions && activelyPlaying) || (ready && activelyPlaying);
23642
- // also allow explicit 'playing' evidence we latched separately
23643
- return strong || this.seenVideoPlaying;
23644
- }
23645
- startVideoInitWatchdog() {
23646
- // restart the interval
23647
- this.stopVideoInitWatchdog();
23648
- const startedAt = Date.now();
23649
- this.videoInitWatchdogInterval = window.setInterval(() => {
23650
- if (this.videoInitializedSent) {
23651
- this.stopVideoInitWatchdog();
23652
- return;
23653
- }
23654
- if (this.hasMediaEvidence()) {
23655
- this.sendVideoInitializedOnce();
23656
- this.stopVideoInitWatchdog();
23657
- return;
23658
- }
23659
- // Optional maximum wait: if set and exceeded, backfill only if we saw playing
23660
- if (this.videoInitMaxWaitMs && Date.now() - startedAt > this.videoInitMaxWaitMs) {
23661
- if (this.seenVideoPlaying && !this.videoInitializedSent) {
23662
- this.sendVideoInitializedOnce();
23663
- }
23664
- this.stopVideoInitWatchdog();
23665
- }
23666
- }, this.videoInitCheckMs);
23667
- }
23668
- stopVideoInitWatchdog() {
23669
- if (this.videoInitWatchdogInterval) {
23670
- window.clearInterval(this.videoInitWatchdogInterval);
23671
- this.videoInitWatchdogInterval = undefined;
23672
- }
23673
- }
23674
- startTransportWatcher() {
23675
- // Rebind if the transport object reference changes (cheap safety net)
23676
- if (this._transportWatchTimer)
23677
- return;
23678
- this._transportWatchTimer = window.setInterval(() => {
23679
- var _a;
23680
- const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23681
- if (cur && cur !== this._boundTransport)
23682
- this.bindTransportEvents();
23683
- }, 3000); // light-touch; adjust if you like
23684
- }
23685
- stopTransportWatcher() {
23686
- if (this._transportWatchTimer) {
23687
- window.clearInterval(this._transportWatchTimer);
23688
- this._transportWatchTimer = undefined;
23689
- }
23690
- }
23691
- bindVideoElPlayingOnce() {
23692
- var _a, _b, _c;
23693
- const videoEl = (_c = (_b = (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23694
- if (!videoEl || videoEl === this._boundVideoEl)
23695
- return;
23696
- // Unbind older one if replaced
23697
- if (this._boundVideoEl && this._onVideoPlaying) {
23698
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
23699
- }
23700
- this._onVideoPlaying = () => {
23701
- this.seenVideoPlaying = true;
23702
- };
23703
- videoEl.addEventListener("playing", this._onVideoPlaying, { once: true });
23704
- this._boundVideoEl = videoEl;
23705
- }
23706
23654
  get isWsOpen() {
23707
23655
  var _a, _b;
23708
23656
  const ws = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.webSocket;
@@ -23723,7 +23671,7 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23723
23671
  return state !== undefined ? state : WebSocket.CLOSED;
23724
23672
  }
23725
23673
  constructor(config, overrides) {
23726
- var _a, _b, _c, _d, _f, _g, _h;
23674
+ var _a, _b, _c;
23727
23675
  /** As soon as upstream is fixed, we got to adjust the url building process. */
23728
23676
  (0,_ApplyUrlHack__WEBPACK_IMPORTED_MODULE_6__.ApplyUrlHack)();
23729
23677
  super(config, overrides);
@@ -23731,25 +23679,18 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23731
23679
  this.isProcessingQueue = false;
23732
23680
  // --- reliable send / idempotence ---
23733
23681
  this.outbox = [];
23734
- this.retryTimers = new Map(); // reserved if you later add ACKs
23735
23682
  this.videoInitializedSent = false;
23736
- this.versionReplyInFlight = false;
23737
- this.nextMsgId = 1;
23738
- this.videoInitCheckMs = 250; // default poll; can be overridden from config
23739
- this.seenVideoPlaying = false;
23683
+ // --- first-frame (rVFC) detection + element swap handling ---
23684
+ this._rVFCsupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype;
23685
+ this._postInitSideEffectsDone = false;
23740
23686
  // Externalized
23741
- /** On ping the session creation timestamp will be updated. */
23742
23687
  this.queueHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23743
- /** Error receiver. */
23744
23688
  this.errorHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23745
- /** LoveLetter */
23746
23689
  this.loveLetterHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23747
- /** SessionId */
23748
23690
  this.sessionIdHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23749
- /** VideoInitialized */
23691
+ /** VideoInitialized (native Epic event) */
23750
23692
  this.videoInitializedHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23751
23693
  // Internal
23752
- /** WebSocket Close */
23753
23694
  this.websocketOnCloseHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23754
23695
  /** Theoretically this call should be used to implement the signalling url, but this would not work at all, if config is setup for "AutoConnect=true"
23755
23696
  * Instead we use ApplyUrlHack();
@@ -23762,19 +23703,15 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23762
23703
  const transport = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23763
23704
  (_b = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _b === void 0 ? void 0 : _b.call(transport, "open", () => {
23764
23705
  this.flushOutbox();
23765
- this.startVideoInitWatchdog(); // only start, no send
23706
+ // no polling watchdog
23766
23707
  });
23767
23708
  (_c = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _c === void 0 ? void 0 : _c.call(transport, "close", (evt) => {
23768
- // Bubble to your public close handler if you expose one
23769
23709
  if (this.websocketOnCloseHandler) {
23770
23710
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23771
23711
  }
23772
23712
  });
23773
- this.setupVideoInitFallbacks();
23774
23713
  // Set override config.
23775
23714
  this.config = config;
23776
- this.videoInitCheckMs = (_f = (_d = this.config.settings) === null || _d === void 0 ? void 0 : _d.videoInitCheckMs) !== null && _f !== void 0 ? _f : this.videoInitCheckMs;
23777
- this.videoInitMaxWaitMs = (_h = (_g = this.config.settings) === null || _g === void 0 ? void 0 : _g.videoInitMaxWaitMs) !== null && _h !== void 0 ? _h : undefined;
23778
23715
  this.loveLettersList = [];
23779
23716
  this.microphoneOverlay = new _ui_MicrophoneOverlay__WEBPACK_IMPORTED_MODULE_7__.MicrophoneOverlay(this);
23780
23717
  this.diagnosticsCollector = new _features_DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__.DiagnosticsCollector({
@@ -23783,9 +23720,9 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23783
23720
  // after super(...) and once webRtcController is available
23784
23721
  this.bindTransportEvents();
23785
23722
  this.startTransportWatcher();
23786
- // ensure video 'playing' fallback is attached to the current element
23787
- this.bindVideoElPlayingOnce();
23788
- // this.loveLettersContainer = null;
23723
+ // Arm first-frame detector & watch for element swaps
23724
+ this.attachFirstRenderedFrameOnce();
23725
+ this.watchVideoElementReplacement();
23789
23726
  this.wrapWebSocketOnCloseHandler();
23790
23727
  // Bind the event listener function to the class instance
23791
23728
  this.handleResolutionChange = this.handleResolutionChange.bind(this);
@@ -23853,14 +23790,13 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23853
23790
  (_a = _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Warning) === null || _a === void 0 ? void 0 : _a.call(_epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger, `Diagnostics collection failed: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
23854
23791
  }
23855
23792
  finally {
23856
- // Build payload with required fields only; append diagnostics if available
23857
23793
  const payload = {
23858
23794
  type: "version",
23859
23795
  version: this.config.VERSION
23860
23796
  };
23861
23797
  if (diagnostics !== undefined)
23862
23798
  payload.diagnostics = diagnostics;
23863
- this.send(payload); // uses your buffered send
23799
+ this.send(payload); // uses buffered send
23864
23800
  }
23865
23801
  });
23866
23802
  }
@@ -23873,14 +23809,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23873
23809
  this.session.set(this.session.current);
23874
23810
  }
23875
23811
  /** Handle incoming configurations. */
23876
- // public readonly streamInfoHandler = new EventHandler<never>();
23877
23812
  onStreamInfo(streamInfo) {
23878
23813
  var _a, _b, _c, _d;
23879
23814
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(`StreamInfo received.`);
23880
- // Save the streamInfo to the instance variable
23881
23815
  this.streamInfo = streamInfo;
23882
- // By setting this hidden flag, the evaluation of the streamInfo can be cut off.
23883
- // Cutting of the streamInfo can lead to unwanted behavior, therefore it's not advertised.
23884
23816
  if (!((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.settings) === null || _b === void 0 ? void 0 : _b["do-not-eval-streamInfo"])) {
23885
23817
  const { afk } = streamInfo.streamInfo;
23886
23818
  if (afk) {
@@ -23890,7 +23822,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23890
23822
  const { afkController } = this.webRtcController;
23891
23823
  afkController.countDown = afk.action;
23892
23824
  }
23893
- // Relevant settings need to be interpreted when they come through the streamInfo to the client.
23894
23825
  if ((_c = streamInfo.streamInfo.webSdkSettings) === null || _c === void 0 ? void 0 : _c.init) {
23895
23826
  this.config.setSettings(streamInfo.streamInfo.webSdkSettings.init);
23896
23827
  }
@@ -23905,7 +23836,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23905
23836
  }
23906
23837
  this.handleMouseLock();
23907
23838
  this.injectCustomUI();
23908
- // EventHandler.Emit(this.streamInfoHandler, undefined as never);
23909
23839
  }
23910
23840
  onQueue(message) {
23911
23841
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(`QueueInfo received.`);
@@ -23916,7 +23846,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23916
23846
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.errorHandler, error);
23917
23847
  }
23918
23848
  onLoveLetter(loveLetter) {
23919
- // Logger.Info(Logger.GetStackTrace(), loveLetter.reason);
23920
23849
  if (this.config.settings.loveLetterLogging)
23921
23850
  console.info(loveLetter.reason);
23922
23851
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.loveLetterHandler, loveLetter);
@@ -23924,7 +23853,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23924
23853
  }
23925
23854
  onSessionId(message) {
23926
23855
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(message.sessionId);
23927
- // console.info(`Session: ${message.sessionId}`);
23928
23856
  this.session.set(message.sessionId);
23929
23857
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23930
23858
  }
@@ -23932,59 +23860,112 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23932
23860
  if (this.videoInitializedSent)
23933
23861
  return;
23934
23862
  this.videoInitializedSent = true;
23935
- // Use withId if you plan to support idempotence on the server
23936
- this.send(/* this.withId( */ { type: "onVideoInitialized" } /* ) */);
23863
+ // cancel rVFC if still armed (we've already initialized)
23864
+ this.cancelFirstRenderedFrameHook();
23865
+ this.send({ type: "onVideoInitialized" });
23937
23866
  if (this.videoInitializedHandler) {
23938
23867
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.videoInitializedHandler, undefined);
23939
23868
  }
23940
23869
  }
23941
- // Attach fallbacks once (call this in constructor after transport wiring)
23942
- setupVideoInitFallbacks() {
23943
- var _a, _b, _c, _d, _f, _g, _h;
23944
- // Fallback #1: HTMLVideoElement 'playing'
23945
- const videoEl = (_c = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23946
- (_d = videoEl === null || videoEl === void 0 ? void 0 : videoEl.addEventListener) === null || _d === void 0 ? void 0 : _d.call(videoEl, "playing", () => {
23947
- this.seenVideoPlaying = true;
23948
- }, { once: true });
23949
- (_f = this.addEventListener) === null || _f === void 0 ? void 0 : _f.call(this, "statsReceived", (_e) => {
23950
- /* no-op for video init */
23951
- });
23952
- // Fallback #3: after socket opens, give it a tick
23953
- const transport = (_g = this.webRtcController) === null || _g === void 0 ? void 0 : _g.transport;
23954
- (_h = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _h === void 0 ? void 0 : _h.call(transport, "open", () => {
23955
- this.startVideoInitWatchdog(); // only start, no send
23956
- });
23957
- }
23958
- onVideoInitialized() {
23959
- var _a, _b, _c, _d, _f, _g, _h;
23960
- // send early and once
23961
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
23962
- this.sendVideoInitializedOnce();
23963
- // keep side-effects guarded so they can't break the send
23870
+ runPostInitSideEffectsOnce() {
23871
+ var _a, _b, _c, _d, _e, _f, _g;
23872
+ if (this._postInitSideEffectsDone)
23873
+ return;
23874
+ this._postInitSideEffectsDone = true;
23964
23875
  try {
23965
23876
  (_a = this.handleMouseLock) === null || _a === void 0 ? void 0 : _a.call(this);
23966
23877
  }
23967
- catch (_j) { }
23878
+ catch (_h) { }
23968
23879
  try {
23969
23880
  (_b = this.handleResolutionChange) === null || _b === void 0 ? void 0 : _b.call(this);
23970
23881
  }
23971
- catch (_k) { }
23882
+ catch (_j) { }
23972
23883
  try {
23973
23884
  (_c = this.handleRemoveLoveLetters) === null || _c === void 0 ? void 0 : _c.call(this);
23974
23885
  }
23886
+ catch (_k) { }
23887
+ try {
23888
+ (_e = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _e === void 0 ? void 0 : _e.call(_d, false);
23889
+ }
23975
23890
  catch (_l) { }
23976
23891
  try {
23977
- (_f = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _f === void 0 ? void 0 : _f.call(_d, false);
23892
+ (_f = this.applyResolutionIfPlaying) === null || _f === void 0 ? void 0 : _f.call(this);
23978
23893
  }
23979
23894
  catch (_m) { }
23980
23895
  try {
23981
- (_g = this.applyResolutionIfPlaying) === null || _g === void 0 ? void 0 : _g.call(this);
23896
+ (_g = this.removeXRIconIfDisabled) === null || _g === void 0 ? void 0 : _g.call(this);
23982
23897
  }
23983
23898
  catch (_o) { }
23899
+ }
23900
+ onVideoInitialized() {
23901
+ if (this.videoInitializedSent) {
23902
+ // Init was already sent by rVFC; ensure side-effects run once, then bail.
23903
+ this.runPostInitSideEffectsOnce();
23904
+ return;
23905
+ }
23906
+ //console.log("Ran from Epic");
23907
+ this.sendVideoInitializedOnce();
23908
+ this.runPostInitSideEffectsOnce();
23909
+ }
23910
+ // --- First-frame detection (rVFC/resize) ---
23911
+ attachFirstRenderedFrameOnce() {
23912
+ var _a, _b, _c, _d;
23913
+ const v = (_c = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23914
+ if (!v)
23915
+ return;
23916
+ if (this._rVFCArmedForElement === v)
23917
+ return;
23918
+ this.cancelFirstRenderedFrameHook();
23919
+ this._rVFCArmedForElement = v;
23920
+ // Modern path
23921
+ if (this._rVFCsupported) {
23922
+ const anyV = v;
23923
+ this._rvfcHandle = (_d = anyV.requestVideoFrameCallback) === null || _d === void 0 ? void 0 : _d.call(anyV, (_now, _meta) => {
23924
+ this.sendVideoInitializedOnce();
23925
+ this.runPostInitSideEffectsOnce();
23926
+ this._rvfcHandle = undefined;
23927
+ });
23928
+ }
23929
+ // Always attach resize once as a parallel fallback
23930
+ const onResize = () => {
23931
+ v.removeEventListener("resize", onResize);
23932
+ this.sendVideoInitializedOnce();
23933
+ this.runPostInitSideEffectsOnce();
23934
+ };
23935
+ v.addEventListener("resize", onResize, { once: true });
23936
+ }
23937
+ cancelFirstRenderedFrameHook() {
23938
+ var _a;
23939
+ const v = this._rVFCArmedForElement;
23940
+ if (!v)
23941
+ return;
23942
+ if (this._rvfcHandle && "cancelVideoFrameCallback" in HTMLVideoElement.prototype) {
23943
+ const anyV = v;
23944
+ try {
23945
+ (_a = anyV.cancelVideoFrameCallback) === null || _a === void 0 ? void 0 : _a.call(anyV, this._rvfcHandle);
23946
+ }
23947
+ catch (_b) { }
23948
+ }
23949
+ this._rvfcHandle = undefined;
23950
+ this._rVFCArmedForElement = undefined;
23951
+ }
23952
+ watchVideoElementReplacement() {
23953
+ var _a, _b, _c, _d;
23954
+ // Observe the player container; if a new <video> is inserted/replaced, re-arm rVFC
23984
23955
  try {
23985
- (_h = this.removeXRIconIfDisabled) === null || _h === void 0 ? void 0 : _h.call(this);
23956
+ const parent = (_c = (_b = (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoParentElement) === null || _c === void 0 ? void 0 : _c.call(_b);
23957
+ if (!parent)
23958
+ return;
23959
+ (_d = this._mo) === null || _d === void 0 ? void 0 : _d.disconnect();
23960
+ this._mo = new MutationObserver(() => {
23961
+ // Try to re-attach on any child change (cheap and reliable)
23962
+ this.attachFirstRenderedFrameOnce();
23963
+ });
23964
+ this._mo.observe(parent, { childList: true, subtree: true });
23965
+ }
23966
+ catch (_e) {
23967
+ // noop
23986
23968
  }
23987
- catch (_p) { }
23988
23969
  }
23989
23970
  /** Adding a zod-safe handler. */
23990
23971
  addMessageHandler(type, zod, boundMethod) {
@@ -23993,13 +23974,12 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23993
23974
  if (parsed.success)
23994
23975
  boundMethod.call(this, parsed.data);
23995
23976
  else {
23996
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23997
23977
  const parsedError = parsed;
23998
23978
  const error = new Error(`Unexpected message content. Event:'${type}', ZodError: ${parsedError.error.message}`);
23999
23979
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Error(error.message);
24000
23980
  }
24001
23981
  if (type === "error") {
24002
- //this.disconnect();
23982
+ // this.disconnect();
24003
23983
  }
24004
23984
  });
24005
23985
  }
@@ -24010,7 +23990,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24010
23990
  */
24011
23991
  sendStats(stats) {
24012
23992
  const message = { type: "stats", stats: (0,_domain_Stats__WEBPACK_IMPORTED_MODULE_1__.Stats)(stats) };
24013
- // This cast has to be done, since Frontend does not use typescript-strict setting.
24014
23993
  this.send(message);
24015
23994
  }
24016
23995
  send(a, b) {
@@ -24051,22 +24030,18 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24051
24030
  this.signallingProtocol.sendMessage(m);
24052
24031
  }
24053
24032
  catch (_a) {
24054
- // if we fail mid-flush, put remaining back and bail
24055
24033
  this.outbox.unshift(m, ...batch.slice(batch.indexOf(m) + 1));
24056
24034
  break;
24057
24035
  }
24058
24036
  }
24059
24037
  }
24060
24038
  handleResolutionChange() {
24061
- var _a, _b, _c, _d, _f, _g, _h;
24062
- // Get the resolution from the streamInfo if available
24039
+ var _a, _b, _c, _d, _e, _f, _g;
24063
24040
  if (!(this === null || this === void 0 ? void 0 : this.streamInfo) || !(this === null || this === void 0 ? void 0 : this.webRtcController) || !((_b = (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement())) {
24064
24041
  return;
24065
24042
  }
24066
- // Get the resolution from the streamInfo if available
24067
24043
  const resolution = (_d = (_c = this === null || this === void 0 ? void 0 : this.streamInfo) === null || _c === void 0 ? void 0 : _c.streamInfo) === null || _d === void 0 ? void 0 : _d.resolution;
24068
- const unrealVersion = (_h = (_g = (_f = this === null || this === void 0 ? void 0 : this.streamInfo) === null || _f === void 0 ? void 0 : _f.streamInfo) === null || _g === void 0 ? void 0 : _g.meta) === null || _h === void 0 ? void 0 : _h.version;
24069
- // Check if resolution.dynamic is true
24044
+ const unrealVersion = (_g = (_f = (_e = this === null || this === void 0 ? void 0 : this.streamInfo) === null || _e === void 0 ? void 0 : _e.streamInfo) === null || _f === void 0 ? void 0 : _f.meta) === null || _g === void 0 ? void 0 : _g.version;
24070
24045
  if (resolution && (resolution === null || resolution === void 0 ? void 0 : resolution.dynamic)) {
24071
24046
  setTimeout(() => {
24072
24047
  var _a;
@@ -24074,32 +24049,24 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24074
24049
  const videoElementParent = videoPlayer === null || videoPlayer === void 0 ? void 0 : videoPlayer.getVideoParentElement();
24075
24050
  const browserWidth = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().width;
24076
24051
  const browserHeight = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().height;
24077
- // Define maximum allowed resolution
24078
24052
  const maxWidth = resolution === null || resolution === void 0 ? void 0 : resolution.width;
24079
24053
  const maxHeight = resolution === null || resolution === void 0 ? void 0 : resolution.height;
24080
- // Calculate the aspect ratio of the browser viewport
24081
24054
  const aspectRatio = browserWidth / browserHeight;
24082
- // Calculate the new width and height based on the aspect ratio
24083
24055
  let limitedWidth, limitedHeight;
24084
24056
  if (aspectRatio > maxWidth / maxHeight) {
24085
- // Width is the limiting factor
24086
24057
  limitedWidth = maxWidth;
24087
24058
  limitedHeight = maxWidth / aspectRatio;
24088
24059
  }
24089
24060
  else {
24090
- // Height is the limiting factor
24091
24061
  limitedHeight = maxHeight;
24092
24062
  limitedWidth = maxHeight * aspectRatio;
24093
24063
  }
24094
24064
  if (videoPlayer) {
24095
- // Check Unreal Engine version
24096
24065
  if (!unrealVersion || (unrealVersion === null || unrealVersion === void 0 ? void 0 : unrealVersion.startsWith("4.27"))) {
24097
- // Use the aspect ratio limited resolution for the r.setres command
24098
24066
  const descriptor = { Console: `r.setres ${Math.round(limitedWidth)}x${Math.round(limitedHeight)}w` };
24099
24067
  (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.emitUIInteraction(descriptor);
24100
24068
  }
24101
24069
  else {
24102
- // Use the aspect ratio limited resolution for the onMatchViewportResolutionCallback
24103
24070
  videoPlayer.onMatchViewportResolutionCallback(Math.round(limitedWidth), Math.round(limitedHeight));
24104
24071
  }
24105
24072
  }
@@ -24118,21 +24085,39 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24118
24085
  }
24119
24086
  }
24120
24087
  removePlayer() {
24121
- var _a, _b, _c, _d;
24122
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
24088
+ var _a, _b, _c, _d, _e;
24123
24089
  this.stopTransportWatcher();
24124
- // Optional: unbind handlers
24090
+ // Unbind transport listeners
24125
24091
  if (this._boundTransport) {
24126
24092
  if (this._onWsOpen)
24127
24093
  (_b = (_a = this._boundTransport).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "open", this._onWsOpen);
24128
24094
  if (this._onWsClose)
24129
24095
  (_d = (_c = this._boundTransport).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, "close", this._onWsClose);
24130
24096
  }
24131
- if (this._boundVideoEl && this._onVideoPlaying) {
24132
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
24097
+ // Cancel rVFC + observer
24098
+ this.cancelFirstRenderedFrameHook();
24099
+ try {
24100
+ (_e = this._mo) === null || _e === void 0 ? void 0 : _e.disconnect();
24133
24101
  }
24102
+ catch (_f) { }
24134
24103
  this.disconnect();
24135
24104
  }
24105
+ startTransportWatcher() {
24106
+ if (this._transportWatchTimer)
24107
+ return;
24108
+ this._transportWatchTimer = window.setInterval(() => {
24109
+ var _a;
24110
+ const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
24111
+ if (cur && cur !== this._boundTransport)
24112
+ this.bindTransportEvents();
24113
+ }, 3000);
24114
+ }
24115
+ stopTransportWatcher() {
24116
+ if (this._transportWatchTimer) {
24117
+ window.clearInterval(this._transportWatchTimer);
24118
+ this._transportWatchTimer = undefined;
24119
+ }
24120
+ }
24136
24121
  handleMouseLock() {
24137
24122
  var _a, _b, _c;
24138
24123
  (_a = this === null || this === void 0 ? void 0 : this.config) === null || _a === void 0 ? void 0 : _a.modifyInitialSettings(!((_c = (_b = this === null || this === void 0 ? void 0 : this.streamInfo) === null || _b === void 0 ? void 0 : _b.streamInfo.meta) === null || _c === void 0 ? void 0 : _c.mouseLock));
@@ -24164,31 +24149,25 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24164
24149
  }
24165
24150
  pushLetter(letter) {
24166
24151
  var _a;
24167
- // Add the letter to the queue
24168
24152
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersQueue) === null || _a === void 0 ? void 0 : _a.push(letter);
24169
- // If not already processing the queue, start processing
24170
24153
  if (!this.isProcessingQueue) {
24171
24154
  this === null || this === void 0 ? void 0 : this.processLoveLetterQueue();
24172
24155
  }
24173
24156
  }
24174
24157
  processLoveLetterQueue() {
24175
24158
  var _a, _b;
24176
- // Set the flag to indicate that we are processing the queue
24177
24159
  this.isProcessingQueue = true;
24178
- // Take the first item from the queue
24179
24160
  const letter = this.loveLettersQueue.shift();
24180
24161
  if (letter !== undefined) {
24181
24162
  const formattedLoveLetter = letter === null || letter === void 0 ? void 0 : letter.replace(/LL: |\.$/g, "");
24182
24163
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _a === void 0 ? void 0 : _a.push(formattedLoveLetter);
24183
24164
  const loveLettersBox = new _ui_LoveLetters__WEBPACK_IMPORTED_MODULE_13__.LoveLetters();
24184
24165
  loveLettersBox === null || loveLettersBox === void 0 ? void 0 : loveLettersBox.addLetter(formattedLoveLetter, (_b = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _b === void 0 ? void 0 : _b.length);
24185
- // Process the next item in the queue after a delay
24186
24166
  setTimeout(() => {
24187
24167
  this.processLoveLetterQueue();
24188
24168
  }, 1000);
24189
24169
  }
24190
24170
  else {
24191
- // If the queue is empty, reset the flag
24192
24171
  this.isProcessingQueue = false;
24193
24172
  }
24194
24173
  }
@@ -24230,9 +24209,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24230
24209
  (_b = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _b === void 0 ? void 0 : _b.toggleMessage(enable);
24231
24210
  setTimeout(() => { var _a; return (_a = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _a === void 0 ? void 0 : _a.toggleVisibility(true); }, 200);
24232
24211
  setTimeout(() => {
24233
- this.videoInitializedSent = false; // <-- reset before a real media restart
24234
- this.bindVideoElPlayingOnce(); // in case a new element will appear
24235
- this.bindTransportEvents(); // cheap rebind now
24212
+ this.videoInitializedSent = false; // reset before a real media restart
24213
+ // Re-arm rVFC on the current (or new) video element
24214
+ this.attachFirstRenderedFrameOnce();
24215
+ this.bindTransportEvents();
24236
24216
  this.reconnect();
24237
24217
  }, 1000);
24238
24218
  }
@@ -24246,12 +24226,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24246
24226
  }
24247
24227
  onStreamingStateChange(callback) {
24248
24228
  var _a, _b, _c, _d;
24249
- // Listen to video element events
24250
24229
  const videoElement = (_b = (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.videoPlayer) === null || _b === void 0 ? void 0 : _b.getVideoElement();
24251
24230
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("play", () => callback(true));
24252
24231
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("pause", () => callback(false));
24253
24232
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("ended", () => callback(false));
24254
- // Listen to WebSocket events
24255
24233
  (_c = this.webRtcController) === null || _c === void 0 ? void 0 : _c.transport.addEventListener("open", () => {
24256
24234
  callback(true);
24257
24235
  });
@@ -24260,12 +24238,12 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24260
24238
  });
24261
24239
  }
24262
24240
  removeXRIconIfDisabled() {
24263
- var _a, _b, _c, _d, _f;
24241
+ var _a, _b, _c, _d, _e;
24264
24242
  if (!((_b = (_a = this === null || this === void 0 ? void 0 : this.config) === null || _a === void 0 ? void 0 : _a.initialSettings) === null || _b === void 0 ? void 0 : _b.XRControllerInput)) {
24265
24243
  (_c = this === null || this === void 0 ? void 0 : this.config) === null || _c === void 0 ? void 0 : _c.setFlagEnabled(_epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_11__.Flags.XRControllerInput, false);
24266
24244
  if (this.videoElementParent) {
24267
24245
  if ((_d = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _d === void 0 ? void 0 : _d.parentElement) {
24268
- const xrBtn = (_f = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _f === void 0 ? void 0 : _f.parentElement.querySelector("#xrBtn");
24246
+ const xrBtn = (_e = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _e === void 0 ? void 0 : _e.parentElement.querySelector("#xrBtn");
24269
24247
  xrBtn === null || xrBtn === void 0 ? void 0 : xrBtn.remove();
24270
24248
  }
24271
24249
  }
@@ -24411,7 +24389,7 @@ class ConnectionIdentifier {
24411
24389
  return;
24412
24390
  const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24413
24391
  if (activeStates.length === 1) {
24414
- console.log(`PixelStreaming Instance is connected.`);
24392
+ //console.log(`PixelStreaming Instance is connected.`);
24415
24393
  }
24416
24394
  else if (activeStates.length !== 0) {
24417
24395
  console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);