@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.cjs.js CHANGED
@@ -23420,7 +23420,7 @@ class ArcwareConfig extends lib_pixelstreamingfrontend_ue5_5_1.Config {
23420
23420
  if (!config.initialSettings.ss)
23421
23421
  config.initialSettings.ss = exports.DefaultUrl;
23422
23422
  super(config);
23423
- this.VERSION = "1.3.4";
23423
+ this.VERSION = "1.3.7";
23424
23424
  this.settings = settings;
23425
23425
  this.session = new Session_1.Session();
23426
23426
  this._initialSettings = config.initialSettings;
@@ -23574,8 +23574,33 @@ const MicrophoneOverlay_1 = __webpack_require__(3613);
23574
23574
  const ConnectionIdentifier_1 = __webpack_require__(5999);
23575
23575
  const DiagnosticsCollector_1 = __webpack_require__(8429);
23576
23576
  class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStreaming {
23577
+ resetInitGuardsAndHooks() {
23578
+ this.videoInitializedSent = false;
23579
+ this._postInitSideEffectsDone = false;
23580
+ this.cancelFirstRenderedFrameHook(); // just in case
23581
+ this.attachFirstRenderedFrameOnce(); // re-arm rVFC/resize on current video element
23582
+ this.bindTransportEvents(); // ensure listeners are bound to current transport
23583
+ }
23584
+ reconnect() {
23585
+ super.reconnect();
23586
+ this.resetInitGuardsAndHooks();
23587
+ }
23588
+ // Classify CloseEvent into retryable vs. legit (non-retryable)
23589
+ isRetryableClose(evt) {
23590
+ //const reason = (evt.reason || "").toLowerCase();
23591
+ if (evt.code >= 4000 /*||
23592
+ reason.includes("Stream disconnected.") ||
23593
+ reason.includes("forbidden") ||
23594
+ reason.includes("invalid token") ||
23595
+ reason.includes("policy violation") ||
23596
+ reason.includes("not allowed")*/) {
23597
+ return false; // legit server-side rejection: do NOT auto-retry
23598
+ }
23599
+ // Transient/server/network issues: keep auto behavior
23600
+ return true;
23601
+ }
23577
23602
  bindTransportEvents() {
23578
- var _a, _b, _c, _d, _f, _g, _h;
23603
+ var _a, _b, _c, _d, _e, _f, _g;
23579
23604
  const current = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23580
23605
  if (!current || current === this._boundTransport)
23581
23606
  return;
@@ -23584,101 +23609,54 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23584
23609
  if (this._onWsOpen)
23585
23610
  (_c = (_b = this._boundTransport).removeEventListener) === null || _c === void 0 ? void 0 : _c.call(_b, "open", this._onWsOpen);
23586
23611
  if (this._onWsClose)
23587
- (_f = (_d = this._boundTransport).removeEventListener) === null || _f === void 0 ? void 0 : _f.call(_d, "close", this._onWsClose);
23612
+ (_e = (_d = this._boundTransport).removeEventListener) === null || _e === void 0 ? void 0 : _e.call(_d, "close", this._onWsClose);
23588
23613
  }
23589
23614
  // Define (or reuse) callbacks
23590
23615
  if (!this._onWsOpen) {
23591
23616
  this._onWsOpen = () => {
23592
23617
  this.flushOutbox();
23593
- this.startVideoInitWatchdog(); // only start, no send
23618
+ this.attachFirstRenderedFrameOnce();
23619
+ // no watchdog anymore
23620
+ setTimeout(() => {
23621
+ var _a, _b, _c;
23622
+ if (!this.videoInitializedSent) {
23623
+ 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);
23624
+ if (v && v.videoWidth > 0 && v.videoHeight > 0 && v.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
23625
+ this.sendVideoInitializedOnce();
23626
+ this.runPostInitSideEffectsOnce();
23627
+ }
23628
+ }
23629
+ }, 1200);
23594
23630
  };
23595
23631
  }
23596
23632
  if (!this._onWsClose) {
23597
23633
  this._onWsClose = (evt) => {
23634
+ var _a, _b;
23635
+ console.log(evt.code);
23636
+ console.log(evt.reason);
23637
+ const retryable = this.isRetryableClose(evt);
23638
+ if (!retryable) {
23639
+ // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23640
+ try {
23641
+ (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, evt.reason, false);
23642
+ }
23643
+ catch (_c) { }
23644
+ // Optional: pause the transport watcher so we don't churn while idle
23645
+ this.stopTransportWatcher();
23646
+ }
23647
+ else {
23648
+ // Leave your existing auto-retry behavior for transient errors
23649
+ this.startTransportWatcher(); // (in case it was stopped)
23650
+ }
23598
23651
  // Bubble to your public close handler
23599
23652
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23600
23653
  };
23601
23654
  }
23602
23655
  // Bind to current
23603
- (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "open", this._onWsOpen);
23604
- (_h = current.addEventListener) === null || _h === void 0 ? void 0 : _h.call(current, "close", this._onWsClose);
23656
+ (_f = current.addEventListener) === null || _f === void 0 ? void 0 : _f.call(current, "open", this._onWsOpen);
23657
+ (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "close", this._onWsClose);
23605
23658
  this._boundTransport = current;
23606
23659
  }
23607
- // conservative media-evidence check (no stats involved)
23608
- hasMediaEvidence() {
23609
- var _a, _b, _c;
23610
- 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);
23611
- if (!videoEl)
23612
- return false;
23613
- const dimensions = videoEl.videoWidth > 0 && videoEl.videoHeight > 0;
23614
- const ready = videoEl.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // >= 2
23615
- const activelyPlaying = !videoEl.paused && !videoEl.ended;
23616
- // Require at least two signals to avoid false positives on metadata-only loads
23617
- const strong = (dimensions && ready) || (dimensions && activelyPlaying) || (ready && activelyPlaying);
23618
- // also allow explicit 'playing' evidence we latched separately
23619
- return strong || this.seenVideoPlaying;
23620
- }
23621
- startVideoInitWatchdog() {
23622
- // restart the interval
23623
- this.stopVideoInitWatchdog();
23624
- const startedAt = Date.now();
23625
- this.videoInitWatchdogInterval = window.setInterval(() => {
23626
- if (this.videoInitializedSent) {
23627
- this.stopVideoInitWatchdog();
23628
- return;
23629
- }
23630
- if (this.hasMediaEvidence()) {
23631
- this.sendVideoInitializedOnce();
23632
- this.stopVideoInitWatchdog();
23633
- return;
23634
- }
23635
- // Optional maximum wait: if set and exceeded, backfill only if we saw playing
23636
- if (this.videoInitMaxWaitMs && Date.now() - startedAt > this.videoInitMaxWaitMs) {
23637
- if (this.seenVideoPlaying && !this.videoInitializedSent) {
23638
- this.sendVideoInitializedOnce();
23639
- }
23640
- this.stopVideoInitWatchdog();
23641
- }
23642
- }, this.videoInitCheckMs);
23643
- }
23644
- stopVideoInitWatchdog() {
23645
- if (this.videoInitWatchdogInterval) {
23646
- window.clearInterval(this.videoInitWatchdogInterval);
23647
- this.videoInitWatchdogInterval = undefined;
23648
- }
23649
- }
23650
- startTransportWatcher() {
23651
- // Rebind if the transport object reference changes (cheap safety net)
23652
- if (this._transportWatchTimer)
23653
- return;
23654
- this._transportWatchTimer = window.setInterval(() => {
23655
- var _a;
23656
- const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23657
- if (cur && cur !== this._boundTransport)
23658
- this.bindTransportEvents();
23659
- }, 3000); // light-touch; adjust if you like
23660
- }
23661
- stopTransportWatcher() {
23662
- if (this._transportWatchTimer) {
23663
- window.clearInterval(this._transportWatchTimer);
23664
- this._transportWatchTimer = undefined;
23665
- }
23666
- }
23667
- bindVideoElPlayingOnce() {
23668
- var _a, _b, _c;
23669
- 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);
23670
- if (!videoEl || videoEl === this._boundVideoEl)
23671
- return;
23672
- // Unbind older one if replaced
23673
- if (this._boundVideoEl && this._onVideoPlaying) {
23674
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
23675
- }
23676
- this._onVideoPlaying = () => {
23677
- this.seenVideoPlaying = true;
23678
- };
23679
- videoEl.addEventListener("playing", this._onVideoPlaying, { once: true });
23680
- this._boundVideoEl = videoEl;
23681
- }
23682
23660
  get isWsOpen() {
23683
23661
  var _a, _b;
23684
23662
  const ws = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.webSocket;
@@ -23699,7 +23677,7 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23699
23677
  return state !== undefined ? state : WebSocket.CLOSED;
23700
23678
  }
23701
23679
  constructor(config, overrides) {
23702
- var _a, _b, _c, _d, _f, _g, _h;
23680
+ var _a, _b, _c;
23703
23681
  /** As soon as upstream is fixed, we got to adjust the url building process. */
23704
23682
  (0, ApplyUrlHack_1.ApplyUrlHack)();
23705
23683
  super(config, overrides);
@@ -23707,25 +23685,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23707
23685
  this.isProcessingQueue = false;
23708
23686
  // --- reliable send / idempotence ---
23709
23687
  this.outbox = [];
23710
- this.retryTimers = new Map(); // reserved if you later add ACKs
23711
23688
  this.videoInitializedSent = false;
23712
- this.versionReplyInFlight = false;
23713
- this.nextMsgId = 1;
23714
- this.videoInitCheckMs = 250; // default poll; can be overridden from config
23715
- this.seenVideoPlaying = false;
23689
+ // --- first-frame (rVFC) detection + element swap handling ---
23690
+ this._rVFCsupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype;
23691
+ this._postInitSideEffectsDone = false;
23716
23692
  // Externalized
23717
- /** On ping the session creation timestamp will be updated. */
23718
23693
  this.queueHandler = new EventHandler_1.EventHandler();
23719
- /** Error receiver. */
23720
23694
  this.errorHandler = new EventHandler_1.EventHandler();
23721
- /** LoveLetter */
23722
23695
  this.loveLetterHandler = new EventHandler_1.EventHandler();
23723
- /** SessionId */
23724
23696
  this.sessionIdHandler = new EventHandler_1.EventHandler();
23725
- /** VideoInitialized */
23697
+ /** VideoInitialized (native Epic event) */
23726
23698
  this.videoInitializedHandler = new EventHandler_1.EventHandler();
23727
23699
  // Internal
23728
- /** WebSocket Close */
23729
23700
  this.websocketOnCloseHandler = new EventHandler_1.EventHandler();
23730
23701
  /** 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"
23731
23702
  * Instead we use ApplyUrlHack();
@@ -23738,19 +23709,15 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23738
23709
  const transport = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23739
23710
  (_b = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _b === void 0 ? void 0 : _b.call(transport, "open", () => {
23740
23711
  this.flushOutbox();
23741
- this.startVideoInitWatchdog(); // only start, no send
23712
+ // no polling watchdog
23742
23713
  });
23743
23714
  (_c = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _c === void 0 ? void 0 : _c.call(transport, "close", (evt) => {
23744
- // Bubble to your public close handler if you expose one
23745
23715
  if (this.websocketOnCloseHandler) {
23746
23716
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23747
23717
  }
23748
23718
  });
23749
- this.setupVideoInitFallbacks();
23750
23719
  // Set override config.
23751
23720
  this.config = config;
23752
- this.videoInitCheckMs = (_f = (_d = this.config.settings) === null || _d === void 0 ? void 0 : _d.videoInitCheckMs) !== null && _f !== void 0 ? _f : this.videoInitCheckMs;
23753
- this.videoInitMaxWaitMs = (_h = (_g = this.config.settings) === null || _g === void 0 ? void 0 : _g.videoInitMaxWaitMs) !== null && _h !== void 0 ? _h : undefined;
23754
23721
  this.loveLettersList = [];
23755
23722
  this.microphoneOverlay = new MicrophoneOverlay_1.MicrophoneOverlay(this);
23756
23723
  this.diagnosticsCollector = new DiagnosticsCollector_1.DiagnosticsCollector({
@@ -23759,9 +23726,9 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23759
23726
  // after super(...) and once webRtcController is available
23760
23727
  this.bindTransportEvents();
23761
23728
  this.startTransportWatcher();
23762
- // ensure video 'playing' fallback is attached to the current element
23763
- this.bindVideoElPlayingOnce();
23764
- // this.loveLettersContainer = null;
23729
+ // Arm first-frame detector & watch for element swaps
23730
+ this.attachFirstRenderedFrameOnce();
23731
+ this.watchVideoElementReplacement();
23765
23732
  this.wrapWebSocketOnCloseHandler();
23766
23733
  // Bind the event listener function to the class instance
23767
23734
  this.handleResolutionChange = this.handleResolutionChange.bind(this);
@@ -23829,14 +23796,13 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23829
23796
  (_a = lib_pixelstreamingfrontend_ue5_5_1.Logger.Warning) === null || _a === void 0 ? void 0 : _a.call(lib_pixelstreamingfrontend_ue5_5_1.Logger, `Diagnostics collection failed: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
23830
23797
  }
23831
23798
  finally {
23832
- // Build payload with required fields only; append diagnostics if available
23833
23799
  const payload = {
23834
23800
  type: "version",
23835
23801
  version: this.config.VERSION
23836
23802
  };
23837
23803
  if (diagnostics !== undefined)
23838
23804
  payload.diagnostics = diagnostics;
23839
- this.send(payload); // uses your buffered send
23805
+ this.send(payload); // uses buffered send
23840
23806
  }
23841
23807
  });
23842
23808
  }
@@ -23849,14 +23815,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23849
23815
  this.session.set(this.session.current);
23850
23816
  }
23851
23817
  /** Handle incoming configurations. */
23852
- // public readonly streamInfoHandler = new EventHandler<never>();
23853
23818
  onStreamInfo(streamInfo) {
23854
23819
  var _a, _b, _c, _d;
23855
23820
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`StreamInfo received.`);
23856
- // Save the streamInfo to the instance variable
23857
23821
  this.streamInfo = streamInfo;
23858
- // By setting this hidden flag, the evaluation of the streamInfo can be cut off.
23859
- // Cutting of the streamInfo can lead to unwanted behavior, therefore it's not advertised.
23860
23822
  if (!((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.settings) === null || _b === void 0 ? void 0 : _b["do-not-eval-streamInfo"])) {
23861
23823
  const { afk } = streamInfo.streamInfo;
23862
23824
  if (afk) {
@@ -23866,7 +23828,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23866
23828
  const { afkController } = this.webRtcController;
23867
23829
  afkController.countDown = afk.action;
23868
23830
  }
23869
- // Relevant settings need to be interpreted when they come through the streamInfo to the client.
23870
23831
  if ((_c = streamInfo.streamInfo.webSdkSettings) === null || _c === void 0 ? void 0 : _c.init) {
23871
23832
  this.config.setSettings(streamInfo.streamInfo.webSdkSettings.init);
23872
23833
  }
@@ -23881,7 +23842,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23881
23842
  }
23882
23843
  this.handleMouseLock();
23883
23844
  this.injectCustomUI();
23884
- // EventHandler.Emit(this.streamInfoHandler, undefined as never);
23885
23845
  }
23886
23846
  onQueue(message) {
23887
23847
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`QueueInfo received.`);
@@ -23892,7 +23852,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23892
23852
  EventHandler_1.EventHandler.Emit(this.errorHandler, error);
23893
23853
  }
23894
23854
  onLoveLetter(loveLetter) {
23895
- // Logger.Info(Logger.GetStackTrace(), loveLetter.reason);
23896
23855
  if (this.config.settings.loveLetterLogging)
23897
23856
  console.info(loveLetter.reason);
23898
23857
  EventHandler_1.EventHandler.Emit(this.loveLetterHandler, loveLetter);
@@ -23900,7 +23859,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23900
23859
  }
23901
23860
  onSessionId(message) {
23902
23861
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(message.sessionId);
23903
- // console.info(`Session: ${message.sessionId}`);
23904
23862
  this.session.set(message.sessionId);
23905
23863
  EventHandler_1.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23906
23864
  }
@@ -23908,59 +23866,112 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23908
23866
  if (this.videoInitializedSent)
23909
23867
  return;
23910
23868
  this.videoInitializedSent = true;
23911
- // Use withId if you plan to support idempotence on the server
23912
- this.send(/* this.withId( */ { type: "onVideoInitialized" } /* ) */);
23869
+ // cancel rVFC if still armed (we've already initialized)
23870
+ this.cancelFirstRenderedFrameHook();
23871
+ this.send({ type: "onVideoInitialized" });
23913
23872
  if (this.videoInitializedHandler) {
23914
23873
  EventHandler_1.EventHandler.Emit(this.videoInitializedHandler, undefined);
23915
23874
  }
23916
23875
  }
23917
- // Attach fallbacks once (call this in constructor after transport wiring)
23918
- setupVideoInitFallbacks() {
23919
- var _a, _b, _c, _d, _f, _g, _h;
23920
- // Fallback #1: HTMLVideoElement 'playing'
23921
- 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);
23922
- (_d = videoEl === null || videoEl === void 0 ? void 0 : videoEl.addEventListener) === null || _d === void 0 ? void 0 : _d.call(videoEl, "playing", () => {
23923
- this.seenVideoPlaying = true;
23924
- }, { once: true });
23925
- (_f = this.addEventListener) === null || _f === void 0 ? void 0 : _f.call(this, "statsReceived", (_e) => {
23926
- /* no-op for video init */
23927
- });
23928
- // Fallback #3: after socket opens, give it a tick
23929
- const transport = (_g = this.webRtcController) === null || _g === void 0 ? void 0 : _g.transport;
23930
- (_h = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _h === void 0 ? void 0 : _h.call(transport, "open", () => {
23931
- this.startVideoInitWatchdog(); // only start, no send
23932
- });
23933
- }
23934
- onVideoInitialized() {
23935
- var _a, _b, _c, _d, _f, _g, _h;
23936
- // send early and once
23937
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
23938
- this.sendVideoInitializedOnce();
23939
- // keep side-effects guarded so they can't break the send
23876
+ runPostInitSideEffectsOnce() {
23877
+ var _a, _b, _c, _d, _e, _f, _g;
23878
+ if (this._postInitSideEffectsDone)
23879
+ return;
23880
+ this._postInitSideEffectsDone = true;
23940
23881
  try {
23941
23882
  (_a = this.handleMouseLock) === null || _a === void 0 ? void 0 : _a.call(this);
23942
23883
  }
23943
- catch (_j) { }
23884
+ catch (_h) { }
23944
23885
  try {
23945
23886
  (_b = this.handleResolutionChange) === null || _b === void 0 ? void 0 : _b.call(this);
23946
23887
  }
23947
- catch (_k) { }
23888
+ catch (_j) { }
23948
23889
  try {
23949
23890
  (_c = this.handleRemoveLoveLetters) === null || _c === void 0 ? void 0 : _c.call(this);
23950
23891
  }
23892
+ catch (_k) { }
23893
+ try {
23894
+ (_e = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _e === void 0 ? void 0 : _e.call(_d, false);
23895
+ }
23951
23896
  catch (_l) { }
23952
23897
  try {
23953
- (_f = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _f === void 0 ? void 0 : _f.call(_d, false);
23898
+ (_f = this.applyResolutionIfPlaying) === null || _f === void 0 ? void 0 : _f.call(this);
23954
23899
  }
23955
23900
  catch (_m) { }
23956
23901
  try {
23957
- (_g = this.applyResolutionIfPlaying) === null || _g === void 0 ? void 0 : _g.call(this);
23902
+ (_g = this.removeXRIconIfDisabled) === null || _g === void 0 ? void 0 : _g.call(this);
23958
23903
  }
23959
23904
  catch (_o) { }
23905
+ }
23906
+ onVideoInitialized() {
23907
+ if (this.videoInitializedSent) {
23908
+ // Init was already sent by rVFC; ensure side-effects run once, then bail.
23909
+ this.runPostInitSideEffectsOnce();
23910
+ return;
23911
+ }
23912
+ //console.log("Ran from Epic");
23913
+ this.sendVideoInitializedOnce();
23914
+ this.runPostInitSideEffectsOnce();
23915
+ }
23916
+ // --- First-frame detection (rVFC/resize) ---
23917
+ attachFirstRenderedFrameOnce() {
23918
+ var _a, _b, _c, _d;
23919
+ 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);
23920
+ if (!v)
23921
+ return;
23922
+ if (this._rVFCArmedForElement === v)
23923
+ return;
23924
+ this.cancelFirstRenderedFrameHook();
23925
+ this._rVFCArmedForElement = v;
23926
+ // Modern path
23927
+ if (this._rVFCsupported) {
23928
+ const anyV = v;
23929
+ this._rvfcHandle = (_d = anyV.requestVideoFrameCallback) === null || _d === void 0 ? void 0 : _d.call(anyV, (_now, _meta) => {
23930
+ this.sendVideoInitializedOnce();
23931
+ this.runPostInitSideEffectsOnce();
23932
+ this._rvfcHandle = undefined;
23933
+ });
23934
+ }
23935
+ // Always attach resize once as a parallel fallback
23936
+ const onResize = () => {
23937
+ v.removeEventListener("resize", onResize);
23938
+ this.sendVideoInitializedOnce();
23939
+ this.runPostInitSideEffectsOnce();
23940
+ };
23941
+ v.addEventListener("resize", onResize, { once: true });
23942
+ }
23943
+ cancelFirstRenderedFrameHook() {
23944
+ var _a;
23945
+ const v = this._rVFCArmedForElement;
23946
+ if (!v)
23947
+ return;
23948
+ if (this._rvfcHandle && "cancelVideoFrameCallback" in HTMLVideoElement.prototype) {
23949
+ const anyV = v;
23950
+ try {
23951
+ (_a = anyV.cancelVideoFrameCallback) === null || _a === void 0 ? void 0 : _a.call(anyV, this._rvfcHandle);
23952
+ }
23953
+ catch (_b) { }
23954
+ }
23955
+ this._rvfcHandle = undefined;
23956
+ this._rVFCArmedForElement = undefined;
23957
+ }
23958
+ watchVideoElementReplacement() {
23959
+ var _a, _b, _c, _d;
23960
+ // Observe the player container; if a new <video> is inserted/replaced, re-arm rVFC
23960
23961
  try {
23961
- (_h = this.removeXRIconIfDisabled) === null || _h === void 0 ? void 0 : _h.call(this);
23962
+ 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);
23963
+ if (!parent)
23964
+ return;
23965
+ (_d = this._mo) === null || _d === void 0 ? void 0 : _d.disconnect();
23966
+ this._mo = new MutationObserver(() => {
23967
+ // Try to re-attach on any child change (cheap and reliable)
23968
+ this.attachFirstRenderedFrameOnce();
23969
+ });
23970
+ this._mo.observe(parent, { childList: true, subtree: true });
23971
+ }
23972
+ catch (_e) {
23973
+ // noop
23962
23974
  }
23963
- catch (_p) { }
23964
23975
  }
23965
23976
  /** Adding a zod-safe handler. */
23966
23977
  addMessageHandler(type, zod, boundMethod) {
@@ -23969,13 +23980,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23969
23980
  if (parsed.success)
23970
23981
  boundMethod.call(this, parsed.data);
23971
23982
  else {
23972
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23973
23983
  const parsedError = parsed;
23974
23984
  const error = new Error(`Unexpected message content. Event:'${type}', ZodError: ${parsedError.error.message}`);
23975
23985
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Error(error.message);
23976
23986
  }
23977
23987
  if (type === "error") {
23978
- //this.disconnect();
23988
+ // this.disconnect();
23979
23989
  }
23980
23990
  });
23981
23991
  }
@@ -23986,7 +23996,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23986
23996
  */
23987
23997
  sendStats(stats) {
23988
23998
  const message = { type: "stats", stats: (0, Stats_1.Stats)(stats) };
23989
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23990
23999
  this.send(message);
23991
24000
  }
23992
24001
  send(a, b) {
@@ -24027,22 +24036,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24027
24036
  this.signallingProtocol.sendMessage(m);
24028
24037
  }
24029
24038
  catch (_a) {
24030
- // if we fail mid-flush, put remaining back and bail
24031
24039
  this.outbox.unshift(m, ...batch.slice(batch.indexOf(m) + 1));
24032
24040
  break;
24033
24041
  }
24034
24042
  }
24035
24043
  }
24036
24044
  handleResolutionChange() {
24037
- var _a, _b, _c, _d, _f, _g, _h;
24038
- // Get the resolution from the streamInfo if available
24045
+ var _a, _b, _c, _d, _e, _f, _g;
24039
24046
  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())) {
24040
24047
  return;
24041
24048
  }
24042
- // Get the resolution from the streamInfo if available
24043
24049
  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;
24044
- 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;
24045
- // Check if resolution.dynamic is true
24050
+ 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;
24046
24051
  if (resolution && (resolution === null || resolution === void 0 ? void 0 : resolution.dynamic)) {
24047
24052
  setTimeout(() => {
24048
24053
  var _a;
@@ -24050,32 +24055,24 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24050
24055
  const videoElementParent = videoPlayer === null || videoPlayer === void 0 ? void 0 : videoPlayer.getVideoParentElement();
24051
24056
  const browserWidth = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().width;
24052
24057
  const browserHeight = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().height;
24053
- // Define maximum allowed resolution
24054
24058
  const maxWidth = resolution === null || resolution === void 0 ? void 0 : resolution.width;
24055
24059
  const maxHeight = resolution === null || resolution === void 0 ? void 0 : resolution.height;
24056
- // Calculate the aspect ratio of the browser viewport
24057
24060
  const aspectRatio = browserWidth / browserHeight;
24058
- // Calculate the new width and height based on the aspect ratio
24059
24061
  let limitedWidth, limitedHeight;
24060
24062
  if (aspectRatio > maxWidth / maxHeight) {
24061
- // Width is the limiting factor
24062
24063
  limitedWidth = maxWidth;
24063
24064
  limitedHeight = maxWidth / aspectRatio;
24064
24065
  }
24065
24066
  else {
24066
- // Height is the limiting factor
24067
24067
  limitedHeight = maxHeight;
24068
24068
  limitedWidth = maxHeight * aspectRatio;
24069
24069
  }
24070
24070
  if (videoPlayer) {
24071
- // Check Unreal Engine version
24072
24071
  if (!unrealVersion || (unrealVersion === null || unrealVersion === void 0 ? void 0 : unrealVersion.startsWith("4.27"))) {
24073
- // Use the aspect ratio limited resolution for the r.setres command
24074
24072
  const descriptor = { Console: `r.setres ${Math.round(limitedWidth)}x${Math.round(limitedHeight)}w` };
24075
24073
  (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.emitUIInteraction(descriptor);
24076
24074
  }
24077
24075
  else {
24078
- // Use the aspect ratio limited resolution for the onMatchViewportResolutionCallback
24079
24076
  videoPlayer.onMatchViewportResolutionCallback(Math.round(limitedWidth), Math.round(limitedHeight));
24080
24077
  }
24081
24078
  }
@@ -24094,21 +24091,39 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24094
24091
  }
24095
24092
  }
24096
24093
  removePlayer() {
24097
- var _a, _b, _c, _d;
24098
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
24094
+ var _a, _b, _c, _d, _e;
24099
24095
  this.stopTransportWatcher();
24100
- // Optional: unbind handlers
24096
+ // Unbind transport listeners
24101
24097
  if (this._boundTransport) {
24102
24098
  if (this._onWsOpen)
24103
24099
  (_b = (_a = this._boundTransport).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "open", this._onWsOpen);
24104
24100
  if (this._onWsClose)
24105
24101
  (_d = (_c = this._boundTransport).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, "close", this._onWsClose);
24106
24102
  }
24107
- if (this._boundVideoEl && this._onVideoPlaying) {
24108
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
24103
+ // Cancel rVFC + observer
24104
+ this.cancelFirstRenderedFrameHook();
24105
+ try {
24106
+ (_e = this._mo) === null || _e === void 0 ? void 0 : _e.disconnect();
24109
24107
  }
24108
+ catch (_f) { }
24110
24109
  this.disconnect();
24111
24110
  }
24111
+ startTransportWatcher() {
24112
+ if (this._transportWatchTimer)
24113
+ return;
24114
+ this._transportWatchTimer = window.setInterval(() => {
24115
+ var _a;
24116
+ const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
24117
+ if (cur && cur !== this._boundTransport)
24118
+ this.bindTransportEvents();
24119
+ }, 3000);
24120
+ }
24121
+ stopTransportWatcher() {
24122
+ if (this._transportWatchTimer) {
24123
+ window.clearInterval(this._transportWatchTimer);
24124
+ this._transportWatchTimer = undefined;
24125
+ }
24126
+ }
24112
24127
  handleMouseLock() {
24113
24128
  var _a, _b, _c;
24114
24129
  (_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));
@@ -24140,31 +24155,25 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24140
24155
  }
24141
24156
  pushLetter(letter) {
24142
24157
  var _a;
24143
- // Add the letter to the queue
24144
24158
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersQueue) === null || _a === void 0 ? void 0 : _a.push(letter);
24145
- // If not already processing the queue, start processing
24146
24159
  if (!this.isProcessingQueue) {
24147
24160
  this === null || this === void 0 ? void 0 : this.processLoveLetterQueue();
24148
24161
  }
24149
24162
  }
24150
24163
  processLoveLetterQueue() {
24151
24164
  var _a, _b;
24152
- // Set the flag to indicate that we are processing the queue
24153
24165
  this.isProcessingQueue = true;
24154
- // Take the first item from the queue
24155
24166
  const letter = this.loveLettersQueue.shift();
24156
24167
  if (letter !== undefined) {
24157
24168
  const formattedLoveLetter = letter === null || letter === void 0 ? void 0 : letter.replace(/LL: |\.$/g, "");
24158
24169
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _a === void 0 ? void 0 : _a.push(formattedLoveLetter);
24159
24170
  const loveLettersBox = new LoveLetters_1.LoveLetters();
24160
24171
  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);
24161
- // Process the next item in the queue after a delay
24162
24172
  setTimeout(() => {
24163
24173
  this.processLoveLetterQueue();
24164
24174
  }, 1000);
24165
24175
  }
24166
24176
  else {
24167
- // If the queue is empty, reset the flag
24168
24177
  this.isProcessingQueue = false;
24169
24178
  }
24170
24179
  }
@@ -24206,9 +24215,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24206
24215
  (_b = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _b === void 0 ? void 0 : _b.toggleMessage(enable);
24207
24216
  setTimeout(() => { var _a; return (_a = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _a === void 0 ? void 0 : _a.toggleVisibility(true); }, 200);
24208
24217
  setTimeout(() => {
24209
- this.videoInitializedSent = false; // <-- reset before a real media restart
24210
- this.bindVideoElPlayingOnce(); // in case a new element will appear
24211
- this.bindTransportEvents(); // cheap rebind now
24218
+ this.videoInitializedSent = false; // reset before a real media restart
24219
+ // Re-arm rVFC on the current (or new) video element
24220
+ this.attachFirstRenderedFrameOnce();
24221
+ this.bindTransportEvents();
24212
24222
  this.reconnect();
24213
24223
  }, 1000);
24214
24224
  }
@@ -24222,12 +24232,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24222
24232
  }
24223
24233
  onStreamingStateChange(callback) {
24224
24234
  var _a, _b, _c, _d;
24225
- // Listen to video element events
24226
24235
  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();
24227
24236
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("play", () => callback(true));
24228
24237
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("pause", () => callback(false));
24229
24238
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("ended", () => callback(false));
24230
- // Listen to WebSocket events
24231
24239
  (_c = this.webRtcController) === null || _c === void 0 ? void 0 : _c.transport.addEventListener("open", () => {
24232
24240
  callback(true);
24233
24241
  });
@@ -24236,12 +24244,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24236
24244
  });
24237
24245
  }
24238
24246
  removeXRIconIfDisabled() {
24239
- var _a, _b, _c, _d, _f;
24247
+ var _a, _b, _c, _d, _e;
24240
24248
  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)) {
24241
24249
  (_c = this === null || this === void 0 ? void 0 : this.config) === null || _c === void 0 ? void 0 : _c.setFlagEnabled(lib_pixelstreamingfrontend_ue5_5_1.Flags.XRControllerInput, false);
24242
24250
  if (this.videoElementParent) {
24243
24251
  if ((_d = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _d === void 0 ? void 0 : _d.parentElement) {
24244
- const xrBtn = (_f = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _f === void 0 ? void 0 : _f.parentElement.querySelector("#xrBtn");
24252
+ const xrBtn = (_e = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _e === void 0 ? void 0 : _e.parentElement.querySelector("#xrBtn");
24245
24253
  xrBtn === null || xrBtn === void 0 ? void 0 : xrBtn.remove();
24246
24254
  }
24247
24255
  }
@@ -24383,7 +24391,7 @@ class ConnectionIdentifier {
24383
24391
  return;
24384
24392
  const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24385
24393
  if (activeStates.length === 1) {
24386
- console.log(`PixelStreaming Instance is connected.`);
24394
+ //console.log(`PixelStreaming Instance is connected.`);
24387
24395
  }
24388
24396
  else if (activeStates.length !== 0) {
24389
24397
  console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);