Skip to content

feat(camera): local IP camera bridge with seekable instant replay - #19

Merged
santidev21 merged 3 commits into
mainfrom
feat/ip-camera-bridge
Sep 30, 2026
Merged

santidev21 merged 3 commits into
mainfrom
feat/ip-camera-bridge

Conversation

@santidev21

Copy link
Copy Markdown
Owner

What

Adds support for IP cameras on the local network (Imou/Dahua, etc.) without the vendor app, plus a seekable instant replay.

How

  • camera-bridge/ (Node, no deps) + go2rtc (Docker, docker-compose.camera.yml):
    • discovers cameras by scanning TCP/554 and probing the Dahua magicBox CGI;
    • forces H.264 on Dahua/Imou main streams (browsers can't decode HEVC over WebRTC);
    • registers streams in go2rtc and proxies WebRTC signaling with CORS/PNA headers.
  • Security: bridge binds 127.0.0.1, CORS allow-list, proxy limited to /api/webrtc (+ frame.jpeg), go2rtc API/RTSP on loopback. The camera password never reaches the browser.
  • Frontend: source picker (device + IP), WebRTC live view; replay keeps working via MediaRecorder and is assembled with MediaSource (sequence mode) so the clip is continuous and seekable.
  • Replay buffer: 1s chunks, 3-minute default window (localStorage.replayBufferSeconds).
  • CSP: allow http://localhost:* / http://127.0.0.1:* (connect-src) and blob: (media-src) in both index.html meta and the nginx site config. The gateway repo must be deployed too (browser enforces both CSPs).

Notes

  • Only works when the bridge runs on the same machine as the browser (loopback requirement).
  • Camera credentials come from CAMERA_PASSWORD (.env), never committed.

See docs/specs/ip-cameras.md.

- camera-bridge (Node) + go2rtc: discovers Imou/Dahua/RTSP cameras on the
  LAN, forces H.264, registers streams, and proxies WebRTC signaling with
  CORS/PNA. Binds to loopback and only exposes /api/webrtc (+ frame).
- frontend: camera source picker (device + IP), WebRTC live view, and a
  replay clip assembled with MediaSource (sequence mode) so it is continuous
  and seekable; MediaRecorder output is duration-patched as a fallback.
- replay buffer: 1s chunks, 3-minute default window (replayBufferSeconds).
- CSP: allow http://localhost:* / http://127.0.0.1:* (connect-src) and
  blob: (media-src) in both index.html meta and the nginx site config.
- docs: docs/specs/ip-cameras.md, AGENTS/README/roadmap/known-issues.

Refs: docs/specs/ip-cameras.md
Comment thread camera-bridge/src/dahua.js Fixed
Comment thread camera-bridge/src/index.js Fixed
- Strip control characters from logged request values (CodeQL log injection).
- Suppress js/insufficient-password-hash: MD5 is required by HTTP Digest
  (RFC 2617) and is not used to store passwords.
Comment thread camera-bridge/src/index.js Fixed
- Log only allow-listed constants (no raw request values) to satisfy
  js/log-injection in the camera-bridge access log.
- Add .github/codeql/codeql-config.yml excluding js/insufficient-password-hash:
  MD5 is required by HTTP Digest (RFC 2617), not a password store.
@santidev21
santidev21 merged commit d7c7aa8 into main Sep 30, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants