@arcware-cloud/pixelstreaming-websdk 1.3.3 → 1.3.6

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