@arcware-cloud/pixelstreaming-websdk 1.3.7 → 1.3.9

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/README.md CHANGED
@@ -80,6 +80,14 @@ For more detailed examples and advanced usage, please refer to our documentation
80
80
 
81
81
  # Changelog
82
82
 
83
+ ### 1.3.9
84
+
85
+ - added an event handler to check if video is streaming if cloud requests evidence
86
+
87
+ ### 1.3.8
88
+
89
+ - fixed error handling in SDK to back of reconnects if connection was intentionally terminated by cloud
90
+
83
91
  ### 1.3.3
84
92
 
85
93
  - adding built in functionality for filetransfer (docs will follow soon)
package/index.cjs.js CHANGED
@@ -23420,7 +23420,7 @@ class ArcwareConfig extends lib_pixelstreamingfrontend_ue5_5_1.Config {
23420
23420
  if (!config.initialSettings.ss)
23421
23421
  config.initialSettings.ss = exports.DefaultUrl;
23422
23422
  super(config);
23423
- this.VERSION = "1.3.7";
23423
+ this.VERSION = "1.3.9";
23424
23424
  this.settings = settings;
23425
23425
  this.session = new Session_1.Session();
23426
23426
  this._initialSettings = config.initialSettings;
@@ -23586,9 +23586,11 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23586
23586
  this.resetInitGuardsAndHooks();
23587
23587
  }
23588
23588
  // Classify CloseEvent into retryable vs. legit (non-retryable)
23589
- isRetryableClose(evt) {
23589
+ isRetryableClose(err) {
23590
+ var _a, _b;
23590
23591
  //const reason = (evt.reason || "").toLowerCase();
23591
- if (evt.code >= 4000 /*||
23592
+ const code = (_b = (_a = err.code) !== null && _a !== void 0 ? _a : err.status) !== null && _b !== void 0 ? _b : err.errorCode;
23593
+ if (code >= 4000 /*||
23592
23594
  reason.includes("Stream disconnected.") ||
23593
23595
  reason.includes("forbidden") ||
23594
23596
  reason.includes("invalid token") ||
@@ -23631,23 +23633,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23631
23633
  }
23632
23634
  if (!this._onWsClose) {
23633
23635
  this._onWsClose = (evt) => {
23634
- var _a, _b;
23635
- console.log(evt.code);
23636
- console.log(evt.reason);
23637
- const retryable = this.isRetryableClose(evt);
23638
- if (!retryable) {
23639
- // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23640
- try {
23641
- (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, evt.reason, false);
23642
- }
23643
- catch (_c) { }
23644
- // Optional: pause the transport watcher so we don't churn while idle
23645
- this.stopTransportWatcher();
23646
- }
23647
- else {
23648
- // Leave your existing auto-retry behavior for transient errors
23649
- this.startTransportWatcher(); // (in case it was stopped)
23650
- }
23651
23636
  // Bubble to your public close handler
23652
23637
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23653
23638
  };
@@ -23740,6 +23725,7 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23740
23725
  this.addMessageHandler("ping", shared_pixelstreaming_websdk_1.Messages.ZPing, this.onPing);
23741
23726
  this.addMessageHandler("queue", shared_pixelstreaming_websdk_1.Messages.ZQueue, this.onQueue);
23742
23727
  this.addMessageHandler("version", shared_pixelstreaming_websdk_1.Messages.ZVersion, this.onVersion);
23728
+ this.addMessageHandler("render", shared_pixelstreaming_websdk_1.Messages.ZRender, this.onRender);
23743
23729
  // Create a debounced version of the handleResolutionChange function
23744
23730
  const debouncedResolutionChange = (0, debounce_1.default)(() => {
23745
23731
  this.handleResolutionChange();
@@ -23806,6 +23792,64 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23806
23792
  }
23807
23793
  });
23808
23794
  }
23795
+ isVideoRenderingNow(video) {
23796
+ if (!video)
23797
+ return false;
23798
+ const hasDims = video.videoWidth > 0 && video.videoHeight > 0;
23799
+ const hasData = video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // ≥2
23800
+ const playing = !video.paused && !video.ended;
23801
+ // Be conservative: require at least two signals to avoid metadata-only false positives
23802
+ return (hasDims && hasData) || (hasDims && playing) || (hasData && playing);
23803
+ }
23804
+ waitForFirstFrameWithTimeout(ms = 2000) {
23805
+ var _a, _b, _c;
23806
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
23807
+ const video = (_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);
23808
+ if (this.isVideoRenderingNow(video))
23809
+ return true;
23810
+ let t;
23811
+ const ok = yield Promise.race([
23812
+ new Promise((resolve) => {
23813
+ var _a, _b;
23814
+ const done = () => {
23815
+ cleanup();
23816
+ resolve(true);
23817
+ };
23818
+ const cleanup = () => {
23819
+ var _a, _b;
23820
+ video.removeEventListener("playing", onPlaying);
23821
+ video.removeEventListener("resize", onResize);
23822
+ if ("cancelVideoFrameCallback" in HTMLVideoElement.prototype && rvfcId != null) {
23823
+ (_b = (_a = video).cancelVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, rvfcId);
23824
+ }
23825
+ };
23826
+ const onPlaying = () => this.isVideoRenderingNow(video) && done();
23827
+ const onResize = () => this.isVideoRenderingNow(video) && done();
23828
+ video.addEventListener("playing", onPlaying, { once: true });
23829
+ video.addEventListener("resize", onResize, { once: true });
23830
+ const rvfcId = "requestVideoFrameCallback" in HTMLVideoElement.prototype
23831
+ ? (_b = (_a = video).requestVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, () => done())
23832
+ : null;
23833
+ }),
23834
+ new Promise((resolve) => {
23835
+ t = window.setTimeout(() => resolve(false), ms);
23836
+ })
23837
+ ]);
23838
+ if (t)
23839
+ clearTimeout(t);
23840
+ return ok;
23841
+ });
23842
+ }
23843
+ onRender(_msg) {
23844
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
23845
+ const isRendering = yield this.waitForFirstFrameWithTimeout();
23846
+ const payload = {
23847
+ type: "render",
23848
+ isRendering: isRendering
23849
+ };
23850
+ this.send(payload); // uses buffered send
23851
+ });
23852
+ }
23809
23853
  /** On ping the session creation timestamp will be updated. */
23810
23854
  onPing(message) {
23811
23855
  var _a, _b;
@@ -23848,7 +23892,23 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23848
23892
  EventHandler_1.EventHandler.Emit(this.queueHandler, message);
23849
23893
  }
23850
23894
  onError(error) {
23895
+ var _a, _b;
23851
23896
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Error(error.type);
23897
+ console.log(error);
23898
+ const retryable = this.isRetryableClose(error);
23899
+ if (!retryable) {
23900
+ // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23901
+ try {
23902
+ (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, error.reason, false);
23903
+ }
23904
+ catch (_c) { }
23905
+ // Optional: pause the transport watcher so we don't churn while idle
23906
+ this.stopTransportWatcher();
23907
+ }
23908
+ else {
23909
+ // Leave your existing auto-retry behavior for transient errors
23910
+ this.startTransportWatcher(); // (in case it was stopped)
23911
+ }
23852
23912
  EventHandler_1.EventHandler.Emit(this.errorHandler, error);
23853
23913
  }
23854
23914
  onLoveLetter(loveLetter) {
@@ -26426,6 +26486,20 @@ exports.ZQueue = zod_1.z.object({
26426
26486
  });
26427
26487
 
26428
26488
 
26489
+ /***/ }),
26490
+
26491
+ /***/ 1919:
26492
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
26493
+
26494
+
26495
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
26496
+ exports.ZRender = void 0;
26497
+ const zod_1 = __webpack_require__(8754);
26498
+ exports.ZRender = zod_1.z.object({
26499
+ type: zod_1.z.literal("render")
26500
+ });
26501
+
26502
+
26429
26503
  /***/ }),
26430
26504
 
26431
26505
  /***/ 4201:
@@ -26783,9 +26857,10 @@ tslib_1.__exportStar(__webpack_require__(1224), exports);
26783
26857
  tslib_1.__exportStar(__webpack_require__(4882), exports);
26784
26858
  tslib_1.__exportStar(__webpack_require__(3751), exports);
26785
26859
  tslib_1.__exportStar(__webpack_require__(9864), exports);
26860
+ tslib_1.__exportStar(__webpack_require__(1919), exports);
26786
26861
  const Stats_1 = __webpack_require__(3403);
26787
26862
  exports.Send = {
26788
- stats: Stats_1.ZStats,
26863
+ stats: Stats_1.ZStats
26789
26864
  };
26790
26865
 
26791
26866
 
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.7";
23431
+ this.VERSION = "1.3.9";
23432
23432
  this.settings = settings;
23433
23433
  this.session = new _domain_Session__WEBPACK_IMPORTED_MODULE_0__.Session();
23434
23434
  this._initialSettings = config.initialSettings;
@@ -23611,9 +23611,11 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23611
23611
  this.resetInitGuardsAndHooks();
23612
23612
  }
23613
23613
  // Classify CloseEvent into retryable vs. legit (non-retryable)
23614
- isRetryableClose(evt) {
23614
+ isRetryableClose(err) {
23615
+ var _a, _b;
23615
23616
  //const reason = (evt.reason || "").toLowerCase();
23616
- if (evt.code >= 4000 /*||
23617
+ const code = (_b = (_a = err.code) !== null && _a !== void 0 ? _a : err.status) !== null && _b !== void 0 ? _b : err.errorCode;
23618
+ if (code >= 4000 /*||
23617
23619
  reason.includes("Stream disconnected.") ||
23618
23620
  reason.includes("forbidden") ||
23619
23621
  reason.includes("invalid token") ||
@@ -23656,23 +23658,6 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23656
23658
  }
23657
23659
  if (!this._onWsClose) {
23658
23660
  this._onWsClose = (evt) => {
23659
- var _a, _b;
23660
- console.log(evt.code);
23661
- console.log(evt.reason);
23662
- const retryable = this.isRetryableClose(evt);
23663
- if (!retryable) {
23664
- // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23665
- try {
23666
- (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, evt.reason, false);
23667
- }
23668
- catch (_c) { }
23669
- // Optional: pause the transport watcher so we don't churn while idle
23670
- this.stopTransportWatcher();
23671
- }
23672
- else {
23673
- // Leave your existing auto-retry behavior for transient errors
23674
- this.startTransportWatcher(); // (in case it was stopped)
23675
- }
23676
23661
  // Bubble to your public close handler
23677
23662
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23678
23663
  };
@@ -23765,6 +23750,7 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23765
23750
  this.addMessageHandler("ping", _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZPing, this.onPing);
23766
23751
  this.addMessageHandler("queue", _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZQueue, this.onQueue);
23767
23752
  this.addMessageHandler("version", _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZVersion, this.onVersion);
23753
+ this.addMessageHandler("render", _arcware_cloud_shared_pixelstreaming_websdk__WEBPACK_IMPORTED_MODULE_0__.Messages.ZRender, this.onRender);
23768
23754
  // Create a debounced version of the handleResolutionChange function
23769
23755
  const debouncedResolutionChange = (0,_domain_debounce__WEBPACK_IMPORTED_MODULE_8__["default"])(() => {
23770
23756
  this.handleResolutionChange();
@@ -23831,6 +23817,64 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23831
23817
  }
23832
23818
  });
23833
23819
  }
23820
+ isVideoRenderingNow(video) {
23821
+ if (!video)
23822
+ return false;
23823
+ const hasDims = video.videoWidth > 0 && video.videoHeight > 0;
23824
+ const hasData = video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // ≥2
23825
+ const playing = !video.paused && !video.ended;
23826
+ // Be conservative: require at least two signals to avoid metadata-only false positives
23827
+ return (hasDims && hasData) || (hasDims && playing) || (hasData && playing);
23828
+ }
23829
+ waitForFirstFrameWithTimeout(ms = 2000) {
23830
+ var _a, _b, _c;
23831
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_9__.__awaiter)(this, void 0, void 0, function* () {
23832
+ const video = (_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);
23833
+ if (this.isVideoRenderingNow(video))
23834
+ return true;
23835
+ let t;
23836
+ const ok = yield Promise.race([
23837
+ new Promise((resolve) => {
23838
+ var _a, _b;
23839
+ const done = () => {
23840
+ cleanup();
23841
+ resolve(true);
23842
+ };
23843
+ const cleanup = () => {
23844
+ var _a, _b;
23845
+ video.removeEventListener("playing", onPlaying);
23846
+ video.removeEventListener("resize", onResize);
23847
+ if ("cancelVideoFrameCallback" in HTMLVideoElement.prototype && rvfcId != null) {
23848
+ (_b = (_a = video).cancelVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, rvfcId);
23849
+ }
23850
+ };
23851
+ const onPlaying = () => this.isVideoRenderingNow(video) && done();
23852
+ const onResize = () => this.isVideoRenderingNow(video) && done();
23853
+ video.addEventListener("playing", onPlaying, { once: true });
23854
+ video.addEventListener("resize", onResize, { once: true });
23855
+ const rvfcId = "requestVideoFrameCallback" in HTMLVideoElement.prototype
23856
+ ? (_b = (_a = video).requestVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, () => done())
23857
+ : null;
23858
+ }),
23859
+ new Promise((resolve) => {
23860
+ t = window.setTimeout(() => resolve(false), ms);
23861
+ })
23862
+ ]);
23863
+ if (t)
23864
+ clearTimeout(t);
23865
+ return ok;
23866
+ });
23867
+ }
23868
+ onRender(_msg) {
23869
+ return (0,tslib__WEBPACK_IMPORTED_MODULE_9__.__awaiter)(this, void 0, void 0, function* () {
23870
+ const isRendering = yield this.waitForFirstFrameWithTimeout();
23871
+ const payload = {
23872
+ type: "render",
23873
+ isRendering: isRendering
23874
+ };
23875
+ this.send(payload); // uses buffered send
23876
+ });
23877
+ }
23834
23878
  /** On ping the session creation timestamp will be updated. */
23835
23879
  onPing(message) {
23836
23880
  var _a, _b;
@@ -23873,7 +23917,23 @@ class ArcwarePixelStreaming extends _epicgames_ps_lib_pixelstreamingfrontend_ue5
23873
23917
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.queueHandler, message);
23874
23918
  }
23875
23919
  onError(error) {
23920
+ var _a, _b;
23876
23921
  _epicgames_ps_lib_pixelstreamingfrontend_ue5_5__WEBPACK_IMPORTED_MODULE_10__.Logger.Error(error.type);
23922
+ console.log(error);
23923
+ const retryable = this.isRetryableClose(error);
23924
+ if (!retryable) {
23925
+ // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23926
+ try {
23927
+ (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, error.reason, false);
23928
+ }
23929
+ catch (_c) { }
23930
+ // Optional: pause the transport watcher so we don't churn while idle
23931
+ this.stopTransportWatcher();
23932
+ }
23933
+ else {
23934
+ // Leave your existing auto-retry behavior for transient errors
23935
+ this.startTransportWatcher(); // (in case it was stopped)
23936
+ }
23877
23937
  _domain_EventHandler__WEBPACK_IMPORTED_MODULE_5__.EventHandler.Emit(this.errorHandler, error);
23878
23938
  }
23879
23939
  onLoveLetter(loveLetter) {
@@ -26491,6 +26551,22 @@ const ZQueue = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
26491
26551
  });
26492
26552
 
26493
26553
 
26554
+ /***/ }),
26555
+
26556
+ /***/ 1919:
26557
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
26558
+
26559
+ __webpack_require__.r(__webpack_exports__);
26560
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26561
+ /* harmony export */ "ZRender": () => (/* binding */ ZRender)
26562
+ /* harmony export */ });
26563
+ /* harmony import */ var zod__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1604);
26564
+
26565
+ const ZRender = zod__WEBPACK_IMPORTED_MODULE_0__.z.object({
26566
+ type: zod__WEBPACK_IMPORTED_MODULE_0__.z.literal("render")
26567
+ });
26568
+
26569
+
26494
26570
  /***/ }),
26495
26571
 
26496
26572
  /***/ 4201:
@@ -26828,6 +26904,7 @@ __webpack_require__.r(__webpack_exports__);
26828
26904
  /* harmony export */ "ZLoveLetter": () => (/* reexport safe */ _LoveLetter__WEBPACK_IMPORTED_MODULE_1__.ZLoveLetter),
26829
26905
  /* harmony export */ "ZPing": () => (/* reexport safe */ _Ping__WEBPACK_IMPORTED_MODULE_5__.ZPing),
26830
26906
  /* harmony export */ "ZQueue": () => (/* reexport safe */ _Queue__WEBPACK_IMPORTED_MODULE_6__.ZQueue),
26907
+ /* harmony export */ "ZRender": () => (/* reexport safe */ _Render__WEBPACK_IMPORTED_MODULE_9__.ZRender),
26831
26908
  /* harmony export */ "ZSessionId": () => (/* reexport safe */ _SessionId__WEBPACK_IMPORTED_MODULE_2__.ZSessionId),
26832
26909
  /* harmony export */ "ZStats": () => (/* reexport safe */ _Stats__WEBPACK_IMPORTED_MODULE_4__.ZStats),
26833
26910
  /* harmony export */ "ZStreamInfo": () => (/* reexport safe */ _StreamInfo__WEBPACK_IMPORTED_MODULE_3__.ZStreamInfo),
@@ -26842,6 +26919,8 @@ __webpack_require__.r(__webpack_exports__);
26842
26919
  /* harmony import */ var _Queue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4882);
26843
26920
  /* harmony import */ var _Version__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(3751);
26844
26921
  /* harmony import */ var _WebSdkSettings__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(9864);
26922
+ /* harmony import */ var _Render__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(1919);
26923
+
26845
26924
 
26846
26925
 
26847
26926
 
@@ -26853,7 +26932,7 @@ __webpack_require__.r(__webpack_exports__);
26853
26932
 
26854
26933
 
26855
26934
  const Send = {
26856
- stats: _Stats__WEBPACK_IMPORTED_MODULE_4__.ZStats,
26935
+ stats: _Stats__WEBPACK_IMPORTED_MODULE_4__.ZStats
26857
26936
  };
26858
26937
 
26859
26938
 
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.7";
23433
+ this.VERSION = "1.3.9";
23434
23434
  this.settings = settings;
23435
23435
  this.session = new Session_1.Session();
23436
23436
  this._initialSettings = config.initialSettings;
@@ -23596,9 +23596,11 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23596
23596
  this.resetInitGuardsAndHooks();
23597
23597
  }
23598
23598
  // Classify CloseEvent into retryable vs. legit (non-retryable)
23599
- isRetryableClose(evt) {
23599
+ isRetryableClose(err) {
23600
+ var _a, _b;
23600
23601
  //const reason = (evt.reason || "").toLowerCase();
23601
- if (evt.code >= 4000 /*||
23602
+ const code = (_b = (_a = err.code) !== null && _a !== void 0 ? _a : err.status) !== null && _b !== void 0 ? _b : err.errorCode;
23603
+ if (code >= 4000 /*||
23602
23604
  reason.includes("Stream disconnected.") ||
23603
23605
  reason.includes("forbidden") ||
23604
23606
  reason.includes("invalid token") ||
@@ -23641,23 +23643,6 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23641
23643
  }
23642
23644
  if (!this._onWsClose) {
23643
23645
  this._onWsClose = (evt) => {
23644
- var _a, _b;
23645
- console.log(evt.code);
23646
- console.log(evt.reason);
23647
- const retryable = this.isRetryableClose(evt);
23648
- if (!retryable) {
23649
- // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23650
- try {
23651
- (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, evt.reason, false);
23652
- }
23653
- catch (_c) { }
23654
- // Optional: pause the transport watcher so we don't churn while idle
23655
- this.stopTransportWatcher();
23656
- }
23657
- else {
23658
- // Leave your existing auto-retry behavior for transient errors
23659
- this.startTransportWatcher(); // (in case it was stopped)
23660
- }
23661
23646
  // Bubble to your public close handler
23662
23647
  EventHandler_1.EventHandler.Emit(this.websocketOnCloseHandler, evt);
23663
23648
  };
@@ -23750,6 +23735,7 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23750
23735
  this.addMessageHandler("ping", shared_pixelstreaming_websdk_1.Messages.ZPing, this.onPing);
23751
23736
  this.addMessageHandler("queue", shared_pixelstreaming_websdk_1.Messages.ZQueue, this.onQueue);
23752
23737
  this.addMessageHandler("version", shared_pixelstreaming_websdk_1.Messages.ZVersion, this.onVersion);
23738
+ this.addMessageHandler("render", shared_pixelstreaming_websdk_1.Messages.ZRender, this.onRender);
23753
23739
  // Create a debounced version of the handleResolutionChange function
23754
23740
  const debouncedResolutionChange = (0, debounce_1.default)(() => {
23755
23741
  this.handleResolutionChange();
@@ -23816,6 +23802,64 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23816
23802
  }
23817
23803
  });
23818
23804
  }
23805
+ isVideoRenderingNow(video) {
23806
+ if (!video)
23807
+ return false;
23808
+ const hasDims = video.videoWidth > 0 && video.videoHeight > 0;
23809
+ const hasData = video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA; // ≥2
23810
+ const playing = !video.paused && !video.ended;
23811
+ // Be conservative: require at least two signals to avoid metadata-only false positives
23812
+ return (hasDims && hasData) || (hasDims && playing) || (hasData && playing);
23813
+ }
23814
+ waitForFirstFrameWithTimeout(ms = 2000) {
23815
+ var _a, _b, _c;
23816
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
23817
+ const video = (_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);
23818
+ if (this.isVideoRenderingNow(video))
23819
+ return true;
23820
+ let t;
23821
+ const ok = yield Promise.race([
23822
+ new Promise((resolve) => {
23823
+ var _a, _b;
23824
+ const done = () => {
23825
+ cleanup();
23826
+ resolve(true);
23827
+ };
23828
+ const cleanup = () => {
23829
+ var _a, _b;
23830
+ video.removeEventListener("playing", onPlaying);
23831
+ video.removeEventListener("resize", onResize);
23832
+ if ("cancelVideoFrameCallback" in HTMLVideoElement.prototype && rvfcId != null) {
23833
+ (_b = (_a = video).cancelVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, rvfcId);
23834
+ }
23835
+ };
23836
+ const onPlaying = () => this.isVideoRenderingNow(video) && done();
23837
+ const onResize = () => this.isVideoRenderingNow(video) && done();
23838
+ video.addEventListener("playing", onPlaying, { once: true });
23839
+ video.addEventListener("resize", onResize, { once: true });
23840
+ const rvfcId = "requestVideoFrameCallback" in HTMLVideoElement.prototype
23841
+ ? (_b = (_a = video).requestVideoFrameCallback) === null || _b === void 0 ? void 0 : _b.call(_a, () => done())
23842
+ : null;
23843
+ }),
23844
+ new Promise((resolve) => {
23845
+ t = window.setTimeout(() => resolve(false), ms);
23846
+ })
23847
+ ]);
23848
+ if (t)
23849
+ clearTimeout(t);
23850
+ return ok;
23851
+ });
23852
+ }
23853
+ onRender(_msg) {
23854
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
23855
+ const isRendering = yield this.waitForFirstFrameWithTimeout();
23856
+ const payload = {
23857
+ type: "render",
23858
+ isRendering: isRendering
23859
+ };
23860
+ this.send(payload); // uses buffered send
23861
+ });
23862
+ }
23819
23863
  /** On ping the session creation timestamp will be updated. */
23820
23864
  onPing(message) {
23821
23865
  var _a, _b;
@@ -23858,7 +23902,23 @@ class ArcwarePixelStreaming extends lib_pixelstreamingfrontend_ue5_5_1.PixelStre
23858
23902
  EventHandler_1.EventHandler.Emit(this.queueHandler, message);
23859
23903
  }
23860
23904
  onError(error) {
23905
+ var _a, _b;
23861
23906
  lib_pixelstreamingfrontend_ue5_5_1.Logger.Error(error.type);
23907
+ console.log(error);
23908
+ const retryable = this.isRetryableClose(error);
23909
+ if (!retryable) {
23910
+ // This stops the signaling client and shows the built-in "Play to reconnect" overlay
23911
+ try {
23912
+ (_b = (_a = this.webRtcController) === null || _a === void 0 ? void 0 : _a.closeSignalingServer) === null || _b === void 0 ? void 0 : _b.call(_a, error.reason, false);
23913
+ }
23914
+ catch (_c) { }
23915
+ // Optional: pause the transport watcher so we don't churn while idle
23916
+ this.stopTransportWatcher();
23917
+ }
23918
+ else {
23919
+ // Leave your existing auto-retry behavior for transient errors
23920
+ this.startTransportWatcher(); // (in case it was stopped)
23921
+ }
23862
23922
  EventHandler_1.EventHandler.Emit(this.errorHandler, error);
23863
23923
  }
23864
23924
  onLoveLetter(loveLetter) {
@@ -26436,6 +26496,20 @@ exports.ZQueue = zod_1.z.object({
26436
26496
  });
26437
26497
 
26438
26498
 
26499
+ /***/ }),
26500
+
26501
+ /***/ 1919:
26502
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
26503
+
26504
+
26505
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
26506
+ exports.ZRender = void 0;
26507
+ const zod_1 = __webpack_require__(8754);
26508
+ exports.ZRender = zod_1.z.object({
26509
+ type: zod_1.z.literal("render")
26510
+ });
26511
+
26512
+
26439
26513
  /***/ }),
26440
26514
 
26441
26515
  /***/ 4201:
@@ -26793,9 +26867,10 @@ tslib_1.__exportStar(__webpack_require__(1224), exports);
26793
26867
  tslib_1.__exportStar(__webpack_require__(4882), exports);
26794
26868
  tslib_1.__exportStar(__webpack_require__(3751), exports);
26795
26869
  tslib_1.__exportStar(__webpack_require__(9864), exports);
26870
+ tslib_1.__exportStar(__webpack_require__(1919), exports);
26796
26871
  const Stats_1 = __webpack_require__(3403);
26797
26872
  exports.Send = {
26798
- stats: Stats_1.ZStats,
26873
+ stats: Stats_1.ZStats
26799
26874
  };
26800
26875
 
26801
26876
 
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.7",
4
+ "version": "1.3.9",
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.7";
29
+ readonly VERSION = "1.3.9";
30
30
  constructor(config: ArcwareConfigParams);
31
31
  /** Setup connection string. */
32
32
  get urlFlags(): string;
@@ -50,6 +50,9 @@ export declare class ArcwarePixelStreaming extends PixelStreaming {
50
50
  */
51
51
  /** On version requested, the version of the WebSDK would be returned. */
52
52
  private onVersion;
53
+ private isVideoRenderingNow;
54
+ private waitForFirstFrameWithTimeout;
55
+ private onRender;
53
56
  /** On ping the session creation timestamp will be updated. */
54
57
  private onPing;
55
58
  /** Handle incoming configurations. */
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ export declare const ZRender: z.ZodObject<{
3
+ type: z.ZodLiteral<"render">;
4
+ }, "strip", z.ZodTypeAny, {
5
+ type?: "render";
6
+ }, {
7
+ type?: "render";
8
+ }>;
9
+ export type Render = z.infer<typeof ZRender>;
@@ -7,6 +7,7 @@ export * from "./Ping";
7
7
  export * from "./Queue";
8
8
  export * from "./Version";
9
9
  export * from "./WebSdkSettings";
10
+ export * from "./Render";
10
11
  export declare const Send: {
11
12
  stats: import("zod").ZodObject<{
12
13
  type: import("zod").ZodLiteral<"stats">;