@arcware-cloud/pixelstreaming-websdk 1.3.4 → 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.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.6";
23424
23424
  this.settings = settings;
23425
23425
  this.session = new Session_1.Session();
23426
23426
  this._initialSettings = config.initialSettings;
@@ -23574,8 +23574,19 @@ 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
+ }
23577
23588
  bindTransportEvents() {
23578
- var _a, _b, _c, _d, _f, _g, _h;
23589
+ var _a, _b, _c, _d, _e, _f, _g;
23579
23590
  const current = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23580
23591
  if (!current || current === this._boundTransport)
23581
23592
  return;
@@ -23584,13 +23595,24 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23584
23595
  if (this._onWsOpen)
23585
23596
  (_c = (_b = this._boundTransport).removeEventListener) === null || _c === void 0 ? void 0 : _c.call(_b, "open", this._onWsOpen);
23586
23597
  if (this._onWsClose)
23587
- (_f = (_d = this._boundTransport).removeEventListener) === null || _f === void 0 ? void 0 : _f.call(_d, "close", this._onWsClose);
23598
+ (_e = (_d = this._boundTransport).removeEventListener) === null || _e === void 0 ? void 0 : _e.call(_d, "close", this._onWsClose);
23588
23599
  }
23589
23600
  // Define (or reuse) callbacks
23590
23601
  if (!this._onWsOpen) {
23591
23602
  this._onWsOpen = () => {
23592
23603
  this.flushOutbox();
23593
- this.startVideoInitWatchdog(); // only start, no send
23604
+ this.attachFirstRenderedFrameOnce();
23605
+ // no watchdog anymore
23606
+ setTimeout(() => {
23607
+ var _a, _b, _c;
23608
+ if (!this.videoInitializedSent) {
23609
+ 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);
23610
+ if (v && v.videoWidth > 0 && v.videoHeight > 0 && v.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
23611
+ this.sendVideoInitializedOnce();
23612
+ this.runPostInitSideEffectsOnce();
23613
+ }
23614
+ }
23615
+ }, 1200);
23594
23616
  };
23595
23617
  }
23596
23618
  if (!this._onWsClose) {
@@ -23600,85 +23622,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23600
23622
  };
23601
23623
  }
23602
23624
  // 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);
23625
+ (_f = current.addEventListener) === null || _f === void 0 ? void 0 : _f.call(current, "open", this._onWsOpen);
23626
+ (_g = current.addEventListener) === null || _g === void 0 ? void 0 : _g.call(current, "close", this._onWsClose);
23605
23627
  this._boundTransport = current;
23606
23628
  }
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
23629
  get isWsOpen() {
23683
23630
  var _a, _b;
23684
23631
  const ws = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.webSocket;
@@ -23699,7 +23646,7 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23699
23646
  return state !== undefined ? state : WebSocket.CLOSED;
23700
23647
  }
23701
23648
  constructor(config, overrides) {
23702
- var _a, _b, _c, _d, _f, _g, _h;
23649
+ var _a, _b, _c;
23703
23650
  /** As soon as upstream is fixed, we got to adjust the url building process. */
23704
23651
  (0, ApplyUrlHack_1.ApplyUrlHack)();
23705
23652
  super(config, overrides);
@@ -23707,25 +23654,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23707
23654
  this.isProcessingQueue = false;
23708
23655
  // --- reliable send / idempotence ---
23709
23656
  this.outbox = [];
23710
- this.retryTimers = new Map(); // reserved if you later add ACKs
23711
23657
  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;
23658
+ // --- first-frame (rVFC) detection + element swap handling ---
23659
+ this._rVFCsupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype;
23660
+ this._postInitSideEffectsDone = false;
23716
23661
  // Externalized
23717
- /** On ping the session creation timestamp will be updated. */
23718
23662
  this.queueHandler = new EventHandler_1.EventHandler();
23719
- /** Error receiver. */
23720
23663
  this.errorHandler = new EventHandler_1.EventHandler();
23721
- /** LoveLetter */
23722
23664
  this.loveLetterHandler = new EventHandler_1.EventHandler();
23723
- /** SessionId */
23724
23665
  this.sessionIdHandler = new EventHandler_1.EventHandler();
23725
- /** VideoInitialized */
23666
+ /** VideoInitialized (native Epic event) */
23726
23667
  this.videoInitializedHandler = new EventHandler_1.EventHandler();
23727
23668
  // Internal
23728
- /** WebSocket Close */
23729
23669
  this.websocketOnCloseHandler = new EventHandler_1.EventHandler();
23730
23670
  /** 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
23671
  * Instead we use ApplyUrlHack();
@@ -23738,19 +23678,15 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23738
23678
  const transport = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23739
23679
  (_b = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _b === void 0 ? void 0 : _b.call(transport, "open", () => {
23740
23680
  this.flushOutbox();
23741
- this.startVideoInitWatchdog(); // only start, no send
23681
+ // no polling watchdog
23742
23682
  });
23743
23683
  (_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
23684
  if (this.websocketOnCloseHandler) {
23746
23685
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23747
23686
  }
23748
23687
  });
23749
- this.setupVideoInitFallbacks();
23750
23688
  // Set override config.
23751
23689
  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
23690
  this.loveLettersList = [];
23755
23691
  this.microphoneOverlay = new MicrophoneOverlay_1.MicrophoneOverlay(this);
23756
23692
  this.diagnosticsCollector = new DiagnosticsCollector_1.DiagnosticsCollector({
@@ -23759,9 +23695,9 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23759
23695
  // after super(...) and once webRtcController is available
23760
23696
  this.bindTransportEvents();
23761
23697
  this.startTransportWatcher();
23762
- // ensure video 'playing' fallback is attached to the current element
23763
- this.bindVideoElPlayingOnce();
23764
- // this.loveLettersContainer = null;
23698
+ // Arm first-frame detector & watch for element swaps
23699
+ this.attachFirstRenderedFrameOnce();
23700
+ this.watchVideoElementReplacement();
23765
23701
  this.wrapWebSocketOnCloseHandler();
23766
23702
  // Bind the event listener function to the class instance
23767
23703
  this.handleResolutionChange = this.handleResolutionChange.bind(this);
@@ -23829,14 +23765,13 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23829
23765
  (_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
23766
  }
23831
23767
  finally {
23832
- // Build payload with required fields only; append diagnostics if available
23833
23768
  const payload = {
23834
23769
  type: "version",
23835
23770
  version: this.config.VERSION
23836
23771
  };
23837
23772
  if (diagnostics !== undefined)
23838
23773
  payload.diagnostics = diagnostics;
23839
- this.send(payload); // uses your buffered send
23774
+ this.send(payload); // uses buffered send
23840
23775
  }
23841
23776
  });
23842
23777
  }
@@ -23849,14 +23784,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23849
23784
  this.session.set(this.session.current);
23850
23785
  }
23851
23786
  /** Handle incoming configurations. */
23852
- // public readonly streamInfoHandler = new EventHandler<never>();
23853
23787
  onStreamInfo(streamInfo) {
23854
23788
  var _a, _b, _c, _d;
23855
23789
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`StreamInfo received.`);
23856
- // Save the streamInfo to the instance variable
23857
23790
  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
23791
  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
23792
  const { afk } = streamInfo.streamInfo;
23862
23793
  if (afk) {
@@ -23866,7 +23797,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23866
23797
  const { afkController } = this.webRtcController;
23867
23798
  afkController.countDown = afk.action;
23868
23799
  }
23869
- // Relevant settings need to be interpreted when they come through the streamInfo to the client.
23870
23800
  if ((_c = streamInfo.streamInfo.webSdkSettings) === null || _c === void 0 ? void 0 : _c.init) {
23871
23801
  this.config.setSettings(streamInfo.streamInfo.webSdkSettings.init);
23872
23802
  }
@@ -23881,7 +23811,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23881
23811
  }
23882
23812
  this.handleMouseLock();
23883
23813
  this.injectCustomUI();
23884
- // EventHandler.Emit(this.streamInfoHandler, undefined as never);
23885
23814
  }
23886
23815
  onQueue(message) {
23887
23816
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`QueueInfo received.`);
@@ -23892,7 +23821,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23892
23821
  EventHandler_1.EventHandler.Emit(this.errorHandler, error);
23893
23822
  }
23894
23823
  onLoveLetter(loveLetter) {
23895
- // Logger.Info(Logger.GetStackTrace(), loveLetter.reason);
23896
23824
  if (this.config.settings.loveLetterLogging)
23897
23825
  console.info(loveLetter.reason);
23898
23826
  EventHandler_1.EventHandler.Emit(this.loveLetterHandler, loveLetter);
@@ -23900,7 +23828,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23900
23828
  }
23901
23829
  onSessionId(message) {
23902
23830
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(message.sessionId);
23903
- // console.info(`Session: ${message.sessionId}`);
23904
23831
  this.session.set(message.sessionId);
23905
23832
  EventHandler_1.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23906
23833
  }
@@ -23908,59 +23835,112 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23908
23835
  if (this.videoInitializedSent)
23909
23836
  return;
23910
23837
  this.videoInitializedSent = true;
23911
- // Use withId if you plan to support idempotence on the server
23912
- this.send(/* this.withId( */ { type: "onVideoInitialized" } /* ) */);
23838
+ // cancel rVFC if still armed (we've already initialized)
23839
+ this.cancelFirstRenderedFrameHook();
23840
+ this.send({ type: "onVideoInitialized" });
23913
23841
  if (this.videoInitializedHandler) {
23914
23842
  EventHandler_1.EventHandler.Emit(this.videoInitializedHandler, undefined);
23915
23843
  }
23916
23844
  }
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
23845
+ runPostInitSideEffectsOnce() {
23846
+ var _a, _b, _c, _d, _e, _f, _g;
23847
+ if (this._postInitSideEffectsDone)
23848
+ return;
23849
+ this._postInitSideEffectsDone = true;
23940
23850
  try {
23941
23851
  (_a = this.handleMouseLock) === null || _a === void 0 ? void 0 : _a.call(this);
23942
23852
  }
23943
- catch (_j) { }
23853
+ catch (_h) { }
23944
23854
  try {
23945
23855
  (_b = this.handleResolutionChange) === null || _b === void 0 ? void 0 : _b.call(this);
23946
23856
  }
23947
- catch (_k) { }
23857
+ catch (_j) { }
23948
23858
  try {
23949
23859
  (_c = this.handleRemoveLoveLetters) === null || _c === void 0 ? void 0 : _c.call(this);
23950
23860
  }
23861
+ catch (_k) { }
23862
+ try {
23863
+ (_e = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _e === void 0 ? void 0 : _e.call(_d, false);
23864
+ }
23951
23865
  catch (_l) { }
23952
23866
  try {
23953
- (_f = (_d = this.microphoneOverlay) === null || _d === void 0 ? void 0 : _d.toggleVisibility) === null || _f === void 0 ? void 0 : _f.call(_d, false);
23867
+ (_f = this.applyResolutionIfPlaying) === null || _f === void 0 ? void 0 : _f.call(this);
23954
23868
  }
23955
23869
  catch (_m) { }
23956
23870
  try {
23957
- (_g = this.applyResolutionIfPlaying) === null || _g === void 0 ? void 0 : _g.call(this);
23871
+ (_g = this.removeXRIconIfDisabled) === null || _g === void 0 ? void 0 : _g.call(this);
23958
23872
  }
23959
23873
  catch (_o) { }
23874
+ }
23875
+ onVideoInitialized() {
23876
+ if (this.videoInitializedSent) {
23877
+ // Init was already sent by rVFC; ensure side-effects run once, then bail.
23878
+ this.runPostInitSideEffectsOnce();
23879
+ return;
23880
+ }
23881
+ //console.log("Ran from Epic");
23882
+ this.sendVideoInitializedOnce();
23883
+ this.runPostInitSideEffectsOnce();
23884
+ }
23885
+ // --- First-frame detection (rVFC/resize) ---
23886
+ attachFirstRenderedFrameOnce() {
23887
+ var _a, _b, _c, _d;
23888
+ 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);
23889
+ if (!v)
23890
+ return;
23891
+ if (this._rVFCArmedForElement === v)
23892
+ return;
23893
+ this.cancelFirstRenderedFrameHook();
23894
+ this._rVFCArmedForElement = v;
23895
+ // Modern path
23896
+ if (this._rVFCsupported) {
23897
+ const anyV = v;
23898
+ this._rvfcHandle = (_d = anyV.requestVideoFrameCallback) === null || _d === void 0 ? void 0 : _d.call(anyV, (_now, _meta) => {
23899
+ this.sendVideoInitializedOnce();
23900
+ this.runPostInitSideEffectsOnce();
23901
+ this._rvfcHandle = undefined;
23902
+ });
23903
+ }
23904
+ // Always attach resize once as a parallel fallback
23905
+ const onResize = () => {
23906
+ v.removeEventListener("resize", onResize);
23907
+ this.sendVideoInitializedOnce();
23908
+ this.runPostInitSideEffectsOnce();
23909
+ };
23910
+ v.addEventListener("resize", onResize, { once: true });
23911
+ }
23912
+ cancelFirstRenderedFrameHook() {
23913
+ var _a;
23914
+ const v = this._rVFCArmedForElement;
23915
+ if (!v)
23916
+ return;
23917
+ if (this._rvfcHandle && "cancelVideoFrameCallback" in HTMLVideoElement.prototype) {
23918
+ const anyV = v;
23919
+ try {
23920
+ (_a = anyV.cancelVideoFrameCallback) === null || _a === void 0 ? void 0 : _a.call(anyV, this._rvfcHandle);
23921
+ }
23922
+ catch (_b) { }
23923
+ }
23924
+ this._rvfcHandle = undefined;
23925
+ this._rVFCArmedForElement = undefined;
23926
+ }
23927
+ watchVideoElementReplacement() {
23928
+ var _a, _b, _c, _d;
23929
+ // Observe the player container; if a new <video> is inserted/replaced, re-arm rVFC
23960
23930
  try {
23961
- (_h = this.removeXRIconIfDisabled) === null || _h === void 0 ? void 0 : _h.call(this);
23931
+ 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);
23932
+ if (!parent)
23933
+ return;
23934
+ (_d = this._mo) === null || _d === void 0 ? void 0 : _d.disconnect();
23935
+ this._mo = new MutationObserver(() => {
23936
+ // Try to re-attach on any child change (cheap and reliable)
23937
+ this.attachFirstRenderedFrameOnce();
23938
+ });
23939
+ this._mo.observe(parent, { childList: true, subtree: true });
23940
+ }
23941
+ catch (_e) {
23942
+ // noop
23962
23943
  }
23963
- catch (_p) { }
23964
23944
  }
23965
23945
  /** Adding a zod-safe handler. */
23966
23946
  addMessageHandler(type, zod, boundMethod) {
@@ -23969,13 +23949,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23969
23949
  if (parsed.success)
23970
23950
  boundMethod.call(this, parsed.data);
23971
23951
  else {
23972
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23973
23952
  const parsedError = parsed;
23974
23953
  const error = new Error(`Unexpected message content. Event:'${type}', ZodError: ${parsedError.error.message}`);
23975
23954
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Error(error.message);
23976
23955
  }
23977
23956
  if (type === "error") {
23978
- //this.disconnect();
23957
+ // this.disconnect();
23979
23958
  }
23980
23959
  });
23981
23960
  }
@@ -23986,7 +23965,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23986
23965
  */
23987
23966
  sendStats(stats) {
23988
23967
  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
23968
  this.send(message);
23991
23969
  }
23992
23970
  send(a, b) {
@@ -24027,22 +24005,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24027
24005
  this.signallingProtocol.sendMessage(m);
24028
24006
  }
24029
24007
  catch (_a) {
24030
- // if we fail mid-flush, put remaining back and bail
24031
24008
  this.outbox.unshift(m, ...batch.slice(batch.indexOf(m) + 1));
24032
24009
  break;
24033
24010
  }
24034
24011
  }
24035
24012
  }
24036
24013
  handleResolutionChange() {
24037
- var _a, _b, _c, _d, _f, _g, _h;
24038
- // Get the resolution from the streamInfo if available
24014
+ var _a, _b, _c, _d, _e, _f, _g;
24039
24015
  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
24016
  return;
24041
24017
  }
24042
- // Get the resolution from the streamInfo if available
24043
24018
  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
24019
+ 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
24020
  if (resolution && (resolution === null || resolution === void 0 ? void 0 : resolution.dynamic)) {
24047
24021
  setTimeout(() => {
24048
24022
  var _a;
@@ -24050,32 +24024,24 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24050
24024
  const videoElementParent = videoPlayer === null || videoPlayer === void 0 ? void 0 : videoPlayer.getVideoParentElement();
24051
24025
  const browserWidth = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().width;
24052
24026
  const browserHeight = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().height;
24053
- // Define maximum allowed resolution
24054
24027
  const maxWidth = resolution === null || resolution === void 0 ? void 0 : resolution.width;
24055
24028
  const maxHeight = resolution === null || resolution === void 0 ? void 0 : resolution.height;
24056
- // Calculate the aspect ratio of the browser viewport
24057
24029
  const aspectRatio = browserWidth / browserHeight;
24058
- // Calculate the new width and height based on the aspect ratio
24059
24030
  let limitedWidth, limitedHeight;
24060
24031
  if (aspectRatio > maxWidth / maxHeight) {
24061
- // Width is the limiting factor
24062
24032
  limitedWidth = maxWidth;
24063
24033
  limitedHeight = maxWidth / aspectRatio;
24064
24034
  }
24065
24035
  else {
24066
- // Height is the limiting factor
24067
24036
  limitedHeight = maxHeight;
24068
24037
  limitedWidth = maxHeight * aspectRatio;
24069
24038
  }
24070
24039
  if (videoPlayer) {
24071
- // Check Unreal Engine version
24072
24040
  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
24041
  const descriptor = { Console: `r.setres ${Math.round(limitedWidth)}x${Math.round(limitedHeight)}w` };
24075
24042
  (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.emitUIInteraction(descriptor);
24076
24043
  }
24077
24044
  else {
24078
- // Use the aspect ratio limited resolution for the onMatchViewportResolutionCallback
24079
24045
  videoPlayer.onMatchViewportResolutionCallback(Math.round(limitedWidth), Math.round(limitedHeight));
24080
24046
  }
24081
24047
  }
@@ -24094,21 +24060,39 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24094
24060
  }
24095
24061
  }
24096
24062
  removePlayer() {
24097
- var _a, _b, _c, _d;
24098
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
24063
+ var _a, _b, _c, _d, _e;
24099
24064
  this.stopTransportWatcher();
24100
- // Optional: unbind handlers
24065
+ // Unbind transport listeners
24101
24066
  if (this._boundTransport) {
24102
24067
  if (this._onWsOpen)
24103
24068
  (_b = (_a = this._boundTransport).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "open", this._onWsOpen);
24104
24069
  if (this._onWsClose)
24105
24070
  (_d = (_c = this._boundTransport).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, "close", this._onWsClose);
24106
24071
  }
24107
- if (this._boundVideoEl && this._onVideoPlaying) {
24108
- this._boundVideoEl.removeEventListener("playing", this._onVideoPlaying);
24072
+ // Cancel rVFC + observer
24073
+ this.cancelFirstRenderedFrameHook();
24074
+ try {
24075
+ (_e = this._mo) === null || _e === void 0 ? void 0 : _e.disconnect();
24109
24076
  }
24077
+ catch (_f) { }
24110
24078
  this.disconnect();
24111
24079
  }
24080
+ startTransportWatcher() {
24081
+ if (this._transportWatchTimer)
24082
+ return;
24083
+ this._transportWatchTimer = window.setInterval(() => {
24084
+ var _a;
24085
+ const cur = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
24086
+ if (cur && cur !== this._boundTransport)
24087
+ this.bindTransportEvents();
24088
+ }, 3000);
24089
+ }
24090
+ stopTransportWatcher() {
24091
+ if (this._transportWatchTimer) {
24092
+ window.clearInterval(this._transportWatchTimer);
24093
+ this._transportWatchTimer = undefined;
24094
+ }
24095
+ }
24112
24096
  handleMouseLock() {
24113
24097
  var _a, _b, _c;
24114
24098
  (_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 +24124,25 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24140
24124
  }
24141
24125
  pushLetter(letter) {
24142
24126
  var _a;
24143
- // Add the letter to the queue
24144
24127
  (_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
24128
  if (!this.isProcessingQueue) {
24147
24129
  this === null || this === void 0 ? void 0 : this.processLoveLetterQueue();
24148
24130
  }
24149
24131
  }
24150
24132
  processLoveLetterQueue() {
24151
24133
  var _a, _b;
24152
- // Set the flag to indicate that we are processing the queue
24153
24134
  this.isProcessingQueue = true;
24154
- // Take the first item from the queue
24155
24135
  const letter = this.loveLettersQueue.shift();
24156
24136
  if (letter !== undefined) {
24157
24137
  const formattedLoveLetter = letter === null || letter === void 0 ? void 0 : letter.replace(/LL: |\.$/g, "");
24158
24138
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _a === void 0 ? void 0 : _a.push(formattedLoveLetter);
24159
24139
  const loveLettersBox = new LoveLetters_1.LoveLetters();
24160
24140
  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
24141
  setTimeout(() => {
24163
24142
  this.processLoveLetterQueue();
24164
24143
  }, 1000);
24165
24144
  }
24166
24145
  else {
24167
- // If the queue is empty, reset the flag
24168
24146
  this.isProcessingQueue = false;
24169
24147
  }
24170
24148
  }
@@ -24206,9 +24184,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24206
24184
  (_b = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _b === void 0 ? void 0 : _b.toggleMessage(enable);
24207
24185
  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
24186
  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
24187
+ this.videoInitializedSent = false; // reset before a real media restart
24188
+ // Re-arm rVFC on the current (or new) video element
24189
+ this.attachFirstRenderedFrameOnce();
24190
+ this.bindTransportEvents();
24212
24191
  this.reconnect();
24213
24192
  }, 1000);
24214
24193
  }
@@ -24222,12 +24201,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24222
24201
  }
24223
24202
  onStreamingStateChange(callback) {
24224
24203
  var _a, _b, _c, _d;
24225
- // Listen to video element events
24226
24204
  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
24205
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("play", () => callback(true));
24228
24206
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("pause", () => callback(false));
24229
24207
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("ended", () => callback(false));
24230
- // Listen to WebSocket events
24231
24208
  (_c = this.webRtcController) === null || _c === void 0 ? void 0 : _c.transport.addEventListener("open", () => {
24232
24209
  callback(true);
24233
24210
  });
@@ -24236,12 +24213,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24236
24213
  });
24237
24214
  }
24238
24215
  removeXRIconIfDisabled() {
24239
- var _a, _b, _c, _d, _f;
24216
+ var _a, _b, _c, _d, _e;
24240
24217
  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
24218
  (_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
24219
  if (this.videoElementParent) {
24243
24220
  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");
24221
+ const xrBtn = (_e = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _e === void 0 ? void 0 : _e.parentElement.querySelector("#xrBtn");
24245
24222
  xrBtn === null || xrBtn === void 0 ? void 0 : xrBtn.remove();
24246
24223
  }
24247
24224
  }
@@ -24383,7 +24360,7 @@ class ConnectionIdentifier {
24383
24360
  return;
24384
24361
  const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24385
24362
  if (activeStates.length === 1) {
24386
- console.log(`PixelStreaming Instance is connected.`);
24363
+ //console.log(`PixelStreaming Instance is connected.`);
24387
24364
  }
24388
24365
  else if (activeStates.length !== 0) {
24389
24366
  console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);