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