@arcware-cloud/pixelstreaming-websdk 1.3.4 → 1.3.7

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
@@ -23428,7 +23428,7 @@ class ArcwareConfig extends _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBP
23428
23428
  if (!config.initialSettings.ss)
23429
23429
  config.initialSettings.ss = DefaultUrl;
23430
23430
  super(config);
23431
- this.VERSION = "1.3.4";
23431
+ this.VERSION = "1.3.7";
23432
23432
  this.settings = settings;
23433
23433
  this.session = new _domain_Session__WEBPACK_IMPORTED_MODULE_0__.Session();
23434
23434
  this._initialSettings = config.initialSettings;
@@ -23599,8 +23599,33 @@ __webpack_require__.r(__webpack_exports__);
23599
23599
 
23600
23600
 
23601
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
+ }
23613
+ // Classify CloseEvent into retryable vs. legit (non-retryable)
23614
+ isRetryableClose(evt) {
23615
+ //const reason = (evt.reason || "").toLowerCase();
23616
+ if (evt.code >= 4000 /*||
23617
+ reason.includes("Stream disconnected.") ||
23618
+ reason.includes("forbidden") ||
23619
+ reason.includes("invalid token") ||
23620
+ reason.includes("policy violation") ||
23621
+ reason.includes("not allowed")*/) {
23622
+ return false; // legit server-side rejection: do NOT auto-retry
23623
+ }
23624
+ // Transient/server/network issues: keep auto behavior
23625
+ return true;
23626
+ }
23602
23627
  bindTransportEvents() {
23603
- var _a, _b, _c, _d, _f, _g, _h;
23628
+ var _a, _b, _c, _d, _e, _f, _g;
23604
23629
  const current = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23605
23630
  if (!current || current === this._boundTransport)
23606
23631
  return;
@@ -23609,101 +23634,54 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23609
23634
  if (this._onWsOpen)
23610
23635
  (_c = (_b = this._boundTransport).removeEventListener) === null || _c === void 0 ? void 0 : _c.call(_b, "open", this._onWsOpen);
23611
23636
  if (this._onWsClose)
23612
- (_f = (_d = this._boundTransport).removeEventListener) === null || _f === void 0 ? void 0 : _f.call(_d, "close", this._onWsClose);
23637
+ (_e = (_d = this._boundTransport).removeEventListener) === null || _e === void 0 ? void 0 : _e.call(_d, "close", this._onWsClose);
23613
23638
  }
23614
23639
  // Define (or reuse) callbacks
23615
23640
  if (!this._onWsOpen) {
23616
23641
  this._onWsOpen = () => {
23617
23642
  this.flushOutbox();
23618
- this.startVideoInitWatchdog(); // only start, no send
23643
+ this.attachFirstRenderedFrameOnce();
23644
+ // no watchdog anymore
23645
+ setTimeout(() => {
23646
+ var _a, _b, _c;
23647
+ if (!this.videoInitializedSent) {
23648
+ 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);
23649
+ if (v && v.videoWidth > 0 && v.videoHeight > 0 && v.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
23650
+ this.sendVideoInitializedOnce();
23651
+ this.runPostInitSideEffectsOnce();
23652
+ }
23653
+ }
23654
+ }, 1200);
23619
23655
  };
23620
23656
  }
23621
23657
  if (!this._onWsClose) {
23622
23658
  this._onWsClose = (evt) => {
23659
+ var _a, _b;
23660
+ console.log(evt.code);
23661
+ console.log(evt.reason);
23662
+ const retryable = this.isRetryableClose(evt);
23663
+ if (!retryable) {
23664
+ // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23665
+ try {
23666
+ (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, evt.reason, false);
23667
+ }
23668
+ catch (_c) { }
23669
+ // Optional: pause the transport watcher so we don't churn while idle
23670
+ this.stopTransportWatcher();
23671
+ }
23672
+ else {
23673
+ // Leave your existing auto-retry behavior for transient errors
23674
+ this.startTransportWatcher(); // (in case it was stopped)
23675
+ }
23623
23676
  // Bubble to your public close handler
23624
23677
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23625
23678
  };
23626
23679
  }
23627
23680
  // Bind to current
23628
- (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "open", this._onWsOpen);
23629
- (_h = current.addEventListener) === null || _h === void 0 ? void 0 : _h.call(current, "close", this._onWsClose);
23681
+ (_f = current.addEventListener) === null || _f === void 0 ? void 0 : _f.call(current, "open", this._onWsOpen);
23682
+ (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "close", this._onWsClose);
23630
23683
  this._boundTransport = current;
23631
23684
  }
23632
- // conservative media-evidence check (no stats involved)
23633
- hasMediaEvidence() {
23634
- var _a, _b, _c;
23635
- 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);
23636
- if (!videoEl)
23637
- return false;
23638
- const dimensions = videoEl.videoWidth > 0 && videoEl.videoHeight > 0;
23639
- const ready = videoEl.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // >= 2
23640
- const activelyPlaying = !videoEl.paused && !videoEl.ended;
23641
- // Require at least two signals to avoid false positives on metadata-only loads
23642
- const strong = (dimensions && ready) || (dimensions && activelyPlaying) || (ready && activelyPlaying);
23643
- // also allow explicit 'playing' evidence we latched separately
23644
- return strong || this.seenVideoPlaying;
23645
- }
23646
- startVideoInitWatchdog() {
23647
- // restart the interval
23648
- this.stopVideoInitWatchdog();
23649
- const startedAt = Date.now();
23650
- this.videoInitWatchdogInterval = window.setInterval(() => {
23651
- if (this.videoInitializedSent) {
23652
- this.stopVideoInitWatchdog();
23653
- return;
23654
- }
23655
- if (this.hasMediaEvidence()) {
23656
- this.sendVideoInitializedOnce();
23657
- this.stopVideoInitWatchdog();
23658
- return;
23659
- }
23660
- // Optional maximum wait: if set and exceeded, backfill only if we saw playing
23661
- if (this.videoInitMaxWaitMs && Date.now() - startedAt > this.videoInitMaxWaitMs) {
23662
- if (this.seenVideoPlaying && !this.videoInitializedSent) {
23663
- this.sendVideoInitializedOnce();
23664
- }
23665
- this.stopVideoInitWatchdog();
23666
- }
23667
- }, this.videoInitCheckMs);
23668
- }
23669
- stopVideoInitWatchdog() {
23670
- if (this.videoInitWatchdogInterval) {
23671
- window.clearInterval(this.videoInitWatchdogInterval);
23672
- this.videoInitWatchdogInterval = undefined;
23673
- }
23674
- }
23675
- startTransportWatcher() {
23676
- // Rebind if the transport object reference changes (cheap safety net)
23677
- if (this._transportWatchTimer)
23678
- return;
23679
- this._transportWatchTimer = window.setInterval(() => {
23680
- var _a;
23681
- const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23682
- if (cur && cur !== this._boundTransport)
23683
- this.bindTransportEvents();
23684
- }, 3000); // light-touch; adjust if you like
23685
- }
23686
- stopTransportWatcher() {
23687
- if (this._transportWatchTimer) {
23688
- window.clearInterval(this._transportWatchTimer);
23689
- this._transportWatchTimer = undefined;
23690
- }
23691
- }
23692
- bindVideoElPlayingOnce() {
23693
- var _a, _b, _c;
23694
- 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);
23695
- if (!videoEl || videoEl === this._boundVideoEl)
23696
- return;
23697
- // Unbind older one if replaced
23698
- if (this._boundVideoEl && this._onVideoPlaying) {
23699
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
23700
- }
23701
- this._onVideoPlaying = () => {
23702
- this.seenVideoPlaying = true;
23703
- };
23704
- videoEl.addEventListener("playing", this._onVideoPlaying, { once: true });
23705
- this._boundVideoEl = videoEl;
23706
- }
23707
23685
  get isWsOpen() {
23708
23686
  var _a, _b;
23709
23687
  const ws = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.webSocket;
@@ -23724,7 +23702,7 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23724
23702
  return state !== undefined ? state : WebSocket.CLOSED;
23725
23703
  }
23726
23704
  constructor(config, overrides) {
23727
- var _a, _b, _c, _d, _f, _g, _h;
23705
+ var _a, _b, _c;
23728
23706
  /** As soon as upstream is fixed, we got to adjust the url building process. */
23729
23707
  (0,_ApplyUrlHack__WEBPACK_IMPORTED_MODULE_6__.ApplyUrlHack)();
23730
23708
  super(config, overrides);
@@ -23732,25 +23710,18 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23732
23710
  this.isProcessingQueue = false;
23733
23711
  // --- reliable send / idempotence ---
23734
23712
  this.outbox = [];
23735
- this.retryTimers = new Map(); // reserved if you later add ACKs
23736
23713
  this.videoInitializedSent = false;
23737
- this.versionReplyInFlight = false;
23738
- this.nextMsgId = 1;
23739
- this.videoInitCheckMs = 250; // default poll; can be overridden from config
23740
- this.seenVideoPlaying = false;
23714
+ // --- first-frame (rVFC) detection + element swap handling ---
23715
+ this._rVFCsupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype;
23716
+ this._postInitSideEffectsDone = false;
23741
23717
  // Externalized
23742
- /** On ping the session creation timestamp will be updated. */
23743
23718
  this.queueHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23744
- /** Error receiver. */
23745
23719
  this.errorHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23746
- /** LoveLetter */
23747
23720
  this.loveLetterHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23748
- /** SessionId */
23749
23721
  this.sessionIdHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23750
- /** VideoInitialized */
23722
+ /** VideoInitialized (native Epic event) */
23751
23723
  this.videoInitializedHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23752
23724
  // Internal
23753
- /** WebSocket Close */
23754
23725
  this.websocketOnCloseHandler = new _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler();
23755
23726
  /** 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"
23756
23727
  * Instead we use ApplyUrlHack();
@@ -23763,19 +23734,15 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23763
23734
  const transport = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23764
23735
  (_b = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _b === void 0 ? void 0 : _b.call(transport, "open", () => {
23765
23736
  this.flushOutbox();
23766
- this.startVideoInitWatchdog(); // only start, no send
23737
+ // no polling watchdog
23767
23738
  });
23768
23739
  (_c = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _c === void 0 ? void 0 : _c.call(transport, "close", (evt) => {
23769
- // Bubble to your public close handler if you expose one
23770
23740
  if (this.websocketOnCloseHandler) {
23771
23741
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23772
23742
  }
23773
23743
  });
23774
- this.setupVideoInitFallbacks();
23775
23744
  // Set override config.
23776
23745
  this.config = config;
23777
- this.videoInitCheckMs = (_f = (_d = this.config.settings) === null || _d === void 0 ? void 0 : _d.videoInitCheckMs) !== null && _f !== void 0 ? _f : this.videoInitCheckMs;
23778
- this.videoInitMaxWaitMs = (_h = (_g = this.config.settings) === null || _g === void 0 ? void 0 : _g.videoInitMaxWaitMs) !== null && _h !== void 0 ? _h : undefined;
23779
23746
  this.loveLettersList = [];
23780
23747
  this.microphoneOverlay = new _ui_MicrophoneOverlay__WEBPACK_IMPORTED_MODULE_7__.MicrophoneOverlay(this);
23781
23748
  this.diagnosticsCollector = new _features_DiagnosticsCollector__WEBPACK_IMPORTED_MODULE_3__.DiagnosticsCollector({
@@ -23784,9 +23751,9 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23784
23751
  // after super(...) and once webRtcController is available
23785
23752
  this.bindTransportEvents();
23786
23753
  this.startTransportWatcher();
23787
- // ensure video 'playing' fallback is attached to the current element
23788
- this.bindVideoElPlayingOnce();
23789
- // this.loveLettersContainer = null;
23754
+ // Arm first-frame detector & watch for element swaps
23755
+ this.attachFirstRenderedFrameOnce();
23756
+ this.watchVideoElementReplacement();
23790
23757
  this.wrapWebSocketOnCloseHandler();
23791
23758
  // Bind the event listener function to the class instance
23792
23759
  this.handleResolutionChange = this.handleResolutionChange.bind(this);
@@ -23854,14 +23821,13 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23854
23821
  (_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}`);
23855
23822
  }
23856
23823
  finally {
23857
- // Build payload with required fields only; append diagnostics if available
23858
23824
  const payload = {
23859
23825
  type: "version",
23860
23826
  version: this.config.VERSION
23861
23827
  };
23862
23828
  if (diagnostics !== undefined)
23863
23829
  payload.diagnostics = diagnostics;
23864
- this.send(payload); // uses your buffered send
23830
+ this.send(payload); // uses buffered send
23865
23831
  }
23866
23832
  });
23867
23833
  }
@@ -23874,14 +23840,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23874
23840
  this.session.set(this.session.current);
23875
23841
  }
23876
23842
  /** Handle incoming configurations. */
23877
- // public readonly streamInfoHandler = new EventHandler<never>();
23878
23843
  onStreamInfo(streamInfo) {
23879
23844
  var _a, _b, _c, _d;
23880
23845
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(`StreamInfo received.`);
23881
- // Save the streamInfo to the instance variable
23882
23846
  this.streamInfo = streamInfo;
23883
- // By setting this hidden flag, the evaluation of the streamInfo can be cut off.
23884
- // Cutting of the streamInfo can lead to unwanted behavior, therefore it's not advertised.
23885
23847
  if (!((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.settings) === null || _b === void 0 ? void 0 : _b["do-not-eval-streamInfo"])) {
23886
23848
  const { afk } = streamInfo.streamInfo;
23887
23849
  if (afk) {
@@ -23891,7 +23853,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23891
23853
  const { afkController } = this.webRtcController;
23892
23854
  afkController.countDown = afk.action;
23893
23855
  }
23894
- // Relevant settings need to be interpreted when they come through the streamInfo to the client.
23895
23856
  if ((_c = streamInfo.streamInfo.webSdkSettings) === null || _c === void 0 ? void 0 : _c.init) {
23896
23857
  this.config.setSettings(streamInfo.streamInfo.webSdkSettings.init);
23897
23858
  }
@@ -23906,7 +23867,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23906
23867
  }
23907
23868
  this.handleMouseLock();
23908
23869
  this.injectCustomUI();
23909
- // EventHandler.Emit(this.streamInfoHandler, undefined as never);
23910
23870
  }
23911
23871
  onQueue(message) {
23912
23872
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(`QueueInfo received.`);
@@ -23917,7 +23877,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23917
23877
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.errorHandler, error);
23918
23878
  }
23919
23879
  onLoveLetter(loveLetter) {
23920
- // Logger.Info(Logger.GetStackTrace(), loveLetter.reason);
23921
23880
  if (this.config.settings.loveLetterLogging)
23922
23881
  console.info(loveLetter.reason);
23923
23882
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.loveLetterHandler, loveLetter);
@@ -23925,7 +23884,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23925
23884
  }
23926
23885
  onSessionId(message) {
23927
23886
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Info(message.sessionId);
23928
- // console.info(`Session: ${message.sessionId}`);
23929
23887
  this.session.set(message.sessionId);
23930
23888
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23931
23889
  }
@@ -23933,59 +23891,112 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23933
23891
  if (this.videoInitializedSent)
23934
23892
  return;
23935
23893
  this.videoInitializedSent = true;
23936
- // Use withId if you plan to support idempotence on the server
23937
- this.send(/* this.withId( */ { type: "onVideoInitialized" } /* ) */);
23894
+ // cancel rVFC if still armed (we've already initialized)
23895
+ this.cancelFirstRenderedFrameHook();
23896
+ this.send({ type: "onVideoInitialized" });
23938
23897
  if (this.videoInitializedHandler) {
23939
23898
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.videoInitializedHandler, undefined);
23940
23899
  }
23941
23900
  }
23942
- // Attach fallbacks once (call this in constructor after transport wiring)
23943
- setupVideoInitFallbacks() {
23944
- var _a, _b, _c, _d, _f, _g, _h;
23945
- // Fallback #1: HTMLVideoElement 'playing'
23946
- 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);
23947
- (_d = videoEl === null || videoEl === void 0 ? void 0 : videoEl.addEventListener) === null || _d === void 0 ? void 0 : _d.call(videoEl, "playing", () => {
23948
- this.seenVideoPlaying = true;
23949
- }, { once: true });
23950
- (_f = this.addEventListener) === null || _f === void 0 ? void 0 : _f.call(this, "statsReceived", (_e) => {
23951
- /* no-op for video init */
23952
- });
23953
- // Fallback #3: after socket opens, give it a tick
23954
- const transport = (_g = this.webRtcController) === null || _g === void 0 ? void 0 : _g.transport;
23955
- (_h = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _h === void 0 ? void 0 : _h.call(transport, "open", () => {
23956
- this.startVideoInitWatchdog(); // only start, no send
23957
- });
23958
- }
23959
- onVideoInitialized() {
23960
- var _a, _b, _c, _d, _f, _g, _h;
23961
- // send early and once
23962
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
23963
- this.sendVideoInitializedOnce();
23964
- // keep side-effects guarded so they can't break the send
23901
+ runPostInitSideEffectsOnce() {
23902
+ var _a, _b, _c, _d, _e, _f, _g;
23903
+ if (this._postInitSideEffectsDone)
23904
+ return;
23905
+ this._postInitSideEffectsDone = true;
23965
23906
  try {
23966
23907
  (_a = this.handleMouseLock) === null || _a === void 0 ? void 0 : _a.call(this);
23967
23908
  }
23968
- catch (_j) { }
23909
+ catch (_h) { }
23969
23910
  try {
23970
23911
  (_b = this.handleResolutionChange) === null || _b === void 0 ? void 0 : _b.call(this);
23971
23912
  }
23972
- catch (_k) { }
23913
+ catch (_j) { }
23973
23914
  try {
23974
23915
  (_c = this.handleRemoveLoveLetters) === null || _c === void 0 ? void 0 : _c.call(this);
23975
23916
  }
23917
+ catch (_k) { }
23918
+ try {
23919
+ (_e = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _e === void 0 ? void 0 : _e.call(_d, false);
23920
+ }
23976
23921
  catch (_l) { }
23977
23922
  try {
23978
- (_f = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _f === void 0 ? void 0 : _f.call(_d, false);
23923
+ (_f = this.applyResolutionIfPlaying) === null || _f === void 0 ? void 0 : _f.call(this);
23979
23924
  }
23980
23925
  catch (_m) { }
23981
23926
  try {
23982
- (_g = this.applyResolutionIfPlaying) === null || _g === void 0 ? void 0 : _g.call(this);
23927
+ (_g = this.removeXRIconIfDisabled) === null || _g === void 0 ? void 0 : _g.call(this);
23983
23928
  }
23984
23929
  catch (_o) { }
23930
+ }
23931
+ onVideoInitialized() {
23932
+ if (this.videoInitializedSent) {
23933
+ // Init was already sent by rVFC; ensure side-effects run once, then bail.
23934
+ this.runPostInitSideEffectsOnce();
23935
+ return;
23936
+ }
23937
+ //console.log("Ran from Epic");
23938
+ this.sendVideoInitializedOnce();
23939
+ this.runPostInitSideEffectsOnce();
23940
+ }
23941
+ // --- First-frame detection (rVFC/resize) ---
23942
+ attachFirstRenderedFrameOnce() {
23943
+ var _a, _b, _c, _d;
23944
+ 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);
23945
+ if (!v)
23946
+ return;
23947
+ if (this._rVFCArmedForElement === v)
23948
+ return;
23949
+ this.cancelFirstRenderedFrameHook();
23950
+ this._rVFCArmedForElement = v;
23951
+ // Modern path
23952
+ if (this._rVFCsupported) {
23953
+ const anyV = v;
23954
+ this._rvfcHandle = (_d = anyV.requestVideoFrameCallback) === null || _d === void 0 ? void 0 : _d.call(anyV, (_now, _meta) => {
23955
+ this.sendVideoInitializedOnce();
23956
+ this.runPostInitSideEffectsOnce();
23957
+ this._rvfcHandle = undefined;
23958
+ });
23959
+ }
23960
+ // Always attach resize once as a parallel fallback
23961
+ const onResize = () => {
23962
+ v.removeEventListener("resize", onResize);
23963
+ this.sendVideoInitializedOnce();
23964
+ this.runPostInitSideEffectsOnce();
23965
+ };
23966
+ v.addEventListener("resize", onResize, { once: true });
23967
+ }
23968
+ cancelFirstRenderedFrameHook() {
23969
+ var _a;
23970
+ const v = this._rVFCArmedForElement;
23971
+ if (!v)
23972
+ return;
23973
+ if (this._rvfcHandle && "cancelVideoFrameCallback" in HTMLVideoElement.prototype) {
23974
+ const anyV = v;
23975
+ try {
23976
+ (_a = anyV.cancelVideoFrameCallback) === null || _a === void 0 ? void 0 : _a.call(anyV, this._rvfcHandle);
23977
+ }
23978
+ catch (_b) { }
23979
+ }
23980
+ this._rvfcHandle = undefined;
23981
+ this._rVFCArmedForElement = undefined;
23982
+ }
23983
+ watchVideoElementReplacement() {
23984
+ var _a, _b, _c, _d;
23985
+ // Observe the player container; if a new <video> is inserted/replaced, re-arm rVFC
23985
23986
  try {
23986
- (_h = this.removeXRIconIfDisabled) === null || _h === void 0 ? void 0 : _h.call(this);
23987
+ 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);
23988
+ if (!parent)
23989
+ return;
23990
+ (_d = this._mo) === null || _d === void 0 ? void 0 : _d.disconnect();
23991
+ this._mo = new MutationObserver(() => {
23992
+ // Try to re-attach on any child change (cheap and reliable)
23993
+ this.attachFirstRenderedFrameOnce();
23994
+ });
23995
+ this._mo.observe(parent, { childList: true, subtree: true });
23996
+ }
23997
+ catch (_e) {
23998
+ // noop
23987
23999
  }
23988
- catch (_p) { }
23989
24000
  }
23990
24001
  /** Adding a zod-safe handler. */
23991
24002
  addMessageHandler(type, zod, boundMethod) {
@@ -23994,13 +24005,12 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23994
24005
  if (parsed.success)
23995
24006
  boundMethod.call(this, parsed.data);
23996
24007
  else {
23997
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23998
24008
  const parsedError = parsed;
23999
24009
  const error = new Error(`Unexpected message content. Event:'${type}', ZodError: ${parsedError.error.message}`);
24000
24010
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Error(error.message);
24001
24011
  }
24002
24012
  if (type === "error") {
24003
- //this.disconnect();
24013
+ // this.disconnect();
24004
24014
  }
24005
24015
  });
24006
24016
  }
@@ -24011,7 +24021,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24011
24021
  */
24012
24022
  sendStats(stats) {
24013
24023
  const message = { type: "stats", stats: (0,_domain_Stats__WEBPACK_IMPORTED_MODULE_1__.Stats)(stats) };
24014
- // This cast has to be done, since Frontend does not use typescript-strict setting.
24015
24024
  this.send(message);
24016
24025
  }
24017
24026
  send(a, b) {
@@ -24052,22 +24061,18 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24052
24061
  this.signallingProtocol.sendMessage(m);
24053
24062
  }
24054
24063
  catch (_a) {
24055
- // if we fail mid-flush, put remaining back and bail
24056
24064
  this.outbox.unshift(m, ...batch.slice(batch.indexOf(m) + 1));
24057
24065
  break;
24058
24066
  }
24059
24067
  }
24060
24068
  }
24061
24069
  handleResolutionChange() {
24062
- var _a, _b, _c, _d, _f, _g, _h;
24063
- // Get the resolution from the streamInfo if available
24070
+ var _a, _b, _c, _d, _e, _f, _g;
24064
24071
  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())) {
24065
24072
  return;
24066
24073
  }
24067
- // Get the resolution from the streamInfo if available
24068
24074
  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;
24069
- 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;
24070
- // Check if resolution.dynamic is true
24075
+ 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;
24071
24076
  if (resolution && (resolution === null || resolution === void 0 ? void 0 : resolution.dynamic)) {
24072
24077
  setTimeout(() => {
24073
24078
  var _a;
@@ -24075,32 +24080,24 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24075
24080
  const videoElementParent = videoPlayer === null || videoPlayer === void 0 ? void 0 : videoPlayer.getVideoParentElement();
24076
24081
  const browserWidth = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().width;
24077
24082
  const browserHeight = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().height;
24078
- // Define maximum allowed resolution
24079
24083
  const maxWidth = resolution === null || resolution === void 0 ? void 0 : resolution.width;
24080
24084
  const maxHeight = resolution === null || resolution === void 0 ? void 0 : resolution.height;
24081
- // Calculate the aspect ratio of the browser viewport
24082
24085
  const aspectRatio = browserWidth / browserHeight;
24083
- // Calculate the new width and height based on the aspect ratio
24084
24086
  let limitedWidth, limitedHeight;
24085
24087
  if (aspectRatio > maxWidth / maxHeight) {
24086
- // Width is the limiting factor
24087
24088
  limitedWidth = maxWidth;
24088
24089
  limitedHeight = maxWidth / aspectRatio;
24089
24090
  }
24090
24091
  else {
24091
- // Height is the limiting factor
24092
24092
  limitedHeight = maxHeight;
24093
24093
  limitedWidth = maxHeight * aspectRatio;
24094
24094
  }
24095
24095
  if (videoPlayer) {
24096
- // Check Unreal Engine version
24097
24096
  if (!unrealVersion || (unrealVersion === null || unrealVersion === void 0 ? void 0 : unrealVersion.startsWith("4.27"))) {
24098
- // Use the aspect ratio limited resolution for the r.setres command
24099
24097
  const descriptor = { Console: `r.setres ${Math.round(limitedWidth)}x${Math.round(limitedHeight)}w` };
24100
24098
  (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.emitUIInteraction(descriptor);
24101
24099
  }
24102
24100
  else {
24103
- // Use the aspect ratio limited resolution for the onMatchViewportResolutionCallback
24104
24101
  videoPlayer.onMatchViewportResolutionCallback(Math.round(limitedWidth), Math.round(limitedHeight));
24105
24102
  }
24106
24103
  }
@@ -24119,21 +24116,39 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24119
24116
  }
24120
24117
  }
24121
24118
  removePlayer() {
24122
- var _a, _b, _c, _d;
24123
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
24119
+ var _a, _b, _c, _d, _e;
24124
24120
  this.stopTransportWatcher();
24125
- // Optional: unbind handlers
24121
+ // Unbind transport listeners
24126
24122
  if (this._boundTransport) {
24127
24123
  if (this._onWsOpen)
24128
24124
  (_b = (_a = this._boundTransport).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "open", this._onWsOpen);
24129
24125
  if (this._onWsClose)
24130
24126
  (_d = (_c = this._boundTransport).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, "close", this._onWsClose);
24131
24127
  }
24132
- if (this._boundVideoEl && this._onVideoPlaying) {
24133
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
24128
+ // Cancel rVFC + observer
24129
+ this.cancelFirstRenderedFrameHook();
24130
+ try {
24131
+ (_e = this._mo) === null || _e === void 0 ? void 0 : _e.disconnect();
24134
24132
  }
24133
+ catch (_f) { }
24135
24134
  this.disconnect();
24136
24135
  }
24136
+ startTransportWatcher() {
24137
+ if (this._transportWatchTimer)
24138
+ return;
24139
+ this._transportWatchTimer = window.setInterval(() => {
24140
+ var _a;
24141
+ const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
24142
+ if (cur && cur !== this._boundTransport)
24143
+ this.bindTransportEvents();
24144
+ }, 3000);
24145
+ }
24146
+ stopTransportWatcher() {
24147
+ if (this._transportWatchTimer) {
24148
+ window.clearInterval(this._transportWatchTimer);
24149
+ this._transportWatchTimer = undefined;
24150
+ }
24151
+ }
24137
24152
  handleMouseLock() {
24138
24153
  var _a, _b, _c;
24139
24154
  (_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));
@@ -24165,31 +24180,25 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24165
24180
  }
24166
24181
  pushLetter(letter) {
24167
24182
  var _a;
24168
- // Add the letter to the queue
24169
24183
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersQueue) === null || _a === void 0 ? void 0 : _a.push(letter);
24170
- // If not already processing the queue, start processing
24171
24184
  if (!this.isProcessingQueue) {
24172
24185
  this === null || this === void 0 ? void 0 : this.processLoveLetterQueue();
24173
24186
  }
24174
24187
  }
24175
24188
  processLoveLetterQueue() {
24176
24189
  var _a, _b;
24177
- // Set the flag to indicate that we are processing the queue
24178
24190
  this.isProcessingQueue = true;
24179
- // Take the first item from the queue
24180
24191
  const letter = this.loveLettersQueue.shift();
24181
24192
  if (letter !== undefined) {
24182
24193
  const formattedLoveLetter = letter === null || letter === void 0 ? void 0 : letter.replace(/LL: |\.$/g, "");
24183
24194
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _a === void 0 ? void 0 : _a.push(formattedLoveLetter);
24184
24195
  const loveLettersBox = new _ui_LoveLetters__WEBPACK_IMPORTED_MODULE_13__.LoveLetters();
24185
24196
  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);
24186
- // Process the next item in the queue after a delay
24187
24197
  setTimeout(() => {
24188
24198
  this.processLoveLetterQueue();
24189
24199
  }, 1000);
24190
24200
  }
24191
24201
  else {
24192
- // If the queue is empty, reset the flag
24193
24202
  this.isProcessingQueue = false;
24194
24203
  }
24195
24204
  }
@@ -24231,9 +24240,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24231
24240
  (_b = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _b === void 0 ? void 0 : _b.toggleMessage(enable);
24232
24241
  setTimeout(() => { var _a; return (_a = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _a === void 0 ? void 0 : _a.toggleVisibility(true); }, 200);
24233
24242
  setTimeout(() => {
24234
- this.videoInitializedSent = false; // <-- reset before a real media restart
24235
- this.bindVideoElPlayingOnce(); // in case a new element will appear
24236
- this.bindTransportEvents(); // cheap rebind now
24243
+ this.videoInitializedSent = false; // reset before a real media restart
24244
+ // Re-arm rVFC on the current (or new) video element
24245
+ this.attachFirstRenderedFrameOnce();
24246
+ this.bindTransportEvents();
24237
24247
  this.reconnect();
24238
24248
  }, 1000);
24239
24249
  }
@@ -24247,12 +24257,10 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24247
24257
  }
24248
24258
  onStreamingStateChange(callback) {
24249
24259
  var _a, _b, _c, _d;
24250
- // Listen to video element events
24251
24260
  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();
24252
24261
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("play", () => callback(true));
24253
24262
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("pause", () => callback(false));
24254
24263
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("ended", () => callback(false));
24255
- // Listen to WebSocket events
24256
24264
  (_c = this.webRtcController) === null || _c === void 0 ? void 0 : _c.transport.addEventListener("open", () => {
24257
24265
  callback(true);
24258
24266
  });
@@ -24261,12 +24269,12 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
24261
24269
  });
24262
24270
  }
24263
24271
  removeXRIconIfDisabled() {
24264
- var _a, _b, _c, _d, _f;
24272
+ var _a, _b, _c, _d, _e;
24265
24273
  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)) {
24266
24274
  (_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);
24267
24275
  if (this.videoElementParent) {
24268
24276
  if ((_d = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _d === void 0 ? void 0 : _d.parentElement) {
24269
- const xrBtn = (_f = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _f === void 0 ? void 0 : _f.parentElement.querySelector("#xrBtn");
24277
+ const xrBtn = (_e = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _e === void 0 ? void 0 : _e.parentElement.querySelector("#xrBtn");
24270
24278
  xrBtn === null || xrBtn === void 0 ? void 0 : xrBtn.remove();
24271
24279
  }
24272
24280
  }
@@ -24412,7 +24420,7 @@ class ConnectionIdentifier {
24412
24420
  return;
24413
24421
  const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24414
24422
  if (activeStates.length === 1) {
24415
- console.log(`PixelStreaming Instance is connected.`);
24423
+ //console.log(`PixelStreaming Instance is connected.`);
24416
24424
  }
24417
24425
  else if (activeStates.length !== 0) {
24418
24426
  console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);