src.nth.io/

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Hoersten <[email protected]>2026-06-20 10:06:58 -0500
committerLuke Hoersten <[email protected]>2026-06-20 10:06:58 -0500
commitf982b88d447f265f9847a48946a9903d1faebf49 (patch)
tree10179e542eadd78959b0c7bf7551eabb00ca2aa4
parentaa1ce70d546355997c58488fc409767d1dc8088f (diff)
scrypted: drop substream picker, hardcode best-resolution + skip low-res
Reverts the per-viewport "Camera substream" UI added in 6d74d02 — having a knob the user has to find and tune is the wrong shape. The goal is "best quality, highest fps that's actually achievable" and that's a deterministic walk, not a user choice. New walk: medium-resolution → local → remote → camera-default. Explicitly NOT in the list: - low-resolution: the camera's preview substream, capped at the ~5-8 fps that's been bottlenecking us. - remote-recorder: has the camera's ~10s prebuffer baked in (we'd display the past rather than the present). Wire cost is unchanged: every input resolution is re-encoded to panel-native 800x480 mjpeg q:v 1 before going to the device. The only tradeoff for picking the main stream is Scrypted-side ffmpeg CPU, which is plentiful. Throughput to the firmware is the same; upstream camera fps is what changes (5fps → 15-30fps typical).
-rw-r--r--scrypted/scrypted-viewport.ts43
1 files changed, 19 insertions, 24 deletions
diff --git a/scrypted/scrypted-viewport.ts b/scrypted/scrypted-viewport.ts
index bf67a6d..63cab8d 100644
--- a/scrypted/scrypted-viewport.ts
+++ b/scrypted/scrypted-viewport.ts
@@ -6,7 +6,7 @@
// short git hash of the commit that added this constant — if the
// hash in the log doesn't match the HEAD this file came from, the
// Scrypted Script editor is still on stale code.
-const SCRIPT_VERSION = "6d74d02";
+const SCRIPT_VERSION = "pending";
//
// Architecture
// ------------
@@ -123,16 +123,6 @@ class Viewport extends ScryptedDeviceBase implements Settings {
const v = this.storage.getItem("brightness");
return v ? Math.max(0, Math.min(100, parseInt(v, 10) || 0)) : DEFAULT_BRIGHTNESS;
}
- // Preferred camera substream. "auto" walks a low-latency-first
- // priority list and takes the first that resolves; the other
- // options pin a specific substream so the user can pick a higher-
- // fps source when "auto" lands on a slow 5-fps preview stream.
- get streamDestination(): string {
- const v = this.storage.getItem("stream_destination");
- const allowed = new Set(["auto", "low-resolution", "medium-resolution",
- "local", "remote", "remote-recorder"]);
- return allowed.has(v as any) ? (v as string) : "auto";
- }
// ffmpeg mjpeg encoder -q:v. Valid range 1..31, lower = higher
// quality + bigger JPEG (1 ≈ visually lossless, 31 ≈ very lossy).
// Default 1 — with HTTP keep-alive + NODELAY we have plenty of
@@ -207,14 +197,6 @@ class Viewport extends ScryptedDeviceBase implements Settings {
} as any,
{
group: "Display",
- key: "stream_destination",
- title: "Camera substream",
- description: "Which camera-side stream to pull. 'auto' walks low-latency-first and picks the first that resolves (typically a low-fps preview substream — ~5-8 fps). Pin to medium-resolution or remote-recorder to force a higher-fps stream at the cost of larger frames + latency.",
- choices: ["auto", "low-resolution", "medium-resolution", "local", "remote", "remote-recorder"],
- value: this.streamDestination,
- } as any,
- {
- group: "Display",
key: "jpeg_quality",
title: "JPEG quality (1–31, lower = better)",
description: "ffmpeg mjpeg encoder -q:v. 1 ≈ visually lossless (~140KB at panel-native), 5 ≈ good (~70KB), 10+ noticeably lossy. Default 1.",
@@ -794,13 +776,26 @@ class ScryptedViewportProvider extends ScryptedDeviceBase
// prebuffer baked in — we'd watch the past, not the present.
// Walk substreams from lowest-latency → highest-latency and
// take the first one that resolves.
+ // Source substream selection. We always want the highest-fps
+ // highest-quality option that still has acceptable latency.
+ // The wire cost is unaffected — we re-encode to panel-native
+ // 800x480 mjpeg q:v 1 regardless of input resolution — so the
+ // only tradeoff is Scrypted-side ffmpeg CPU (irrelevant here).
+ //
+ // Order of preference:
+ // medium-resolution — usually the camera's main 1080p
+ // stream at 15-30 fps, low latency
+ // local — main stream for local clients
+ // remote — main stream for remote clients
+ // (camera default) — last-ditch fallback
+ //
+ // Explicitly NOT trying:
+ // low-resolution — preview substream, capped 5-8 fps
+ // remote-recorder — has ~10s prebuffer baked in (we'd watch
+ // the past, not the present)
let stream: any;
let pickedDest = "(default)";
- const userPref = v.streamDestination;
- const destOrder = userPref === "auto"
- ? ["low-resolution", "medium-resolution", "local", "remote", "remote-recorder"]
- : [userPref];
- for (const destination of destOrder) {
+ for (const destination of ["medium-resolution", "local", "remote"]) {
try { stream = await cam.getVideoStream({ destination }); pickedDest = destination; break; }
catch { /* try next */ }
}