const connection = document.querySelector("#connection"); const detectionsEl = document.querySelector("#detections"); const frameIdEl = document.querySelector("#frame-id"); const fpsEl = document.querySelector("#fps"); const errorEl = document.querySelector("#error"); const sourceEl = document.querySelector("#source"); const deviceSelect = document.querySelector("#device-select"); const timingTokenEl = document.querySelector("#timing-token"); const timingSignEl = document.querySelector("#timing-sign"); const timingUrlEl = document.querySelector("#timing-url"); const timingOpenEl = document.querySelector("#timing-open"); const timingFrameEl = document.querySelector("#timing-frame"); let selectedDevice = ""; let pendingDevice = ""; let devicesSignature = ""; function setConnection(online, text) { connection.textContent = text; connection.classList.toggle("online", online); connection.classList.toggle("offline", !online); } function formatMs(value) { if (value === undefined || value === null || value === 0) { return "-"; } return `${Number(value).toFixed(2)} ms`; } function renderDevices(devices, currentDeviceNum) { if (!devices.length) { deviceSelect.innerHTML = ''; deviceSelect.disabled = true; return; } const displayDevice = pendingDevice || currentDeviceNum; const nextSignature = `${displayDevice}|${devices.map((device) => device.device_num).join(",")}`; if (nextSignature === devicesSignature) { return; } selectedDevice = displayDevice; devicesSignature = nextSignature; deviceSelect.innerHTML = devices .map((device) => { const selected = device.device_num === displayDevice ? "selected" : ""; return ``; }) .join(""); } function renderTimings(timings) { timingTokenEl.textContent = timings?.token_cache ? "缓存" : formatMs(timings?.token_ms); timingSignEl.textContent = formatMs(timings?.sign_ms); timingUrlEl.textContent = formatMs(timings?.stream_url_ms); timingOpenEl.textContent = formatMs(timings?.open_ms); timingFrameEl.textContent = formatMs(timings?.first_frame_ms); } function renderDetections(detections) { if (!detections.length) { detectionsEl.className = "detections empty"; detectionsEl.textContent = "暂无目标"; return; } detectionsEl.className = "detections"; detectionsEl.innerHTML = detections .map((det) => { const score = `${(det.score * 100).toFixed(1)}%`; const box = det.box.join(", "); return `