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