@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.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.6";
23434
23434
  this.settings = settings;
23435
23435
  this.session = new Session_1.Session();
23436
23436
  this._initialSettings = config.initialSettings;
@@ -23584,8 +23584,19 @@ 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
+ }
23587
23598
  bindTransportEvents() {
23588
- var _a, _b, _c, _d, _f, _g, _h;
23599
+ var _a, _b, _c, _d, _e, _f, _g;
23589
23600
  const current = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23590
23601
  if (!current || current === this._boundTransport)
23591
23602
  return;
@@ -23594,13 +23605,24 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23594
23605
  if (this._onWsOpen)
23595
23606
  (_c = (_b = this._boundTransport).removeEventListener) === null || _c === void 0 ? void 0 : _c.call(_b, "open", this._onWsOpen);
23596
23607
  if (this._onWsClose)
23597
- (_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);
23598
23609
  }
23599
23610
  // Define (or reuse) callbacks
23600
23611
  if (!this._onWsOpen) {
23601
23612
  this._onWsOpen = () => {
23602
23613
  this.flushOutbox();
23603
- 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);
23604
23626
  };
23605
23627
  }
23606
23628
  if (!this._onWsClose) {
@@ -23610,85 +23632,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23610
23632
  };
23611
23633
  }
23612
23634
  // 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);
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);
23615
23637
  this._boundTransport = current;
23616
23638
  }
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
23639
  get isWsOpen() {
23693
23640
  var _a, _b;
23694
23641
  const ws = (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.webSocket;
@@ -23709,7 +23656,7 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23709
23656
  return state !== undefined ? state : WebSocket.CLOSED;
23710
23657
  }
23711
23658
  constructor(config, overrides) {
23712
- var _a, _b, _c, _d, _f, _g, _h;
23659
+ var _a, _b, _c;
23713
23660
  /** As soon as upstream is fixed, we got to adjust the url building process. */
23714
23661
  (0, ApplyUrlHack_1.ApplyUrlHack)();
23715
23662
  super(config, overrides);
@@ -23717,25 +23664,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23717
23664
  this.isProcessingQueue = false;
23718
23665
  // --- reliable send / idempotence ---
23719
23666
  this.outbox = [];
23720
- this.retryTimers = new Map(); // reserved if you later add ACKs
23721
23667
  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;
23668
+ // --- first-frame (rVFC) detection + element swap handling ---
23669
+ this._rVFCsupported = "requestVideoFrameCallback" in HTMLVideoElement.prototype;
23670
+ this._postInitSideEffectsDone = false;
23726
23671
  // Externalized
23727
- /** On ping the session creation timestamp will be updated. */
23728
23672
  this.queueHandler = new EventHandler_1.EventHandler();
23729
- /** Error receiver. */
23730
23673
  this.errorHandler = new EventHandler_1.EventHandler();
23731
- /** LoveLetter */
23732
23674
  this.loveLetterHandler = new EventHandler_1.EventHandler();
23733
- /** SessionId */
23734
23675
  this.sessionIdHandler = new EventHandler_1.EventHandler();
23735
- /** VideoInitialized */
23676
+ /** VideoInitialized (native Epic event) */
23736
23677
  this.videoInitializedHandler = new EventHandler_1.EventHandler();
23737
23678
  // Internal
23738
- /** WebSocket Close */
23739
23679
  this.websocketOnCloseHandler = new EventHandler_1.EventHandler();
23740
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"
23741
23681
  * Instead we use ApplyUrlHack();
@@ -23748,19 +23688,15 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23748
23688
  const transport = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.transport;
23749
23689
  (_b = transport === null || transport === void 0 ? void 0 : transport.addEventListener) === null || _b === void 0 ? void 0 : _b.call(transport, "open", () => {
23750
23690
  this.flushOutbox();
23751
- this.startVideoInitWatchdog(); // only start, no send
23691
+ // no polling watchdog
23752
23692
  });
23753
23693
  (_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
23694
  if (this.websocketOnCloseHandler) {
23756
23695
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23757
23696
  }
23758
23697
  });
23759
- this.setupVideoInitFallbacks();
23760
23698
  // Set override config.
23761
23699
  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
23700
  this.loveLettersList = [];
23765
23701
  this.microphoneOverlay = new MicrophoneOverlay_1.MicrophoneOverlay(this);
23766
23702
  this.diagnosticsCollector = new DiagnosticsCollector_1.DiagnosticsCollector({
@@ -23769,9 +23705,9 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23769
23705
  // after super(...) and once webRtcController is available
23770
23706
  this.bindTransportEvents();
23771
23707
  this.startTransportWatcher();
23772
- // ensure video 'playing' fallback is attached to the current element
23773
- this.bindVideoElPlayingOnce();
23774
- // this.loveLettersContainer = null;
23708
+ // Arm first-frame detector & watch for element swaps
23709
+ this.attachFirstRenderedFrameOnce();
23710
+ this.watchVideoElementReplacement();
23775
23711
  this.wrapWebSocketOnCloseHandler();
23776
23712
  // Bind the event listener function to the class instance
23777
23713
  this.handleResolutionChange = this.handleResolutionChange.bind(this);
@@ -23839,14 +23775,13 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23839
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}`);
23840
23776
  }
23841
23777
  finally {
23842
- // Build payload with required fields only; append diagnostics if available
23843
23778
  const payload = {
23844
23779
  type: "version",
23845
23780
  version: this.config.VERSION
23846
23781
  };
23847
23782
  if (diagnostics !== undefined)
23848
23783
  payload.diagnostics = diagnostics;
23849
- this.send(payload); // uses your buffered send
23784
+ this.send(payload); // uses buffered send
23850
23785
  }
23851
23786
  });
23852
23787
  }
@@ -23859,14 +23794,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23859
23794
  this.session.set(this.session.current);
23860
23795
  }
23861
23796
  /** Handle incoming configurations. */
23862
- // public readonly streamInfoHandler = new EventHandler<never>();
23863
23797
  onStreamInfo(streamInfo) {
23864
23798
  var _a, _b, _c, _d;
23865
23799
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`StreamInfo received.`);
23866
- // Save the streamInfo to the instance variable
23867
23800
  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
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"])) {
23871
23802
  const { afk } = streamInfo.streamInfo;
23872
23803
  if (afk) {
@@ -23876,7 +23807,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23876
23807
  const { afkController } = this.webRtcController;
23877
23808
  afkController.countDown = afk.action;
23878
23809
  }
23879
- // Relevant settings need to be interpreted when they come through the streamInfo to the client.
23880
23810
  if ((_c = streamInfo.streamInfo.webSdkSettings) === null || _c === void 0 ? void 0 : _c.init) {
23881
23811
  this.config.setSettings(streamInfo.streamInfo.webSdkSettings.init);
23882
23812
  }
@@ -23891,7 +23821,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23891
23821
  }
23892
23822
  this.handleMouseLock();
23893
23823
  this.injectCustomUI();
23894
- // EventHandler.Emit(this.streamInfoHandler, undefined as never);
23895
23824
  }
23896
23825
  onQueue(message) {
23897
23826
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(`QueueInfo received.`);
@@ -23902,7 +23831,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23902
23831
  EventHandler_1.EventHandler.Emit(this.errorHandler, error);
23903
23832
  }
23904
23833
  onLoveLetter(loveLetter) {
23905
- // Logger.Info(Logger.GetStackTrace(), loveLetter.reason);
23906
23834
  if (this.config.settings.loveLetterLogging)
23907
23835
  console.info(loveLetter.reason);
23908
23836
  EventHandler_1.EventHandler.Emit(this.loveLetterHandler, loveLetter);
@@ -23910,7 +23838,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23910
23838
  }
23911
23839
  onSessionId(message) {
23912
23840
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Info(message.sessionId);
23913
- // console.info(`Session: ${message.sessionId}`);
23914
23841
  this.session.set(message.sessionId);
23915
23842
  EventHandler_1.EventHandler.Emit(this.sessionIdHandler, message.sessionId);
23916
23843
  }
@@ -23918,59 +23845,112 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23918
23845
  if (this.videoInitializedSent)
23919
23846
  return;
23920
23847
  this.videoInitializedSent = true;
23921
- // Use withId if you plan to support idempotence on the server
23922
- this.send(/* this.withId( */ { type: "onVideoInitialized" } /* ) */);
23848
+ // cancel rVFC if still armed (we've already initialized)
23849
+ this.cancelFirstRenderedFrameHook();
23850
+ this.send({ type: "onVideoInitialized" });
23923
23851
  if (this.videoInitializedHandler) {
23924
23852
  EventHandler_1.EventHandler.Emit(this.videoInitializedHandler, undefined);
23925
23853
  }
23926
23854
  }
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
23855
+ runPostInitSideEffectsOnce() {
23856
+ var _a, _b, _c, _d, _e, _f, _g;
23857
+ if (this._postInitSideEffectsDone)
23858
+ return;
23859
+ this._postInitSideEffectsDone = true;
23950
23860
  try {
23951
23861
  (_a = this.handleMouseLock) === null || _a === void 0 ? void 0 : _a.call(this);
23952
23862
  }
23953
- catch (_j) { }
23863
+ catch (_h) { }
23954
23864
  try {
23955
23865
  (_b = this.handleResolutionChange) === null || _b === void 0 ? void 0 : _b.call(this);
23956
23866
  }
23957
- catch (_k) { }
23867
+ catch (_j) { }
23958
23868
  try {
23959
23869
  (_c = this.handleRemoveLoveLetters) === null || _c === void 0 ? void 0 : _c.call(this);
23960
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
+ }
23961
23875
  catch (_l) { }
23962
23876
  try {
23963
- (_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);
23964
23878
  }
23965
23879
  catch (_m) { }
23966
23880
  try {
23967
- (_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);
23968
23882
  }
23969
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
23970
23940
  try {
23971
- (_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
23972
23953
  }
23973
- catch (_p) { }
23974
23954
  }
23975
23955
  /** Adding a zod-safe handler. */
23976
23956
  addMessageHandler(type, zod, boundMethod) {
@@ -23979,13 +23959,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23979
23959
  if (parsed.success)
23980
23960
  boundMethod.call(this, parsed.data);
23981
23961
  else {
23982
- // This cast has to be done, since Frontend does not use typescript-strict setting.
23983
23962
  const parsedError = parsed;
23984
23963
  const error = new Error(`Unexpected message content. Event:'${type}', ZodError: ${parsedError.error.message}`);
23985
23964
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Error(error.message);
23986
23965
  }
23987
23966
  if (type === "error") {
23988
- //this.disconnect();
23967
+ // this.disconnect();
23989
23968
  }
23990
23969
  });
23991
23970
  }
@@ -23996,7 +23975,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23996
23975
  */
23997
23976
  sendStats(stats) {
23998
23977
  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
23978
  this.send(message);
24001
23979
  }
24002
23980
  send(a, b) {
@@ -24037,22 +24015,18 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24037
24015
  this.signallingProtocol.sendMessage(m);
24038
24016
  }
24039
24017
  catch (_a) {
24040
- // if we fail mid-flush, put remaining back and bail
24041
24018
  this.outbox.unshift(m, ...batch.slice(batch.indexOf(m) + 1));
24042
24019
  break;
24043
24020
  }
24044
24021
  }
24045
24022
  }
24046
24023
  handleResolutionChange() {
24047
- var _a, _b, _c, _d, _f, _g, _h;
24048
- // Get the resolution from the streamInfo if available
24024
+ var _a, _b, _c, _d, _e, _f, _g;
24049
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())) {
24050
24026
  return;
24051
24027
  }
24052
- // Get the resolution from the streamInfo if available
24053
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;
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
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;
24056
24030
  if (resolution && (resolution === null || resolution === void 0 ? void 0 : resolution.dynamic)) {
24057
24031
  setTimeout(() => {
24058
24032
  var _a;
@@ -24060,32 +24034,24 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24060
24034
  const videoElementParent = videoPlayer === null || videoPlayer === void 0 ? void 0 : videoPlayer.getVideoParentElement();
24061
24035
  const browserWidth = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().width;
24062
24036
  const browserHeight = videoElementParent === null || videoElementParent === void 0 ? void 0 : videoElementParent.getBoundingClientRect().height;
24063
- // Define maximum allowed resolution
24064
24037
  const maxWidth = resolution === null || resolution === void 0 ? void 0 : resolution.width;
24065
24038
  const maxHeight = resolution === null || resolution === void 0 ? void 0 : resolution.height;
24066
- // Calculate the aspect ratio of the browser viewport
24067
24039
  const aspectRatio = browserWidth / browserHeight;
24068
- // Calculate the new width and height based on the aspect ratio
24069
24040
  let limitedWidth, limitedHeight;
24070
24041
  if (aspectRatio > maxWidth / maxHeight) {
24071
- // Width is the limiting factor
24072
24042
  limitedWidth = maxWidth;
24073
24043
  limitedHeight = maxWidth / aspectRatio;
24074
24044
  }
24075
24045
  else {
24076
- // Height is the limiting factor
24077
24046
  limitedHeight = maxHeight;
24078
24047
  limitedWidth = maxHeight * aspectRatio;
24079
24048
  }
24080
24049
  if (videoPlayer) {
24081
- // Check Unreal Engine version
24082
24050
  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
24051
  const descriptor = { Console: `r.setres ${Math.round(limitedWidth)}x${Math.round(limitedHeight)}w` };
24085
24052
  (_a = this === null || this === void 0 ? void 0 : this.webRtcController) === null || _a === void 0 ? void 0 : _a.emitUIInteraction(descriptor);
24086
24053
  }
24087
24054
  else {
24088
- // Use the aspect ratio limited resolution for the onMatchViewportResolutionCallback
24089
24055
  videoPlayer.onMatchViewportResolutionCallback(Math.round(limitedWidth), Math.round(limitedHeight));
24090
24056
  }
24091
24057
  }
@@ -24104,21 +24070,39 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24104
24070
  }
24105
24071
  }
24106
24072
  removePlayer() {
24107
- var _a, _b, _c, _d;
24108
- this.stopVideoInitWatchdog(); // PS event arrived, cancel backup
24073
+ var _a, _b, _c, _d, _e;
24109
24074
  this.stopTransportWatcher();
24110
- // Optional: unbind handlers
24075
+ // Unbind transport listeners
24111
24076
  if (this._boundTransport) {
24112
24077
  if (this._onWsOpen)
24113
24078
  (_b = (_a = this._boundTransport).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, "open", this._onWsOpen);
24114
24079
  if (this._onWsClose)
24115
24080
  (_d = (_c = this._boundTransport).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, "close", this._onWsClose);
24116
24081
  }
24117
- if (this._boundVideoEl && this._onVideoPlaying) {
24118
- 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();
24119
24086
  }
24087
+ catch (_f) { }
24120
24088
  this.disconnect();
24121
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
+ }
24122
24106
  handleMouseLock() {
24123
24107
  var _a, _b, _c;
24124
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));
@@ -24150,31 +24134,25 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24150
24134
  }
24151
24135
  pushLetter(letter) {
24152
24136
  var _a;
24153
- // Add the letter to the queue
24154
24137
  (_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
24138
  if (!this.isProcessingQueue) {
24157
24139
  this === null || this === void 0 ? void 0 : this.processLoveLetterQueue();
24158
24140
  }
24159
24141
  }
24160
24142
  processLoveLetterQueue() {
24161
24143
  var _a, _b;
24162
- // Set the flag to indicate that we are processing the queue
24163
24144
  this.isProcessingQueue = true;
24164
- // Take the first item from the queue
24165
24145
  const letter = this.loveLettersQueue.shift();
24166
24146
  if (letter !== undefined) {
24167
24147
  const formattedLoveLetter = letter === null || letter === void 0 ? void 0 : letter.replace(/LL: |\.$/g, "");
24168
24148
  (_a = this === null || this === void 0 ? void 0 : this.loveLettersList) === null || _a === void 0 ? void 0 : _a.push(formattedLoveLetter);
24169
24149
  const loveLettersBox = new LoveLetters_1.LoveLetters();
24170
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);
24171
- // Process the next item in the queue after a delay
24172
24151
  setTimeout(() => {
24173
24152
  this.processLoveLetterQueue();
24174
24153
  }, 1000);
24175
24154
  }
24176
24155
  else {
24177
- // If the queue is empty, reset the flag
24178
24156
  this.isProcessingQueue = false;
24179
24157
  }
24180
24158
  }
@@ -24216,9 +24194,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24216
24194
  (_b = this === null || this === void 0 ? void 0 : this.microphoneOverlay) === null || _b === void 0 ? void 0 : _b.toggleMessage(enable);
24217
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);
24218
24196
  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
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();
24222
24201
  this.reconnect();
24223
24202
  }, 1000);
24224
24203
  }
@@ -24232,12 +24211,10 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24232
24211
  }
24233
24212
  onStreamingStateChange(callback) {
24234
24213
  var _a, _b, _c, _d;
24235
- // Listen to video element events
24236
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();
24237
24215
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("play", () => callback(true));
24238
24216
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("pause", () => callback(false));
24239
24217
  videoElement === null || videoElement === void 0 ? void 0 : videoElement.addEventListener("ended", () => callback(false));
24240
- // Listen to WebSocket events
24241
24218
  (_c = this.webRtcController) === null || _c === void 0 ? void 0 : _c.transport.addEventListener("open", () => {
24242
24219
  callback(true);
24243
24220
  });
@@ -24246,12 +24223,12 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
24246
24223
  });
24247
24224
  }
24248
24225
  removeXRIconIfDisabled() {
24249
- var _a, _b, _c, _d, _f;
24226
+ var _a, _b, _c, _d, _e;
24250
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)) {
24251
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);
24252
24229
  if (this.videoElementParent) {
24253
24230
  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");
24231
+ const xrBtn = (_e = this === null || this === void 0 ? void 0 : this.videoElementParent) === null || _e === void 0 ? void 0 : _e.parentElement.querySelector("#xrBtn");
24255
24232
  xrBtn === null || xrBtn === void 0 ? void 0 : xrBtn.remove();
24256
24233
  }
24257
24234
  }
@@ -24393,7 +24370,7 @@ class ConnectionIdentifier {
24393
24370
  return;
24394
24371
  const activeStates = this.GetWebSocketStates().filter((state) => state <= WebsocketState.OPEN);
24395
24372
  if (activeStates.length === 1) {
24396
- console.log(`PixelStreaming Instance is connected.`);
24373
+ //console.log(`PixelStreaming Instance is connected.`);
24397
24374
  }
24398
24375
  else if (activeStates.length !== 0) {
24399
24376
  console.warn(`${activeStates.length} PixelStreaming Instances are connected!`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcware-cloud/pixelstreaming-websdk",
3
3
  "description": "WebSDK for easy implementation of pixel streaming with Arcware Cloud Services. Heavily based on the '@epicgames-ps' library.",
4
- "version": "1.3.4",
4
+ "version": "1.3.6",
5
5
  "type": "commonjs",
6
6
  "main": "./index.umd.js",
7
7
  "module": "./index.umd.js",
@@ -26,7 +26,7 @@ export declare class ArcwareConfig extends Config {
26
26
  readonly session: Session;
27
27
  readonly settings: Settings;
28
28
  private _initialSettings;
29
- readonly VERSION = "1.3.4";
29
+ readonly VERSION = "1.3.6";
30
30
  constructor(config: ArcwareConfigParams);
31
31
  /** Setup connection string. */
32
32
  get urlFlags(): string;