<feed xmlns='http://www.w3.org/2005/Atom'>
<title>luke/esp32-poe-scrypted-viewport/scrypted/scrypted-viewport.ts, branch v1.3.0</title>
<subtitle>ESP32-POE Scrypted viewport (private)
</subtitle>
<id>https://src.nth.io/luke/esp32-poe-scrypted-viewport/atom?h=v1.3.0</id>
<link rel='self' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/atom?h=v1.3.0'/>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/'/>
<updated>2026-07-01T19:19:01+00:00</updated>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = 0be8f18</title>
<updated>2026-07-01T19:19:01+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T19:19:01+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=ad29fa22e8fc9c15c2cda5232a2b269594475d16'/>
<id>urn:sha1:ad29fa22e8fc9c15c2cda5232a2b269594475d16</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: trim stream logging volume</title>
<updated>2026-07-01T18:22:05+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T18:22:05+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=0be8f188b46bd073e4c6836dedf79a5282250828'/>
<id>urn:sha1:0be8f188b46bd073e4c6836dedf79a5282250828</id>
<content type='text'>
A chatty camera keeps a viewport streaming for long stretches, and the old
logging emitted ~5 cold-start stamps + a 10s window line per ~10s per stream.
Cut it down:
- keep a single cold-start stamp (first ffmpeg frame); drop spawned / first
  stdout byte / first socket.write.
- gate the 10s window line on noteworthy windows only (socket-not-ready drops,
  a buffer-cap flush this window, firmware shedding &gt;=2fps, or painted &lt;20fps);
  healthy 24fps windows print nothing — the per-wake lines already show liveness.
- log "registered" only when the pushed config actually changes, not on every
  5-minute reregister cycle. lastRegisterSig cleared on stop() for fresh-load
  parity.
No behavior change beyond logging.
</content>
</entry>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = c6610c1</title>
<updated>2026-07-01T01:44:00+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:44:00+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=16dd6ca15b037d20fba8db873da064f58e0d0574'/>
<id>urn:sha1:16dd6ca15b037d20fba8db873da064f58e0d0574</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: remove the pre-stream snapshot entirely</title>
<updated>2026-07-01T01:43:52+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:43:52+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=c6610c1047514292b8deee08cbd27d92d9fe6950'/>
<id>urn:sha1:c6610c1047514292b8deee08cbd27d92d9fe6950</id>
<content type='text'>
The prebuffered stream now paints in ~0.7s, so the takePicture→transform→POST
first-paint bridge was consistently slower than the video it was meant to
cover, while adding camera load and a stale-overpaint risk. Drop it: remove
pushSnapshot and its call, the shouldPaint gate, and the firstStreamFrameSeen
plumbing. On the slow paths (no/cold prebuffer) the panel holds its prior frame
until the stream's first frame lands. buildVf stays (used by the live path).
</content>
</entry>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = 39aeeff</title>
<updated>2026-07-01T01:35:54+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:35:54+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=d149bb9fc76dac0b1eb01d35bc932aec00f7f7f3'/>
<id>urn:sha1:d149bb9fc76dac0b1eb01d35bc932aec00f7f7f3</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: gate snapshot POST on the live stream not yet painting</title>
<updated>2026-07-01T01:35:45+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:35:45+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=39aeeff0bcdfaa5ac4520e1fc7b90266c761c3f5'/>
<id>urn:sha1:39aeeff0bcdfaa5ac4520e1fc7b90266c761c3f5</id>
<content type='text'>
Now that the prebuffered stream paints in ~0.7s, the parallel snapshot usually
loses the race and, if slow (a Unifi takePicture cache-miss can be several
seconds), would land after live video and overpaint it with a staler still.
pushSnapshot takes a shouldPaint predicate; startStream passes
() =&gt; !firstStreamFrameSeen (flipped by the ffmpeg first-frame handler), so a
snapshot that finishes after the stream is already painting is dropped before
the POST. Preserves the snapshot as a gap-filler for the slow paths (live-edge
fallback / cold prebuffer) where the stream hasn't painted yet.
</content>
</entry>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = 860f539</title>
<updated>2026-07-01T01:30:34+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:30:34+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=d71bdc9167fda3d5401ec3212a9d2269da7002fd'/>
<id>urn:sha1:d71bdc9167fda3d5401ec3212a9d2269da7002fd</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: strip cold-start/event diagnostics</title>
<updated>2026-07-01T01:30:25+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:30:25+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=860f539e2d8cdc0fc2746adadb864a1fe293a4b5'/>
<id>urn:sha1:860f539e2d8cdc0fc2746adadb864a1fe293a4b5</id>
<content type='text'>
Remove the temporary instrumentation used to diagnose the event-wake and
cold-start work: the evtscan system-wide listener, the attach/trace/streamopt/
src/inputArgs logs, the StartStop entry logs, and ffmpeg -loglevel info/-nostats
(back to error). Keep the lightweight one-shot cold-start stamps (spawned →
first byte → first frame → first socket.write) and usingPrebuffer in the config
line — cheap regression signals — plus all the actual fixes and the prebuffered-
stream selection logic.
</content>
</entry>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = 24d6e90</title>
<updated>2026-07-01T01:26:22+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:26:22+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=e8c5496f668fcfbc597f8f73d7ecff7994582ce1'/>
<id>urn:sha1:e8c5496f668fcfbc597f8f73d7ecff7994582ce1</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: fix event wake, teardown, triggers; cut stream cold-start ~6s→~0.7s</title>
<updated>2026-07-01T01:26:14+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-01T01:26:14+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=24d6e90c742d1a8ced30b713f41637c630c270b1'/>
<id>urn:sha1:24d6e90c742d1a8ced30b713f41637c630c270b1</id>
<content type='text'>
Event/lifecycle fixes:
- attachListener now self-heals: it's idempotent (tracks the bound camera
  per viewport) and re-runs on registerViewport success, so the camera
  subscription survives the reload/add storage-attach race that previously
  left a bound viewport with no listener until a manual re-save.
- stop() always drains the global cleaner array (never gates on this.running)
  and also cancels bindingDebounce timers + clears the stream idle timeout on
  abort, so StartStop.stop()+start() == a fresh load with no orphaned ffmpeg,
  sockets, listeners, or intervals.
- ignore all camera events while a stream is live or starting (guard moved to
  the top of handleCameraEvent) — the wake window is anchored to the first
  event; later events don't queue, relaunch, or extend it.
- prune stale childIds whose storage container is gone (deleted device).

Triggers:
- default to person + doorbell (motion opt-in; doorbell cameras are chatty);
  doorbell only offered for doorbell-capable cameras. Reconcile stored
  triggers + re-render settings live when the camera binding changes.

Cold-start (wake -&gt; first live frame): ~6s -&gt; ~0.7s.
- Root cause: Unifi GOP is ~5s and the rebroadcast RTSP serves live-edge, so
  ffmpeg waited for the next keyframe.
- Fix: select the prebuffered substream by id (smallest that covers the panel,
  not the 5MP High), request prebuffer &gt;= GOP, and drop -avioflags direct /
  -fflags nobuffer on the prebuffer path so ffmpeg gulps the buffered-keyframe
  burst instead of trickling it. Live-edge fallback keeps the low-latency flags.
  Steady-state g2g unchanged (~50-100ms).

Includes temporary diagnostics (evtscan, attach/trace/streamopt/src logs,
ffmpeg -loglevel info) to be stripped in the follow-up commit.
</content>
</entry>
</feed>
