From 439dadb24c4e07eb11f1aa9804cf7a973518e539 Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Sat, 20 Jun 2026 12:11:54 -0500 Subject: scrypted: log node_buf in stream window — confirms script-side buffering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two values to the unified stream log line: node_buf=NKB≈Mms node_buf bytes is sock.writableLength — bytes that socket.write() accepted from us but the kernel send buffer hasn't pulled yet, so they sit in Node's internal queue. These have NOT been sent over the wire; the firmware can't have them. Dividing by the current send rate (bytes/sec computed from the same window's bytesSent) gives the buffer depth in milliseconds: how many seconds of already-extracted-from-ffmpeg frames are stalled at the script. When g2g shows 7s steady state and node_buf shows e.g. 5000-6000KB ≈ 6500ms, the conclusion is obvious — the buffering isn't in the firmware or on the wire, it's in Scrypted's Node process. No behavior change; pure diagnostic. --- scrypted/scrypted-viewport.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scrypted/scrypted-viewport.ts b/scrypted/scrypted-viewport.ts index 747d3c0..7cf5faa 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 = "28f39bf"; +const SCRIPT_VERSION = "pending"; // // Architecture // ------------ @@ -1022,11 +1022,24 @@ class ScryptedViewportProvider extends ScryptedDeviceBase } catch { /* keep the local stats; firmware-side just shows ? */ } const skipped = paintedNum >= 0 ? Math.max(0, sentRate - paintedNum).toFixed(1) : "?"; + // node_buf = bytes Scrypted has handed to socket.write but + // the kernel send buffer hasn't accepted yet — they sit in + // Node's internal queue, NOT on the wire. Divide by the + // current send rate to estimate how many seconds of + // already-emitted frames are waiting at the source. This + // is the load-bearing piece of the g2g "buffer depth" + // story: when this is large, the firmware can't possibly + // be showing the freshest bytes because we haven't even + // sent them yet. + const nodeBufBytes = sock?.writableLength ?? 0; + const sentBps = mbPerSec * 1024 * 1024; + const nodeBufMs = sentBps > 0 ? (nodeBufBytes / sentBps * 1000).toFixed(0) : "?"; this.console.log( `stream "${v.name}": sent=${sentRate.toFixed(1)}fps painted=${painted}fps ` + `(fw-skipped=${skipped}fps, drops=${droppedFrames}) ` + `${mbPerSec.toFixed(2)}MB/s sent / ${paintedMb}MB/s painted | ` + - `socket.write p50=${p50}ms p95=${p95}ms max=${max}ms backpressured=${socketBackpressured} | ` + + `socket.write p50=${p50}ms p95=${p95}ms max=${max}ms backpressured=${socketBackpressured} ` + + `node_buf=${(nodeBufBytes / 1024).toFixed(0)}KB≈${nodeBufMs}ms | ` + `recv=${recvStr}us dec=${decStr}us paint=${paintStr}us idle=${idleStr}us | g2g=${g2g}`); droppedFrames = 0; -- cgit v1.2.3