Files
Piik/src/client/components/StatsGrid.tsx
T

98 lines
3.0 KiB
TypeScript

import type { ConnectionMetrics } from "../types";
function readableNumber(value: number | null, digits = 0): string {
return value === null || !Number.isFinite(value) ? "未知" : value.toFixed(digits);
}
function qualityReason(value: string | null): string {
const labels: Record<string, string> = {
none: "正常",
bandwidth: "带宽受限",
cpu: "编码受限",
other: "其他限制",
};
return value ? (labels[value] ?? value) : "未知";
}
function Metric({ label, value, title }: { label: string; value: string; title?: string }) {
return (
<div className="metric" title={title}>
<dt>{label}</dt>
<dd>{value}</dd>
</div>
);
}
export function StatsGrid({
metrics,
direction,
}: {
metrics: ConnectionMetrics;
direction: "send" | "receive";
}) {
const encoder = metrics.encoderImplementation
? `${metrics.encoderImplementation}${
metrics.powerEfficientEncoder === true
? " · 节能"
: metrics.powerEfficientEncoder === false
? " · 非节能"
: ""
}`
: "未知";
return (
<dl className="stats-grid">
<Metric
label={direction === "send" ? "发送码率" : "接收码率"}
value={`${readableNumber(metrics.bitrateKbps)} kbps`}
/>
<Metric label="帧率" value={`${readableNumber(metrics.framesPerSecond, 1)} fps`} />
<Metric label="分辨率" value={metrics.resolution ?? "未知"} />
<Metric label="RTT" value={`${readableNumber(metrics.rttMs)} ms`} title="网络往返时间" />
<Metric label="丢包" value={readableNumber(metrics.packetsLost)} />
<Metric
label={direction === "send" ? "可用上行" : "抖动"}
value={
direction === "send"
? `${readableNumber(metrics.availableOutgoingKbps)} kbps`
: `${readableNumber(metrics.jitterMs, 1)} ms`
}
/>
<Metric
label="ICE 传输"
value={metrics.iceProtocol?.toUpperCase() ?? "未知"}
/>
{metrics.localCandidateType === "relay" && (
<Metric
label="本地 TURN"
value={metrics.localRelayProtocol?.toUpperCase() ?? "未知"}
/>
)}
<Metric
label="候选"
value={`${metrics.localCandidateType ?? "?"} / ${metrics.remoteCandidateType ?? "?"}`}
title="本地 / 远端候选类型"
/>
<Metric label="Codec" value={metrics.codec ?? "未知"} />
{direction === "send" ? (
<>
<Metric label="编码器" value={encoder} />
<Metric
label="最近区间编码/帧"
value={`${readableNumber(metrics.intervalEncodeMs, 1)} ms`}
/>
<Metric label="质量状态" value={qualityReason(metrics.qualityLimitationReason)} />
</>
) : (
<>
<Metric label="丢帧" value={readableNumber(metrics.framesDropped)} />
<Metric
label="最近区间解码/帧"
value={`${readableNumber(metrics.intervalDecodeMs, 1)} ms`}
/>
</>
)}
</dl>
);
}