|
Browse for _scrypted-viewport._tcp with a plain dgram socket on an
EPHEMERAL port: per RFC 6762 §6.7 a query from a non-5353 source port is
a legacy unicast query and responders reply unicast to that port —
verified in the ESP-IDF responder (mdns_send.c answers to the querier's
addr/port whenever src_port != 5353). No :5353 bind means no conflict
with Scrypted's own HomeKit mDNS or a host avahi-daemon, and it works
under Docker host-networking or native alike. One response packet
carries PTR+SRV+TXT+A (RFC 6763 §12.1), so parsing is per-packet with
no follow-up queries; the wire codec (name compression included) is
~150 lines, no dependencies.
Surfaced three ways:
- add-device form: host field is a combobox listing discovered
viewports as "ip — name (vX, WxH)"; manual entry still works and
only the address token is stored.
- child settings page: same choices on the existing host field.
- auto-heal: when a /config register fails, re-browse and match by MAC
(seeded from /state and discovery; survives renames) then by name;
on a hit at a new address, rewrite host and retry once. Runs only on
register failure, so it's rate-limited to the 5-min cycle and removes
the need for a DHCP reservation.
Browses are best-effort ([] on any failure) and cached 30s so settings
re-renders don't spam the LAN. diagnostic.ts gains the same browse as a
paste-and-run probe that validates dgram + multicast reachability from
inside the real Scrypted sandbox before trusting the feature.
|
|
- bootstrap: drain the shutdown-cleaner array FIRST, not after the child
re-discovery loop. The old order tore down the listeners the loop had
just attached, and since the instance maps still referenced them the
attachListener fast-path blocked the 5-min register-cycle self-heal
from ever re-attaching after a warm re-paste. The attach cleaner now
also invalidates listeners/attachedCameraId (one cleaner per attach),
so a drain by another instance can't strand dead registrations either.
- stream socket: single-flight reconnect scheduled from 'close' only.
A failed connect emits 'error' then 'close'; scheduling from both
doubled outstanding attempts every 500ms against a rebooting device.
openStreamSocket also destroys the previous socket first.
- onRequest wake: same streams/streamStarting guard as every other start
path (a concurrent second startStream overwrote the streams entry and
orphaned the first ffmpeg), respond 204 immediately (the device's POST
times out at 1s — awaiting startStream turned every tap-wake into a
firmware state_post_failure), and skip the redundant wake POST back.
- startStream: bail-out paths after the wake POST (camera missing, no
usable stream, ffmpeg-input conversion failure) now send a
compensating sleep instead of stranding the panel on Loading; the
last-ditch getVideoStream fallback no longer rejects out of the call.
- key streams/streamStarting/stopStream by nativeId — v.name drifts to
the nativeId on reload, stranding or duplicating streams keyed under
the drifted value; findByName matches display_name first for the same
reason.
- idle_timeout_ms=0: the Scrypted-side safety timeout still reclaims
ffmpeg/socket but no longer POSTs sleep over the always-on setting.
- pending bindingDebounce timers cleared across re-paste (they fired
against the dead instance and attached duplicate listeners);
releaseDevice drops lastRegisterSig/streamStarting/debounce entries.
- start() single-flights bootstrap; createDevice drops its redundant
second registerViewport; streamLogger rolls its window on quiet ticks
(post-lull rates were diluted) and lastLogUs -> lastLogMs; hoist the
JPEG EOI needle + resume-scan offset in the demux loop; postJSON drops
the dead 204 check; remove unused sandbox declares and the legacy
streams.interval field.
- tsconfig: moduleResolution Node -> Bundler (removed in TS 6).
|