@camstack/ui-library 1.2.181 → 1.2.183
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/composites/camera-stream-player.d.ts +15 -2
- package/dist/composites/index.d.ts +4 -0
- package/dist/composites/stream-debug/stream-debug-overlay.d.ts +44 -0
- package/dist/composites/stream-debug/webrtc-debug-stats.d.ts +129 -0
- package/dist/generated/system-hooks.d.ts +2 -0
- package/dist/hooks/use-device-webrtc.d.ts +15 -1
- package/dist/index.cjs +659 -36
- package/dist/index.js +656 -37
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7213,6 +7213,8 @@ var useWebrtcSessionCloseSession = trpc.webrtcSession.closeSession.useMutation;
|
|
|
7213
7213
|
var useWebrtcSessionHasAdaptiveBitrate = trpc.webrtcSession.hasAdaptiveBitrate.useQuery;
|
|
7214
7214
|
/** Generated alias around `trpc.webrtcSession.getSessionState.useQuery`. */
|
|
7215
7215
|
var useWebrtcSessionGetSessionState = trpc.webrtcSession.getSessionState.useQuery;
|
|
7216
|
+
/** Generated alias around `trpc.webrtcSession.getSessionDebug.useQuery`. */
|
|
7217
|
+
var useWebrtcSessionGetSessionDebug = trpc.webrtcSession.getSessionDebug.useQuery;
|
|
7216
7218
|
/** Generated alias around `trpc.zoneAnalytics.getCurrentSnapshot.useQuery`. */
|
|
7217
7219
|
var useZoneAnalyticsGetCurrentSnapshot = trpc.zoneAnalytics.getCurrentSnapshot.useQuery;
|
|
7218
7220
|
/** Generated alias around `trpc.zoneAnalytics.getCurrentSnapshotBatch.useQuery`. */
|
|
@@ -26976,6 +26978,590 @@ async function measureDownlinkMbps(origin, deps) {
|
|
|
26976
26978
|
}
|
|
26977
26979
|
}
|
|
26978
26980
|
//#endregion
|
|
26981
|
+
//#region src/composites/stream-debug/webrtc-debug-stats.ts
|
|
26982
|
+
/** Beyond this the log is a scroll, not a signal. */
|
|
26983
|
+
var RESOLUTION_LOG_MAX = 8;
|
|
26984
|
+
function num(value) {
|
|
26985
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
26986
|
+
}
|
|
26987
|
+
function str(value) {
|
|
26988
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
26989
|
+
}
|
|
26990
|
+
/** Δ of two cumulative counters. Null unless BOTH ends were measured; a
|
|
26991
|
+
* counter that went backwards (a reset) yields null rather than a negative. */
|
|
26992
|
+
function delta(now, before) {
|
|
26993
|
+
if (now === null || before === null) return null;
|
|
26994
|
+
const d = now - before;
|
|
26995
|
+
return d < 0 ? null : d;
|
|
26996
|
+
}
|
|
26997
|
+
function lossPercentOf(lost, received) {
|
|
26998
|
+
if (lost === null || received === null) return null;
|
|
26999
|
+
const total = lost + received;
|
|
27000
|
+
if (total <= 0) return null;
|
|
27001
|
+
return lost / total * 100;
|
|
27002
|
+
}
|
|
27003
|
+
function profileLevelIdOf(fmtp) {
|
|
27004
|
+
if (fmtp === null) return null;
|
|
27005
|
+
return /profile-level-id=([0-9a-fA-F]+)/.exec(fmtp)?.[1] ?? null;
|
|
27006
|
+
}
|
|
27007
|
+
function collect(report) {
|
|
27008
|
+
const out = {
|
|
27009
|
+
inbound: null,
|
|
27010
|
+
codecs: /* @__PURE__ */ new Map(),
|
|
27011
|
+
candidates: /* @__PURE__ */ new Map(),
|
|
27012
|
+
pair: null,
|
|
27013
|
+
selectedPairId: null
|
|
27014
|
+
};
|
|
27015
|
+
const pairs = [];
|
|
27016
|
+
report.forEach((raw) => {
|
|
27017
|
+
const s = raw;
|
|
27018
|
+
const id = str(s.id);
|
|
27019
|
+
switch (s.type) {
|
|
27020
|
+
case "inbound-rtp":
|
|
27021
|
+
if ((s.kind ?? s.mediaType) === "video") out.inbound = s;
|
|
27022
|
+
break;
|
|
27023
|
+
case "codec":
|
|
27024
|
+
if (id !== null) out.codecs.set(id, s);
|
|
27025
|
+
break;
|
|
27026
|
+
case "local-candidate":
|
|
27027
|
+
case "remote-candidate":
|
|
27028
|
+
if (id !== null) out.candidates.set(id, s);
|
|
27029
|
+
break;
|
|
27030
|
+
case "candidate-pair":
|
|
27031
|
+
pairs.push(s);
|
|
27032
|
+
break;
|
|
27033
|
+
case "transport":
|
|
27034
|
+
out.selectedPairId = str(s.selectedCandidatePairId);
|
|
27035
|
+
break;
|
|
27036
|
+
default: break;
|
|
27037
|
+
}
|
|
27038
|
+
});
|
|
27039
|
+
const byId = out.selectedPairId;
|
|
27040
|
+
out.pair = pairs.find((p) => str(p.id) === byId) ?? pairs.find((p) => p.selected === true) ?? pairs.find((p) => p.nominated === true && p.state === "succeeded") ?? pairs.find((p) => p.nominated === true) ?? pairs.find((p) => p.state === "succeeded") ?? null;
|
|
27041
|
+
return out;
|
|
27042
|
+
}
|
|
27043
|
+
function readCounters(inbound) {
|
|
27044
|
+
if (inbound === null) return {
|
|
27045
|
+
timestamp: null,
|
|
27046
|
+
bytesReceived: null,
|
|
27047
|
+
packetsReceived: null,
|
|
27048
|
+
packetsLost: null,
|
|
27049
|
+
framesDecoded: null,
|
|
27050
|
+
framesDropped: null,
|
|
27051
|
+
freezeCount: null,
|
|
27052
|
+
totalFreezesDurationS: null,
|
|
27053
|
+
totalDecodeTimeS: null,
|
|
27054
|
+
nackCount: null,
|
|
27055
|
+
pliCount: null
|
|
27056
|
+
};
|
|
27057
|
+
return {
|
|
27058
|
+
timestamp: num(inbound.timestamp),
|
|
27059
|
+
bytesReceived: num(inbound.bytesReceived),
|
|
27060
|
+
packetsReceived: num(inbound.packetsReceived),
|
|
27061
|
+
packetsLost: num(inbound.packetsLost),
|
|
27062
|
+
framesDecoded: num(inbound.framesDecoded),
|
|
27063
|
+
framesDropped: num(inbound.framesDropped),
|
|
27064
|
+
freezeCount: num(inbound.freezeCount),
|
|
27065
|
+
totalFreezesDurationS: num(inbound.totalFreezesDuration),
|
|
27066
|
+
totalDecodeTimeS: num(inbound.totalDecodeTime),
|
|
27067
|
+
nackCount: num(inbound.nackCount),
|
|
27068
|
+
pliCount: num(inbound.pliCount)
|
|
27069
|
+
};
|
|
27070
|
+
}
|
|
27071
|
+
var EMPTY_WINDOW = {
|
|
27072
|
+
spanMs: null,
|
|
27073
|
+
kbps: null,
|
|
27074
|
+
packetsReceived: null,
|
|
27075
|
+
packetsLost: null,
|
|
27076
|
+
lossPercent: null,
|
|
27077
|
+
framesDecoded: null,
|
|
27078
|
+
framesDropped: null,
|
|
27079
|
+
freezeCount: null,
|
|
27080
|
+
freezeMs: null,
|
|
27081
|
+
nackCount: null,
|
|
27082
|
+
pliCount: null,
|
|
27083
|
+
decodeMs: null
|
|
27084
|
+
};
|
|
27085
|
+
function windowOf(now, before) {
|
|
27086
|
+
if (before === null) return EMPTY_WINDOW;
|
|
27087
|
+
const spanMs = delta(now.timestamp, before.timestamp);
|
|
27088
|
+
if (spanMs === null || spanMs <= 0) return EMPTY_WINDOW;
|
|
27089
|
+
const bytes = delta(now.bytesReceived, before.bytesReceived);
|
|
27090
|
+
const lost = delta(now.packetsLost, before.packetsLost);
|
|
27091
|
+
const received = delta(now.packetsReceived, before.packetsReceived);
|
|
27092
|
+
const decoded = delta(now.framesDecoded, before.framesDecoded);
|
|
27093
|
+
const decodeS = delta(now.totalDecodeTimeS, before.totalDecodeTimeS);
|
|
27094
|
+
const freezeS = delta(now.totalFreezesDurationS, before.totalFreezesDurationS);
|
|
27095
|
+
return {
|
|
27096
|
+
spanMs,
|
|
27097
|
+
kbps: bytes === null ? null : Math.round(bytes * 8 / spanMs),
|
|
27098
|
+
packetsReceived: received,
|
|
27099
|
+
packetsLost: lost,
|
|
27100
|
+
lossPercent: lossPercentOf(lost, received),
|
|
27101
|
+
framesDecoded: decoded,
|
|
27102
|
+
framesDropped: delta(now.framesDropped, before.framesDropped),
|
|
27103
|
+
freezeCount: delta(now.freezeCount, before.freezeCount),
|
|
27104
|
+
freezeMs: freezeS === null ? null : Math.round(freezeS * 1e3),
|
|
27105
|
+
nackCount: delta(now.nackCount, before.nackCount),
|
|
27106
|
+
pliCount: delta(now.pliCount, before.pliCount),
|
|
27107
|
+
decodeMs: decodeS === null || decoded === null || decoded <= 0 ? null : decodeS / decoded * 1e3
|
|
27108
|
+
};
|
|
27109
|
+
}
|
|
27110
|
+
function resolutionLogOf(prev, width, height, at) {
|
|
27111
|
+
if (width === null || height === null) return prev;
|
|
27112
|
+
const last = prev.at(-1);
|
|
27113
|
+
if (last !== void 0 && last.width === width && last.height === height) return prev;
|
|
27114
|
+
const next = [...prev, {
|
|
27115
|
+
width,
|
|
27116
|
+
height,
|
|
27117
|
+
at
|
|
27118
|
+
}];
|
|
27119
|
+
return next.length > RESOLUTION_LOG_MAX ? next.slice(next.length - RESOLUTION_LOG_MAX) : next;
|
|
27120
|
+
}
|
|
27121
|
+
/**
|
|
27122
|
+
* Derive one debug sample. Immutable: `prev` is read, never mutated, and the
|
|
27123
|
+
* returned object is the input for the next call.
|
|
27124
|
+
*/
|
|
27125
|
+
function deriveWebrtcDebugStats(input) {
|
|
27126
|
+
const { inbound, codecs, candidates, pair } = collect(input.report);
|
|
27127
|
+
const raw = readCounters(inbound);
|
|
27128
|
+
const codecStat = inbound !== null ? codecs.get(str(inbound.codecId) ?? "") : void 0;
|
|
27129
|
+
const mime = str(codecStat?.mimeType);
|
|
27130
|
+
const fmtp = str(codecStat?.sdpFmtpLine);
|
|
27131
|
+
const localCand = pair !== null ? candidates.get(str(pair.localCandidateId) ?? "") : void 0;
|
|
27132
|
+
const remoteCand = pair !== null ? candidates.get(str(pair.remoteCandidateId) ?? "") : void 0;
|
|
27133
|
+
const localType = str(localCand?.candidateType);
|
|
27134
|
+
const remoteType = str(remoteCand?.candidateType);
|
|
27135
|
+
const width = num(inbound?.frameWidth);
|
|
27136
|
+
const height = num(inbound?.frameHeight);
|
|
27137
|
+
const totalFreezeS = raw.totalFreezesDurationS;
|
|
27138
|
+
const decodeS = raw.totalDecodeTimeS;
|
|
27139
|
+
const decodedTotal = raw.framesDecoded;
|
|
27140
|
+
return {
|
|
27141
|
+
at: input.now,
|
|
27142
|
+
hasInboundVideo: inbound !== null,
|
|
27143
|
+
width,
|
|
27144
|
+
height,
|
|
27145
|
+
fps: num(inbound?.framesPerSecond),
|
|
27146
|
+
codec: mime === null ? null : mime.split("/")[1] ?? mime,
|
|
27147
|
+
codecProfileLevelId: profileLevelIdOf(fmtp),
|
|
27148
|
+
jitterMs: jitterMsOf(inbound),
|
|
27149
|
+
packetsReceived: raw.packetsReceived,
|
|
27150
|
+
packetsLost: raw.packetsLost,
|
|
27151
|
+
lossPercent: lossPercentOf(raw.packetsLost, raw.packetsReceived),
|
|
27152
|
+
nackCount: raw.nackCount,
|
|
27153
|
+
pliCount: raw.pliCount,
|
|
27154
|
+
framesDecoded: decodedTotal,
|
|
27155
|
+
framesDropped: raw.framesDropped,
|
|
27156
|
+
freezeCount: raw.freezeCount,
|
|
27157
|
+
totalFreezeMs: totalFreezeS === null ? null : Math.round(totalFreezeS * 1e3),
|
|
27158
|
+
avgDecodeMs: decodeS === null || decodedTotal === null || decodedTotal <= 0 ? null : decodeS / decodedTotal * 1e3,
|
|
27159
|
+
rttMs: rttMsOf(pair),
|
|
27160
|
+
availableIncomingKbps: availableKbpsOf(pair),
|
|
27161
|
+
localCandidateType: localType,
|
|
27162
|
+
remoteCandidateType: remoteType,
|
|
27163
|
+
candidatePairType: localType === null && remoteType === null ? null : `${localType ?? "?"}/${remoteType ?? "?"}`,
|
|
27164
|
+
candidateProtocol: str(localCand?.protocol),
|
|
27165
|
+
iceConnectionState: input.iceConnectionState,
|
|
27166
|
+
dtlsState: input.dtlsState,
|
|
27167
|
+
window: windowOf(raw, input.prev?.raw ?? null),
|
|
27168
|
+
resolutionLog: resolutionLogOf(input.prev?.resolutionLog ?? [], width, height, input.now),
|
|
27169
|
+
raw
|
|
27170
|
+
};
|
|
27171
|
+
}
|
|
27172
|
+
function jitterMsOf(inbound) {
|
|
27173
|
+
const jitter = num(inbound?.jitter);
|
|
27174
|
+
return jitter === null ? null : jitter * 1e3;
|
|
27175
|
+
}
|
|
27176
|
+
function rttMsOf(pair) {
|
|
27177
|
+
const rtt = num(pair?.currentRoundTripTime);
|
|
27178
|
+
return rtt === null ? null : rtt * 1e3;
|
|
27179
|
+
}
|
|
27180
|
+
function availableKbpsOf(pair) {
|
|
27181
|
+
const bps = num(pair?.availableIncomingBitrate);
|
|
27182
|
+
return bps === null ? null : Math.round(bps / 1e3);
|
|
27183
|
+
}
|
|
27184
|
+
var VERDICTS = {
|
|
27185
|
+
unknown: "no window yet",
|
|
27186
|
+
decoding: "decoding",
|
|
27187
|
+
"decoder-stall": "DECODER STALL — bytes arriving, no frames out",
|
|
27188
|
+
"transport-stall": "TRANSPORT STALL — nothing arriving",
|
|
27189
|
+
"frozen-unknown": "frames stopped — byte rate unknown"
|
|
27190
|
+
};
|
|
27191
|
+
function freezeVerdict(stats) {
|
|
27192
|
+
const kind = freezeVerdictKind(stats);
|
|
27193
|
+
return {
|
|
27194
|
+
kind,
|
|
27195
|
+
label: VERDICTS[kind]
|
|
27196
|
+
};
|
|
27197
|
+
}
|
|
27198
|
+
function freezeVerdictKind(stats) {
|
|
27199
|
+
if (stats === null || !stats.hasInboundVideo) return "unknown";
|
|
27200
|
+
const decoded = stats.window.framesDecoded;
|
|
27201
|
+
if (decoded === null) return "unknown";
|
|
27202
|
+
if (decoded > 0) return "decoding";
|
|
27203
|
+
const kbps = stats.window.kbps;
|
|
27204
|
+
if (kbps === null) return "frozen-unknown";
|
|
27205
|
+
return kbps > 0 ? "decoder-stall" : "transport-stall";
|
|
27206
|
+
}
|
|
27207
|
+
//#endregion
|
|
27208
|
+
//#region src/composites/stream-debug/stream-debug-overlay.tsx
|
|
27209
|
+
/** `—` is the ONLY rendering of unknown. Never an empty cell, never 0. */
|
|
27210
|
+
var UNKNOWN = "—";
|
|
27211
|
+
function n(value, opts) {
|
|
27212
|
+
if (value === null || value === void 0 || !Number.isFinite(value)) return UNKNOWN;
|
|
27213
|
+
const digits = opts?.digits ?? 0;
|
|
27214
|
+
return `${value.toFixed(digits)}${opts?.suffix ?? ""}`;
|
|
27215
|
+
}
|
|
27216
|
+
function s(value) {
|
|
27217
|
+
return value === null || value === void 0 || value === "" ? UNKNOWN : value;
|
|
27218
|
+
}
|
|
27219
|
+
function b(value) {
|
|
27220
|
+
return value === null || value === void 0 ? UNKNOWN : value ? "yes" : "no";
|
|
27221
|
+
}
|
|
27222
|
+
/** Age of a measurement, in the form an operator reads at a glance. */
|
|
27223
|
+
function age(at, now) {
|
|
27224
|
+
if (at === null) return UNKNOWN;
|
|
27225
|
+
const ms = Math.max(0, now - at);
|
|
27226
|
+
if (ms < 1e3) return `${ms}ms ago`;
|
|
27227
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s ago`;
|
|
27228
|
+
return `${Math.floor(ms / 6e4)}m${Math.floor(ms % 6e4 / 1e3)}s ago`;
|
|
27229
|
+
}
|
|
27230
|
+
/** A measurement older than this is NOT live, and is said to be. */
|
|
27231
|
+
var STALE_AFTER_MS = 3e3;
|
|
27232
|
+
var VERDICT_CLASS = {
|
|
27233
|
+
unknown: "bg-white/15 text-white/70",
|
|
27234
|
+
decoding: "bg-emerald-500/25 text-emerald-200",
|
|
27235
|
+
"decoder-stall": "bg-amber-500/30 text-amber-100",
|
|
27236
|
+
"transport-stall": "bg-rose-500/30 text-rose-100",
|
|
27237
|
+
"frozen-unknown": "bg-white/15 text-white/70"
|
|
27238
|
+
};
|
|
27239
|
+
function Row({ label, value, window, strong = false }) {
|
|
27240
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
27241
|
+
className: "flex items-baseline justify-between gap-2 leading-[1.25]",
|
|
27242
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27243
|
+
className: "text-white/55 shrink-0",
|
|
27244
|
+
children: label
|
|
27245
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
27246
|
+
className: "flex items-baseline gap-1 text-right",
|
|
27247
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27248
|
+
className: strong ? "text-white font-semibold tabular-nums" : "text-white/90 tabular-nums",
|
|
27249
|
+
children: value
|
|
27250
|
+
}), window !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27251
|
+
className: "text-sky-300/80 tabular-nums",
|
|
27252
|
+
children: window
|
|
27253
|
+
})]
|
|
27254
|
+
})]
|
|
27255
|
+
});
|
|
27256
|
+
}
|
|
27257
|
+
function SectionHeader({ title, measuredAt, now }) {
|
|
27258
|
+
const stale = measuredAt !== null && now - measuredAt > STALE_AFTER_MS;
|
|
27259
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
27260
|
+
className: "flex items-baseline justify-between gap-3 mt-2 first:mt-0 pb-0.5 border-b border-white/20",
|
|
27261
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27262
|
+
className: "text-white/80 font-semibold tracking-wide uppercase text-[9px]",
|
|
27263
|
+
children: title
|
|
27264
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
27265
|
+
className: stale ? "text-amber-300 text-[9px]" : "text-white/45 text-[9px]",
|
|
27266
|
+
children: [stale ? "STALE · " : "", age(measuredAt, now)]
|
|
27267
|
+
})]
|
|
27268
|
+
});
|
|
27269
|
+
}
|
|
27270
|
+
/** The pill's dot: the verdict, in the one place that is always visible. */
|
|
27271
|
+
var VERDICT_DOT = {
|
|
27272
|
+
unknown: "bg-white/50",
|
|
27273
|
+
decoding: "bg-emerald-400",
|
|
27274
|
+
"decoder-stall": "bg-amber-400",
|
|
27275
|
+
"transport-stall": "bg-rose-400",
|
|
27276
|
+
"frozen-unknown": "bg-white/50"
|
|
27277
|
+
};
|
|
27278
|
+
function StreamDebugOverlay(props) {
|
|
27279
|
+
const { stats, server, serverError, serverWired, now, sessionId } = props;
|
|
27280
|
+
const verdict = freezeVerdict(stats);
|
|
27281
|
+
const win = stats?.window;
|
|
27282
|
+
const [expanded, setExpanded] = (0, react$1.useState)(false);
|
|
27283
|
+
const stale = stats !== null && now - stats.at > STALE_AFTER_MS;
|
|
27284
|
+
if (!expanded) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
27285
|
+
type: "button",
|
|
27286
|
+
onClick: () => setExpanded(true),
|
|
27287
|
+
className: "absolute bottom-10 left-2 z-20 transform-gpu flex items-center gap-1.5 rounded bg-neutral-950/90 ring-1 ring-white/20 px-1.5 py-0.5 text-[9px] font-mono text-white/85 tabular-nums select-text",
|
|
27288
|
+
"data-testid": "stream-debug-pill",
|
|
27289
|
+
title: verdict.label,
|
|
27290
|
+
children: [
|
|
27291
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `h-1.5 w-1.5 rounded-full ${VERDICT_DOT[verdict.kind]}` }),
|
|
27292
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: stats?.width !== null && stats?.width !== void 0 ? `${stats.width}×${stats.height}` : UNKNOWN }),
|
|
27293
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [n(stats?.fps), "fps"] }),
|
|
27294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [n(win?.kbps), "k"] }),
|
|
27295
|
+
stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27296
|
+
className: "text-amber-300",
|
|
27297
|
+
children: "stale"
|
|
27298
|
+
})
|
|
27299
|
+
]
|
|
27300
|
+
});
|
|
27301
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
27302
|
+
className: "absolute bottom-10 left-2 z-20 transform-gpu w-[13.5rem] max-h-[70%] overflow-y-auto rounded-md bg-neutral-950/95 ring-1 ring-white/25 shadow-lg px-1.5 py-1 text-[9px] font-mono text-white/90 select-text",
|
|
27303
|
+
"data-testid": "stream-debug-overlay",
|
|
27304
|
+
children: [
|
|
27305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
27306
|
+
type: "button",
|
|
27307
|
+
onClick: () => setExpanded(false),
|
|
27308
|
+
className: `w-full text-left rounded px-1.5 py-0.5 mb-1 text-[9px] font-semibold ${VERDICT_CLASS[verdict.kind]}`,
|
|
27309
|
+
children: verdict.label
|
|
27310
|
+
}),
|
|
27311
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionHeader, {
|
|
27312
|
+
title: "decode",
|
|
27313
|
+
measuredAt: stats?.at ?? null,
|
|
27314
|
+
now
|
|
27315
|
+
}),
|
|
27316
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27317
|
+
label: "framesDecoded",
|
|
27318
|
+
value: n(stats?.framesDecoded),
|
|
27319
|
+
window: win?.framesDecoded === null ? void 0 : `+${n(win?.framesDecoded)}`,
|
|
27320
|
+
strong: true
|
|
27321
|
+
}),
|
|
27322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27323
|
+
label: "freezeCount",
|
|
27324
|
+
value: n(stats?.freezeCount),
|
|
27325
|
+
strong: true
|
|
27326
|
+
}),
|
|
27327
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27328
|
+
label: "frozen",
|
|
27329
|
+
value: n(stats?.totalFreezeMs, { suffix: "ms" }),
|
|
27330
|
+
window: win?.freezeMs === null ? void 0 : `+${n(win?.freezeMs)}ms`,
|
|
27331
|
+
strong: true
|
|
27332
|
+
}),
|
|
27333
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27334
|
+
label: "pliCount",
|
|
27335
|
+
value: n(stats?.pliCount),
|
|
27336
|
+
window: win?.pliCount === null ? void 0 : `+${n(win?.pliCount)}`,
|
|
27337
|
+
strong: true
|
|
27338
|
+
}),
|
|
27339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27340
|
+
label: "framesDropped",
|
|
27341
|
+
value: n(stats?.framesDropped)
|
|
27342
|
+
}),
|
|
27343
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27344
|
+
label: "decode",
|
|
27345
|
+
value: n(stats?.avgDecodeMs, {
|
|
27346
|
+
digits: 1,
|
|
27347
|
+
suffix: "ms"
|
|
27348
|
+
}),
|
|
27349
|
+
window: win?.decodeMs === null ? void 0 : `${n(win?.decodeMs, { digits: 1 })}ms`
|
|
27350
|
+
}),
|
|
27351
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionHeader, {
|
|
27352
|
+
title: "picture",
|
|
27353
|
+
measuredAt: stats?.at ?? null,
|
|
27354
|
+
now
|
|
27355
|
+
}),
|
|
27356
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27357
|
+
label: "resolution",
|
|
27358
|
+
value: stats?.width !== null && stats?.width !== void 0 && stats.height !== null ? `${stats.width}×${stats.height}` : UNKNOWN
|
|
27359
|
+
}),
|
|
27360
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27361
|
+
label: "fps",
|
|
27362
|
+
value: n(stats?.fps)
|
|
27363
|
+
}),
|
|
27364
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27365
|
+
label: "codec",
|
|
27366
|
+
value: stats?.codec === null || stats?.codec === void 0 ? UNKNOWN : `${stats.codec}${stats.codecProfileLevelId !== null ? ` ${stats.codecProfileLevelId}` : ""}`
|
|
27367
|
+
}),
|
|
27368
|
+
(stats?.resolutionLog.length ?? 0) > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27369
|
+
className: "mt-0.5 text-white/60 leading-tight",
|
|
27370
|
+
children: stats?.resolutionLog.map((mark) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
27371
|
+
className: "flex justify-between",
|
|
27372
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
27373
|
+
mark.width,
|
|
27374
|
+
"×",
|
|
27375
|
+
mark.height
|
|
27376
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
27377
|
+
className: "text-white/40",
|
|
27378
|
+
children: age(mark.at, now)
|
|
27379
|
+
})]
|
|
27380
|
+
}, `${mark.at}-${mark.width}x${mark.height}`))
|
|
27381
|
+
}),
|
|
27382
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionHeader, {
|
|
27383
|
+
title: "transport",
|
|
27384
|
+
measuredAt: stats?.at ?? null,
|
|
27385
|
+
now
|
|
27386
|
+
}),
|
|
27387
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27388
|
+
label: "bitrate",
|
|
27389
|
+
value: n(win?.kbps, { suffix: " kbps" })
|
|
27390
|
+
}),
|
|
27391
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27392
|
+
label: "rtt",
|
|
27393
|
+
value: n(stats?.rttMs, {
|
|
27394
|
+
digits: 0,
|
|
27395
|
+
suffix: "ms"
|
|
27396
|
+
})
|
|
27397
|
+
}),
|
|
27398
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27399
|
+
label: "jitter",
|
|
27400
|
+
value: n(stats?.jitterMs, {
|
|
27401
|
+
digits: 1,
|
|
27402
|
+
suffix: "ms"
|
|
27403
|
+
})
|
|
27404
|
+
}),
|
|
27405
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27406
|
+
label: "packetsLost",
|
|
27407
|
+
value: n(stats?.packetsLost),
|
|
27408
|
+
window: win?.packetsLost === null ? void 0 : `+${n(win?.packetsLost)}`
|
|
27409
|
+
}),
|
|
27410
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27411
|
+
label: "loss",
|
|
27412
|
+
value: n(stats?.lossPercent, {
|
|
27413
|
+
digits: 2,
|
|
27414
|
+
suffix: "%"
|
|
27415
|
+
}),
|
|
27416
|
+
window: win?.lossPercent === null ? void 0 : n(win?.lossPercent, {
|
|
27417
|
+
digits: 2,
|
|
27418
|
+
suffix: "%"
|
|
27419
|
+
})
|
|
27420
|
+
}),
|
|
27421
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27422
|
+
label: "nackCount",
|
|
27423
|
+
value: n(stats?.nackCount),
|
|
27424
|
+
window: win?.nackCount === null ? void 0 : `+${n(win?.nackCount)}`
|
|
27425
|
+
}),
|
|
27426
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27427
|
+
label: "candidate pair",
|
|
27428
|
+
value: s(stats?.candidatePairType)
|
|
27429
|
+
}),
|
|
27430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27431
|
+
label: "protocol",
|
|
27432
|
+
value: s(stats?.candidateProtocol)
|
|
27433
|
+
}),
|
|
27434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27435
|
+
label: "ice",
|
|
27436
|
+
value: s(stats?.iceConnectionState)
|
|
27437
|
+
}),
|
|
27438
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27439
|
+
label: "dtls",
|
|
27440
|
+
value: s(stats?.dtlsState)
|
|
27441
|
+
}),
|
|
27442
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionHeader, {
|
|
27443
|
+
title: "server",
|
|
27444
|
+
measuredAt: server?.observedAt ?? null,
|
|
27445
|
+
now
|
|
27446
|
+
}),
|
|
27447
|
+
!serverWired ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27448
|
+
className: "text-white/50 leading-tight py-0.5",
|
|
27449
|
+
children: "not wired on this host"
|
|
27450
|
+
}) : serverError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27451
|
+
className: "text-rose-300 leading-tight py-0.5 break-words",
|
|
27452
|
+
children: serverError
|
|
27453
|
+
}) : server === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27454
|
+
className: "text-white/50 leading-tight py-0.5",
|
|
27455
|
+
children: "waiting for the first answer…"
|
|
27456
|
+
}) : !server.alive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27457
|
+
className: "text-amber-300 leading-tight py-0.5",
|
|
27458
|
+
children: "session not found on the server — it ended, or this node never had it"
|
|
27459
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
27460
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27461
|
+
label: "served rung",
|
|
27462
|
+
value: s(server.servedTier),
|
|
27463
|
+
strong: true
|
|
27464
|
+
}),
|
|
27465
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27466
|
+
label: "adaptive",
|
|
27467
|
+
value: b(server.adaptive)
|
|
27468
|
+
}),
|
|
27469
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27470
|
+
label: "source",
|
|
27471
|
+
value: s(server.camStreamId)
|
|
27472
|
+
}),
|
|
27473
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27474
|
+
label: "source kind",
|
|
27475
|
+
value: server.sourceKind
|
|
27476
|
+
}),
|
|
27477
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27478
|
+
label: "ingest node",
|
|
27479
|
+
value: s(server.ingestNodeId)
|
|
27480
|
+
}),
|
|
27481
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27482
|
+
label: "· remote",
|
|
27483
|
+
value: b(server.ingestIsRemote)
|
|
27484
|
+
}),
|
|
27485
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27486
|
+
label: "egress leg",
|
|
27487
|
+
value: server.egressLeg,
|
|
27488
|
+
strong: true
|
|
27489
|
+
}),
|
|
27490
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27491
|
+
label: "codec src→out",
|
|
27492
|
+
value: `${s(server.sourceCodec)}→${s(server.negotiatedCodec)}`
|
|
27493
|
+
}),
|
|
27494
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27495
|
+
label: "broker",
|
|
27496
|
+
value: s(server.brokerStatus)
|
|
27497
|
+
}),
|
|
27498
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27499
|
+
label: "· bitrate",
|
|
27500
|
+
value: n(server.brokerBitrateKbps, { suffix: " kbps" })
|
|
27501
|
+
}),
|
|
27502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27503
|
+
label: "· input fps",
|
|
27504
|
+
value: n(server.brokerInputFps)
|
|
27505
|
+
}),
|
|
27506
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27507
|
+
label: "rtp sent",
|
|
27508
|
+
value: n(server.rtpPacketsSent)
|
|
27509
|
+
}),
|
|
27510
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27511
|
+
label: "track mode",
|
|
27512
|
+
value: s(server.trackMode)
|
|
27513
|
+
}),
|
|
27514
|
+
server.lastDecision === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27515
|
+
label: "ladder",
|
|
27516
|
+
value: "never moved"
|
|
27517
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
27518
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27519
|
+
label: "ladder move",
|
|
27520
|
+
value: `${server.lastDecision.cause}${server.lastDecision.tier !== null ? ` → ${server.lastDecision.tier}` : ""}`,
|
|
27521
|
+
strong: true
|
|
27522
|
+
}),
|
|
27523
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27524
|
+
label: "· when",
|
|
27525
|
+
value: age(server.lastDecision.at, now)
|
|
27526
|
+
}),
|
|
27527
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27528
|
+
label: "· lossRatio",
|
|
27529
|
+
value: n(server.lastDecision.lossRatio, { digits: 4 })
|
|
27530
|
+
}),
|
|
27531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27532
|
+
label: "· clientKbps",
|
|
27533
|
+
value: n(server.lastDecision.clientKbps, { suffix: " kbps" })
|
|
27534
|
+
}),
|
|
27535
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27536
|
+
label: "· stalledFor",
|
|
27537
|
+
value: n(server.lastDecision.stalledForMs, { suffix: "ms" })
|
|
27538
|
+
}),
|
|
27539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27540
|
+
label: "· serverSending",
|
|
27541
|
+
value: b(server.lastDecision.serverSending)
|
|
27542
|
+
}),
|
|
27543
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27544
|
+
label: "· transcode-down",
|
|
27545
|
+
value: b(server.lastDecision.forcedTranscodeDown)
|
|
27546
|
+
})
|
|
27547
|
+
] }),
|
|
27548
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27549
|
+
label: "promotions",
|
|
27550
|
+
value: n(server.upgradeCount)
|
|
27551
|
+
}),
|
|
27552
|
+
server.tierFailures.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
|
|
27553
|
+
label: "tier failures",
|
|
27554
|
+
value: server.tierFailures.map((f) => `${f.tier}:${f.failures}`).join(" ")
|
|
27555
|
+
})
|
|
27556
|
+
] }),
|
|
27557
|
+
sessionId !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
27558
|
+
className: "mt-1 pt-0.5 border-t border-white/15 text-white/40 break-all leading-tight",
|
|
27559
|
+
children: sessionId
|
|
27560
|
+
})
|
|
27561
|
+
]
|
|
27562
|
+
});
|
|
27563
|
+
}
|
|
27564
|
+
//#endregion
|
|
26979
27565
|
//#region src/composites/camera-stream-player.tsx
|
|
26980
27566
|
/**
|
|
26981
27567
|
* Silence (or restore) a live WebRTC stream FOR REAL.
|
|
@@ -27074,7 +27660,7 @@ function mergePlayerHints(autoHints, override, downlinkMbps, coldStart) {
|
|
|
27074
27660
|
return coldStart ? stripDownlinkForColdStart(merged) : merged;
|
|
27075
27661
|
}
|
|
27076
27662
|
var FIRST_FRAME_TIMEOUT_MS = 8e3;
|
|
27077
|
-
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27663
|
+
function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, muted: initialMuted = true, showControls = true, showStats = false, onPlaybackStats, onClientNetworkSample, onConnectTiming, className = "", onStateChange, onError, onReconnectAttempt, overlay, createSession, sendAnswer, handleOffer, getIceServers, addIceCandidate, getIceCandidates, closeSession, getSessionState, getSessionDebug, reoffer, posterUrl, hintsOverride, onAdaptiveHints, reconnectSignal, onControlChannel, onVideoElement }) {
|
|
27078
27664
|
const videoRef = (0, react$1.useRef)(null);
|
|
27079
27665
|
const containerRef = (0, react$1.useRef)(null);
|
|
27080
27666
|
const pcRef = (0, react$1.useRef)(null);
|
|
@@ -27971,55 +28557,77 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
27971
28557
|
const webkit = video !== null && typeof video.webkitSetPresentationMode === "function";
|
|
27972
28558
|
setIsPipSupported(standard || webkit);
|
|
27973
28559
|
}, []);
|
|
27974
|
-
const [
|
|
27975
|
-
const
|
|
28560
|
+
const [debugStats, setDebugStats] = (0, react$1.useState)(null);
|
|
28561
|
+
const [serverDebug, setServerDebug] = (0, react$1.useState)(null);
|
|
28562
|
+
const [serverDebugError, setServerDebugError] = (0, react$1.useState)(null);
|
|
28563
|
+
const [debugNow, setDebugNow] = (0, react$1.useState)(() => Date.now());
|
|
28564
|
+
(0, react$1.useEffect)(() => {
|
|
28565
|
+
if (!showStats) return;
|
|
28566
|
+
const id = setInterval(() => setDebugNow(Date.now()), 500);
|
|
28567
|
+
return () => clearInterval(id);
|
|
28568
|
+
}, [showStats]);
|
|
27976
28569
|
(0, react$1.useEffect)(() => {
|
|
27977
28570
|
if (!showStats) {
|
|
27978
|
-
|
|
27979
|
-
statsPrev.current = null;
|
|
28571
|
+
setDebugStats(null);
|
|
27980
28572
|
return;
|
|
27981
28573
|
}
|
|
28574
|
+
const prev = { current: null };
|
|
28575
|
+
let cancelled = false;
|
|
27982
28576
|
const tick = async () => {
|
|
27983
28577
|
const pc = pcRef.current;
|
|
27984
28578
|
if (!pc) return;
|
|
27985
28579
|
try {
|
|
27986
28580
|
const report = await pc.getStats();
|
|
27987
|
-
|
|
27988
|
-
|
|
27989
|
-
|
|
27990
|
-
|
|
27991
|
-
|
|
27992
|
-
|
|
27993
|
-
|
|
27994
|
-
const s = raw;
|
|
27995
|
-
if (s.type === "inbound-rtp" && (s.kind ?? s.mediaType) === "video") {
|
|
27996
|
-
bytes = s.bytesReceived ?? 0;
|
|
27997
|
-
ts = s.timestamp;
|
|
27998
|
-
fps = s.framesPerSecond ?? 0;
|
|
27999
|
-
w = s.frameWidth ?? 0;
|
|
28000
|
-
h = s.frameHeight ?? 0;
|
|
28001
|
-
} else if (s.type === "candidate-pair" && s.nominated && typeof s.currentRoundTripTime === "number") rtt = s.currentRoundTripTime;
|
|
28581
|
+
if (cancelled) return;
|
|
28582
|
+
const next = deriveWebrtcDebugStats({
|
|
28583
|
+
report,
|
|
28584
|
+
prev: prev.current,
|
|
28585
|
+
now: Date.now(),
|
|
28586
|
+
iceConnectionState: pc.iceConnectionState,
|
|
28587
|
+
dtlsState: pc.connectionState
|
|
28002
28588
|
});
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
statsPrev.current = {
|
|
28006
|
-
bytes,
|
|
28007
|
-
ts
|
|
28008
|
-
};
|
|
28009
|
-
const parts = [];
|
|
28010
|
-
if (w && h) parts.push(`${w}×${h}`);
|
|
28011
|
-
if (fps) parts.push(`${Math.round(fps)}fps`);
|
|
28012
|
-
if (kbps > 0) parts.push(`${kbps} kbps`);
|
|
28013
|
-
if (rtt) parts.push(`${Math.round(rtt * 1e3)}ms`);
|
|
28014
|
-
setStatsText(parts.join(" · "));
|
|
28589
|
+
prev.current = next;
|
|
28590
|
+
setDebugStats(next);
|
|
28015
28591
|
} catch {}
|
|
28016
28592
|
};
|
|
28017
28593
|
tick();
|
|
28018
28594
|
const id = setInterval(() => {
|
|
28019
28595
|
tick();
|
|
28020
28596
|
}, 1e3);
|
|
28021
|
-
return () =>
|
|
28597
|
+
return () => {
|
|
28598
|
+
cancelled = true;
|
|
28599
|
+
clearInterval(id);
|
|
28600
|
+
};
|
|
28022
28601
|
}, [showStats]);
|
|
28602
|
+
(0, react$1.useEffect)(() => {
|
|
28603
|
+
if (!showStats || !getSessionDebug) {
|
|
28604
|
+
setServerDebug(null);
|
|
28605
|
+
setServerDebugError(null);
|
|
28606
|
+
return;
|
|
28607
|
+
}
|
|
28608
|
+
let cancelled = false;
|
|
28609
|
+
const tick = async () => {
|
|
28610
|
+
const sid = activeSessionIdRef.current;
|
|
28611
|
+
if (sid === null) return;
|
|
28612
|
+
try {
|
|
28613
|
+
const answer = await getSessionDebug(sid);
|
|
28614
|
+
if (cancelled) return;
|
|
28615
|
+
setServerDebug(answer);
|
|
28616
|
+
setServerDebugError(null);
|
|
28617
|
+
} catch (err) {
|
|
28618
|
+
if (cancelled) return;
|
|
28619
|
+
setServerDebugError(err instanceof Error ? err.message : String(err));
|
|
28620
|
+
}
|
|
28621
|
+
};
|
|
28622
|
+
tick();
|
|
28623
|
+
const id = setInterval(() => {
|
|
28624
|
+
tick();
|
|
28625
|
+
}, 1e3);
|
|
28626
|
+
return () => {
|
|
28627
|
+
cancelled = true;
|
|
28628
|
+
clearInterval(id);
|
|
28629
|
+
};
|
|
28630
|
+
}, [showStats, getSessionDebug]);
|
|
28023
28631
|
const netPrev = (0, react$1.useRef)(null);
|
|
28024
28632
|
(0, react$1.useEffect)(() => {
|
|
28025
28633
|
if (!onClientNetworkSample) {
|
|
@@ -28206,9 +28814,13 @@ function CameraStreamPlayer({ serverUrl, streamKey, label, autoPlay = true, mute
|
|
|
28206
28814
|
className: "absolute top-2 left-2 z-10 transform-gpu px-2 py-0.5 rounded bg-black/60 text-[10px] text-white font-medium",
|
|
28207
28815
|
children: label
|
|
28208
28816
|
}),
|
|
28209
|
-
showStats &&
|
|
28210
|
-
|
|
28211
|
-
|
|
28817
|
+
showStats && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StreamDebugOverlay, {
|
|
28818
|
+
stats: debugStats,
|
|
28819
|
+
server: serverDebug,
|
|
28820
|
+
serverError: serverDebugError,
|
|
28821
|
+
serverWired: getSessionDebug !== void 0,
|
|
28822
|
+
now: debugNow,
|
|
28823
|
+
sessionId: activeSessionIdRef.current
|
|
28212
28824
|
}),
|
|
28213
28825
|
overlay,
|
|
28214
28826
|
showControls && !stillImg && state === "connecting" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -51561,6 +52173,13 @@ function useDeviceWebrtc(trpc, deviceId, pollIntervalMs = 5e3) {
|
|
|
51561
52173
|
} catch {
|
|
51562
52174
|
return { pendingRenegotiation: null };
|
|
51563
52175
|
}
|
|
52176
|
+
}, [trpc, deviceId]),
|
|
52177
|
+
getSessionDebug: (0, react$1.useCallback)(async (sessionId) => {
|
|
52178
|
+
if (deviceId === null) throw new Error("getSessionDebug: no device bound to this player");
|
|
52179
|
+
return trpc.webrtcSession.getSessionDebug.query({
|
|
52180
|
+
deviceId,
|
|
52181
|
+
sessionId
|
|
52182
|
+
});
|
|
51564
52183
|
}, [trpc, deviceId])
|
|
51565
52184
|
};
|
|
51566
52185
|
}
|
|
@@ -52077,6 +52696,7 @@ exports.StepTimings = StepTimings;
|
|
|
52077
52696
|
exports.StepTreeMaster = StepTreeMaster;
|
|
52078
52697
|
exports.Stepper = Stepper;
|
|
52079
52698
|
exports.StreamBrokerSelector = StreamBrokerSelector;
|
|
52699
|
+
exports.StreamDebugOverlay = StreamDebugOverlay;
|
|
52080
52700
|
exports.StreamPanel = StreamPanel;
|
|
52081
52701
|
exports.Switch = Switch;
|
|
52082
52702
|
exports.SwitchHeroCard = SwitchHeroCard;
|
|
@@ -52142,6 +52762,7 @@ exports.cursorFractionFor = cursorFractionFor;
|
|
|
52142
52762
|
exports.darkColors = require_theme_index.darkColors$1;
|
|
52143
52763
|
exports.defaultTheme = require_theme_index.defaultTheme$1;
|
|
52144
52764
|
exports.deriveDeviceKind = deriveDeviceKind;
|
|
52765
|
+
exports.deriveWebrtcDebugStats = deriveWebrtcDebugStats;
|
|
52145
52766
|
exports.describeDeviceSelector = describeDeviceSelector;
|
|
52146
52767
|
exports.deviceMatchesFilter = deviceMatchesFilter;
|
|
52147
52768
|
exports.deviceOptionLabel = deviceOptionLabel;
|
|
@@ -52158,6 +52779,7 @@ exports.findTimezone = _camstack_types.findTimezone;
|
|
|
52158
52779
|
exports.formatControlDateTime = formatControlDateTime;
|
|
52159
52780
|
exports.formatLastSeen = formatLastSeen;
|
|
52160
52781
|
exports.formatNumeric = formatNumeric;
|
|
52782
|
+
exports.freezeVerdict = freezeVerdict;
|
|
52161
52783
|
exports.fuzzyMatch = fuzzyMatch;
|
|
52162
52784
|
exports.getClassColor = getClassColor;
|
|
52163
52785
|
exports.getPhaseVisual = getPhaseVisual;
|
|
@@ -53327,6 +53949,7 @@ exports.useWebrtcSessionAddIceCandidate = useWebrtcSessionAddIceCandidate;
|
|
|
53327
53949
|
exports.useWebrtcSessionCloseSession = useWebrtcSessionCloseSession;
|
|
53328
53950
|
exports.useWebrtcSessionCreateSession = useWebrtcSessionCreateSession;
|
|
53329
53951
|
exports.useWebrtcSessionGetIceCandidates = useWebrtcSessionGetIceCandidates;
|
|
53952
|
+
exports.useWebrtcSessionGetSessionDebug = useWebrtcSessionGetSessionDebug;
|
|
53330
53953
|
exports.useWebrtcSessionGetSessionState = useWebrtcSessionGetSessionState;
|
|
53331
53954
|
exports.useWebrtcSessionHandleAnswer = useWebrtcSessionHandleAnswer;
|
|
53332
53955
|
exports.useWebrtcSessionHandleOffer = useWebrtcSessionHandleOffer;
|