replicas-computer 0.3.16 → 0.3.17
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/dist/index.js +1 -1
- package/dist/stream-viewer.js +100 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
package/dist/stream-viewer.js
CHANGED
|
@@ -49,6 +49,7 @@ var DESKTOP_WEBRTC_FAILURE_REASONS = {
|
|
|
49
49
|
full_chroma_undecodable: "WebRTC could not decode H.264 High 4:4:4"
|
|
50
50
|
};
|
|
51
51
|
var DESKTOP_QUALITY_MESSAGE = "replicas-desktop-quality";
|
|
52
|
+
var DESKTOP_CONNECTION_MESSAGE = "replicas-desktop-connection";
|
|
52
53
|
|
|
53
54
|
// src/input-validation.ts
|
|
54
55
|
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
@@ -102,6 +103,7 @@ var webrtcFirstFrameMs = 3e3;
|
|
|
102
103
|
var webrtcRetryFirstMs = 15e3;
|
|
103
104
|
var webrtcRetryMaxMs = 3e5;
|
|
104
105
|
var webrtcRetryTimeoutMs = 1e4;
|
|
106
|
+
var hiddenDisconnectMs = 6e4;
|
|
105
107
|
var webrtcSkipsAfterFailure = 3;
|
|
106
108
|
var pointerIntervalMs = 4;
|
|
107
109
|
var qualityWindowMs = 3e4;
|
|
@@ -113,8 +115,10 @@ try {
|
|
|
113
115
|
} catch {
|
|
114
116
|
}
|
|
115
117
|
var socket;
|
|
116
|
-
var
|
|
118
|
+
var intersecting = innerWidth > 0 && innerHeight > 0;
|
|
119
|
+
var standby = !intersecting;
|
|
117
120
|
var standbyTimer;
|
|
121
|
+
var hiddenTimer;
|
|
118
122
|
var config;
|
|
119
123
|
var transport;
|
|
120
124
|
var transportReason = "";
|
|
@@ -171,6 +175,24 @@ var restartQuality = () => {
|
|
|
171
175
|
qualitySequence = 0;
|
|
172
176
|
presentedAt = 0;
|
|
173
177
|
};
|
|
178
|
+
var newConnectionTotals = () => ({
|
|
179
|
+
sampledAt: 0,
|
|
180
|
+
presented: 0,
|
|
181
|
+
shownMs: 0,
|
|
182
|
+
frames: 0,
|
|
183
|
+
samples: 0,
|
|
184
|
+
rung: 0,
|
|
185
|
+
worstRung: 0,
|
|
186
|
+
cpu: 0,
|
|
187
|
+
rtt: 0,
|
|
188
|
+
rttSamples: 0,
|
|
189
|
+
worstRtt: 0,
|
|
190
|
+
loss: 0,
|
|
191
|
+
lossSamples: 0,
|
|
192
|
+
longestGapMs: 0,
|
|
193
|
+
reportedAt: 0
|
|
194
|
+
});
|
|
195
|
+
var connectionTotals = newConnectionTotals();
|
|
174
196
|
var state = {};
|
|
175
197
|
globalThis.__replicasDesktop = state;
|
|
176
198
|
var viewerEvents = [];
|
|
@@ -257,6 +279,31 @@ var announceTransport = () => {
|
|
|
257
279
|
if (window.parent === window) return;
|
|
258
280
|
window.parent.postMessage({ type: DESKTOP_TRANSPORT_MESSAGE, transport: transport ?? "connecting", reason: transportReason, path: route.path, codec: route.codec, failure, recovery }, "*");
|
|
259
281
|
};
|
|
282
|
+
var average = (sum, count) => count ? Math.round(sum * 10 / count) / 10 : void 0;
|
|
283
|
+
var reportConnection = (ended) => {
|
|
284
|
+
if (window.parent === window || !config) return;
|
|
285
|
+
const totals = connectionTotals;
|
|
286
|
+
totals.reportedAt = performance.now();
|
|
287
|
+
window.parent.postMessage({
|
|
288
|
+
type: DESKTOP_CONNECTION_MESSAGE,
|
|
289
|
+
serverVersion: config.serverVersion ?? "unknown",
|
|
290
|
+
firstFrameMs: totals.firstFrameAt === void 0 ? void 0 : Math.round(totals.firstFrameAt - connectionStartedAt),
|
|
291
|
+
shownMs: Math.round(totals.shownMs),
|
|
292
|
+
displayedFps: totals.shownMs >= 1e3 ? Math.round(totals.frames * 1e4 / totals.shownMs) / 10 : void 0,
|
|
293
|
+
inputRttMs: average(totals.rtt, totals.rttSamples),
|
|
294
|
+
worstInputRttMs: totals.rttSamples ? Math.round(totals.worstRtt) : void 0,
|
|
295
|
+
qualityRung: average(totals.rung, totals.samples),
|
|
296
|
+
worstQualityRung: totals.samples ? totals.worstRung : void 0,
|
|
297
|
+
longestFrameGapMs: totals.samples ? Math.round(totals.longestGapMs) : void 0,
|
|
298
|
+
lossPercent: average(totals.loss, totals.lossSamples),
|
|
299
|
+
cpuLoadPercent: average(totals.cpu, totals.samples),
|
|
300
|
+
ended
|
|
301
|
+
}, "*");
|
|
302
|
+
};
|
|
303
|
+
var endConnection = (ended) => {
|
|
304
|
+
if (ended || connectionTotals.sampledAt) reportConnection(ended);
|
|
305
|
+
connectionTotals = newConnectionTotals();
|
|
306
|
+
};
|
|
260
307
|
var closePeer = (preserveDeadline = false) => {
|
|
261
308
|
cancelRetry();
|
|
262
309
|
if (!preserveDeadline) {
|
|
@@ -310,9 +357,11 @@ var configureVideo = async (next, preferHardware = true) => {
|
|
|
310
357
|
if (configuration !== videoConfiguration) return;
|
|
311
358
|
const selectedConfig = support.supported && support.config ? support.config : decoderConfig(next);
|
|
312
359
|
activeVideoConfig = selectedConfig;
|
|
360
|
+
const totals = connectionTotals;
|
|
313
361
|
const decoder = new VideoDecoder({
|
|
314
362
|
output: (frame) => {
|
|
315
363
|
socketDecodedFrames += 1;
|
|
364
|
+
totals.firstFrameAt ??= performance.now();
|
|
316
365
|
const writer = videoWriter;
|
|
317
366
|
if (!writer || (writer.desiredSize ?? 1) <= 0) {
|
|
318
367
|
frame.close();
|
|
@@ -776,6 +825,7 @@ var handleServerText = (data, viaSocket) => {
|
|
|
776
825
|
};
|
|
777
826
|
var connect = () => {
|
|
778
827
|
if (document.visibilityState !== "visible") return;
|
|
828
|
+
endConnection();
|
|
779
829
|
clearTimeout(reconnectTimer);
|
|
780
830
|
closePeer();
|
|
781
831
|
transport = void 0;
|
|
@@ -818,9 +868,12 @@ var connect = () => {
|
|
|
818
868
|
});
|
|
819
869
|
nextSocket.addEventListener("close", () => {
|
|
820
870
|
if (socket !== nextSocket) return;
|
|
871
|
+
endConnection();
|
|
821
872
|
closePeer();
|
|
822
|
-
transport
|
|
823
|
-
|
|
873
|
+
if (transport) {
|
|
874
|
+
transport = void 0;
|
|
875
|
+
announceTransport();
|
|
876
|
+
}
|
|
824
877
|
reconnectAttempt += 1;
|
|
825
878
|
const delay = Math.min(8e3, 250 * 2 ** Math.min(reconnectAttempt, 5)) * (0.85 + Math.random() * 0.3);
|
|
826
879
|
note("socket closed", `reconnect in ${Math.round(delay)} ms`);
|
|
@@ -855,6 +908,8 @@ var observeFrame = () => {
|
|
|
855
908
|
lastPresentedCount = metadata.presentedFrames;
|
|
856
909
|
lastFrameAt = performance.now();
|
|
857
910
|
if (presentedAt && qualityWindow) qualityWindow.longestGapMs = Math.max(qualityWindow.longestGapMs, lastFrameAt - Math.max(presentedAt, qualityWindow.at));
|
|
911
|
+
const { shownSince } = connectionTotals;
|
|
912
|
+
if (presentedAt && shownSince !== void 0) connectionTotals.longestGapMs = Math.max(connectionTotals.longestGapMs, lastFrameAt - Math.max(presentedAt, shownSince));
|
|
858
913
|
presentedAt = lastFrameAt;
|
|
859
914
|
status.classList.remove("visible");
|
|
860
915
|
observeFrame();
|
|
@@ -909,22 +964,32 @@ var setStandby = (next) => {
|
|
|
909
964
|
sendSocket({ type: "standby", standby });
|
|
910
965
|
if (!standby && !audioPrompt.hidden) unlockAudio();
|
|
911
966
|
};
|
|
912
|
-
|
|
913
|
-
if (
|
|
967
|
+
var syncStandby = () => {
|
|
968
|
+
if (intersecting && document.visibilityState === "visible") setStandby(false);
|
|
914
969
|
else if (!standby) {
|
|
915
970
|
clearTimeout(standbyTimer);
|
|
916
971
|
standbyTimer = setTimeout(() => setStandby(true), 1e3);
|
|
917
972
|
}
|
|
973
|
+
};
|
|
974
|
+
new IntersectionObserver(([entry]) => {
|
|
975
|
+
intersecting = entry.isIntersecting;
|
|
976
|
+
syncStandby();
|
|
918
977
|
}).observe(document.documentElement);
|
|
919
978
|
window.addEventListener("online", connect);
|
|
920
979
|
document.addEventListener("visibilitychange", () => {
|
|
980
|
+
clearTimeout(hiddenTimer);
|
|
981
|
+
syncStandby();
|
|
921
982
|
if (document.visibilityState === "hidden") {
|
|
922
|
-
|
|
983
|
+
hiddenTimer = setTimeout(() => {
|
|
984
|
+
endConnection("hidden");
|
|
985
|
+
disconnect();
|
|
986
|
+
}, hiddenDisconnectMs);
|
|
923
987
|
return;
|
|
924
988
|
}
|
|
925
|
-
connect();
|
|
989
|
+
if (!socket || socket.readyState >= WebSocket.CLOSING) connect();
|
|
926
990
|
});
|
|
927
991
|
window.addEventListener("pagehide", () => {
|
|
992
|
+
endConnection();
|
|
928
993
|
disconnect();
|
|
929
994
|
screen.cancelVideoFrameCallback(frameCallback);
|
|
930
995
|
videoDecoder?.close();
|
|
@@ -963,6 +1028,32 @@ var updateDiagnostics = (stats) => {
|
|
|
963
1028
|
viewerEvents.length ? `viewer events: ${describeEvents(viewerEvents.slice(-5))}` : ""
|
|
964
1029
|
].filter(Boolean).join("\n");
|
|
965
1030
|
};
|
|
1031
|
+
var sampleConnection = (stats, current, now) => {
|
|
1032
|
+
const totals = connectionTotals;
|
|
1033
|
+
const shown = !standby && document.visibilityState === "visible" && qualityUnseen.samples < 4;
|
|
1034
|
+
if (shown && totals.sampledAt) {
|
|
1035
|
+
totals.shownSince ??= totals.sampledAt;
|
|
1036
|
+
totals.shownMs += now - totals.sampledAt;
|
|
1037
|
+
totals.frames += presentedFrames - totals.presented;
|
|
1038
|
+
totals.samples += 1;
|
|
1039
|
+
totals.rung += current.encoder.rung;
|
|
1040
|
+
totals.worstRung = Math.max(totals.worstRung, current.encoder.rung);
|
|
1041
|
+
totals.cpu += current.cpu.load * 100 / current.cpu.cores;
|
|
1042
|
+
if (stats.rttMs > 0) {
|
|
1043
|
+
totals.rtt += stats.rttMs;
|
|
1044
|
+
totals.rttSamples += 1;
|
|
1045
|
+
totals.worstRtt = Math.max(totals.worstRtt, stats.rttMs);
|
|
1046
|
+
}
|
|
1047
|
+
if (stats.lossPercent !== void 0) {
|
|
1048
|
+
totals.loss += stats.lossPercent;
|
|
1049
|
+
totals.lossSamples += 1;
|
|
1050
|
+
}
|
|
1051
|
+
totals.longestGapMs = Math.max(totals.longestGapMs, now - Math.max(presentedAt, totals.shownSince));
|
|
1052
|
+
} else if (!shown) totals.shownSince = void 0;
|
|
1053
|
+
totals.sampledAt = now;
|
|
1054
|
+
totals.presented = presentedFrames;
|
|
1055
|
+
if (now - totals.reportedAt >= 2e3) reportConnection();
|
|
1056
|
+
};
|
|
966
1057
|
var reportQuality = (stats) => {
|
|
967
1058
|
if (window.parent === window || !transport || !config || !telemetry) return;
|
|
968
1059
|
const now = performance.now();
|
|
@@ -970,6 +1061,7 @@ var reportQuality = (stats) => {
|
|
|
970
1061
|
const decoded = transport === "webrtc" ? inbound?.framesDecoded ?? 0 : socketDecodedFrames;
|
|
971
1062
|
qualityUnseen = { decoded, presented: presentedFrames, samples: decoded > qualityUnseen.decoded && presentedFrames === qualityUnseen.presented ? qualityUnseen.samples + 1 : 0 };
|
|
972
1063
|
if (qualityUnseen.samples >= 4) current.unseen = true;
|
|
1064
|
+
sampleConnection(stats, telemetry, now);
|
|
973
1065
|
qualitySamples.push({ stats, telemetry });
|
|
974
1066
|
const windowMs = now - current.at;
|
|
975
1067
|
if (windowMs < qualityWindowMs) return;
|
|
@@ -1048,6 +1140,7 @@ var reportWebRtcStats = async (pc, stats) => {
|
|
|
1048
1140
|
};
|
|
1049
1141
|
if (counters.framesDecoded > (inbound?.framesDecoded ?? 0)) {
|
|
1050
1142
|
lastFrameAt = now;
|
|
1143
|
+
connectionTotals.firstFrameAt ??= now;
|
|
1051
1144
|
clearTimeout(webrtcDeadline);
|
|
1052
1145
|
webrtcDeadline = void 0;
|
|
1053
1146
|
if (fullChromaFailure) {
|