From fd3faa934999ee3953d66d9db84f67b699e81548 Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Mon, 15 Jun 2026 08:49:34 -0500 Subject: scrypted: rotate-before-scale so portrait JPEGs report 800x480 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The portrait filter chain was scale=480:800,transpose=1. Output frame dimensions came out 800x480 (correct) but the mjpeg encoder was occasionally writing the pre-transpose 480x800 into the JPEG SOF marker, so the firmware's dimension check rejected the frame: expected 800x480, got 480x800 Reordering to transpose=1,scale=800:480,setsar=1 makes the post-filter dims explicit and unambiguous — transpose first changes the frame to 800x480, scale re-asserts it, setsar clears any leftover non-1:1 aspect ratio metadata. The encoder now writes 800x480 into SOF every frame. --- scrypted/scrypted-viewport.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scrypted/scrypted-viewport.ts b/scrypted/scrypted-viewport.ts index 7b69a75..22f233a 100644 --- a/scrypted/scrypted-viewport.ts +++ b/scrypted/scrypted-viewport.ts @@ -614,9 +614,17 @@ class ScryptedViewportProvider extends ScryptedDeviceBase // panel is already in the right rotation. The firmware never // touches pixels — the hardware JPEG decoder writes BGR888 // straight into a DMA buffer that gets handed to the DSI engine. + // Filter order matters here. Earlier we did + // scale=480:800,transpose=1 + // which intermittently produced a JPEG with a SOF marker + // reporting 480x800 — the firmware then rejected it with + // "expected 800x480, got 480x800". Rotating *first* and then + // scaling to an EXPLICIT panelWxpanelH (with setsar to clear + // any leftover aspect-ratio metadata) makes the final encoded + // dimensions deterministic regardless of source resolution. const vf = v.orientation === "portrait" - ? `scale=${panelH}:${panelW}:flags=lanczos,transpose=1` // 90° CW → panelW x panelH - : `scale=${panelW}:${panelH}:flags=lanczos`; + ? `transpose=1,scale=${panelW}:${panelH}:flags=lanczos,setsar=1` + : `scale=${panelW}:${panelH}:flags=lanczos,setsar=1`; const fps = Math.max(1, Math.round(1000 / v.frameIntervalMs)); // Pull the camera's video stream, convert to ffmpeg input args, and -- cgit v1.2.3