feat: code-review-tea — Gitea/tea fork of Anthropic's code-review plugin #10
@@ -0,0 +1,164 @@
|
||||
# Camera Stream Simulator — Design
|
||||
|
||||
**Status:** Approved
|
||||
**Date:** 2026-04-05
|
||||
**Scope:** Development tooling only. Not shipped to operators.
|
||||
|
||||
## Purpose
|
||||
|
||||
Loop phone-recorded video files as local RTSP streams so Vigilar's real camera
|
||||
pipeline (OpenCV capture, motion detection, HLS, recording, reconnect logic)
|
||||
can be exercised end-to-end without physical cameras. The operator records
|
||||
3–5 minutes of footage from each angle on a phone, drops the files into a
|
||||
`videos/` directory, and runs a single script that serves them as
|
||||
`rtsp://127.0.0.1:8554/<camera_id>`.
|
||||
|
||||
Primary use case: manual dev testing with 4–5 real clips. Secondary use case:
|
||||
stress testing with up to 32 distinct clips.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Not a production feature. Never invoked in deployed installs.
|
||||
- Not a pytest fixture. Manual workflow only.
|
||||
- No per-stream jitter, audio, non-mp4 inputs, or CLI flags.
|
||||
- No Python rewrite or `vigilar sim` subcommand.
|
||||
|
||||
## Architecture
|
||||
|
||||
One bash script, one helper binary (MediaMTX), one ffmpeg process per stream.
|
||||
|
||||
```
|
||||
scripts/sim_cameras.sh
|
||||
│
|
||||
├── starts: mediamtx (127.0.0.1:8554) ← single static Go binary
|
||||
├── reads: config/vigilar.toml ← discovers camera ids
|
||||
├── for each [[cameras]] block:
|
||||
│ expects videos/<camera_id>.mp4
|
||||
│ spawns: ffmpeg -stream_loop -1 -re -i videos/<id>.mp4 \
|
||||
│ -c copy -f rtsp rtsp://127.0.0.1:8554/<id>
|
||||
├── writes: config/vigilar.sim.toml ← copy of real config,
|
||||
│ rtsp_urls rewritten
|
||||
└── foreground; Ctrl-C kills all children
|
||||
```
|
||||
|
||||
Operator workflow:
|
||||
|
||||
```
|
||||
$ ./scripts/sim_cameras.sh
|
||||
→ serving 5 streams on 127.0.0.1:8554
|
||||
→ wrote config/vigilar.sim.toml
|
||||
$ vigilar start --config config/vigilar.sim.toml # in another terminal
|
||||
```
|
||||
|
||||
Strict 1:1 mapping — each simulated stream corresponds to exactly one video
|
||||
file, named after the camera id in `vigilar.toml`. No file reuse, no cycling.
|
||||
|
||||
## Components
|
||||
|
||||
### `scripts/sim_cameras.sh`
|
||||
|
||||
Bash, no Python dependencies. Responsibilities:
|
||||
|
||||
1. Resolve paths: `$REPO/config/vigilar.toml`, `$REPO/videos/`,
|
||||
`$REPO/.sim/mediamtx`, `$REPO/.sim/logs/`.
|
||||
2. Check prerequisites: `ffmpeg` on PATH, `mediamtx` binary present,
|
||||
`videos/` directory exists.
|
||||
3. Parse camera ids from `vigilar.toml` by grepping `^id = "..."` lines
|
||||
inside `[[cameras]]` blocks. No TOML parser — tolerable because the
|
||||
config format is stable and this is dev tooling.
|
||||
4. Verify `videos/<id>.mp4` exists for every camera id. List **all**
|
||||
missing files in one error message, then exit 1.
|
||||
5. Launch `mediamtx` in the background, writing its log to
|
||||
`.sim/logs/mediamtx.log`. Wait ~500ms, then verify the RTSP port
|
||||
(8554) is listening (`ss -lnt` or `/dev/tcp`).
|
||||
6. Launch one ffmpeg process per camera, backgrounded, with stdout/stderr
|
||||
redirected to `.sim/logs/<id>.log`. Track PIDs in a bash array.
|
||||
7. Generate `config/vigilar.sim.toml`: copy the real TOML, rewrite each
|
||||
`rtsp_url = "..."` line to `rtsp://127.0.0.1:8554/<id>` using `sed`
|
||||
scoped per camera block.
|
||||
8. Print a summary: each served URL, the sim config path, and
|
||||
"Press Ctrl-C to stop".
|
||||
9. Install a `trap` on EXIT/INT/TERM that kills mediamtx and every ffmpeg
|
||||
child PID, then removes `.sim/`. Idempotent.
|
||||
10. `wait` on child PIDs so the script blocks until Ctrl-C.
|
||||
|
||||
### `scripts/download_mediamtx.sh`
|
||||
|
||||
Parallel to the existing `scripts/download_model.sh`. Downloads the
|
||||
MediaMTX static binary for the current architecture from the official
|
||||
GitHub release into `.sim/mediamtx`, verifies it is executable, and exits.
|
||||
Run once per machine.
|
||||
|
||||
### `videos/` (gitignored)
|
||||
|
||||
Operator drops `<camera_id>.mp4` files matching camera ids in
|
||||
`config/vigilar.toml`. Format is mp4 only; other extensions are rejected.
|
||||
|
||||
### `.sim/` (gitignored)
|
||||
|
||||
Working directory, recreated each run:
|
||||
- `.sim/mediamtx` — the static binary (installed by `download_mediamtx.sh`)
|
||||
- `.sim/logs/mediamtx.log`
|
||||
- `.sim/logs/<camera_id>.log`
|
||||
|
||||
### `config/vigilar.sim.toml` (gitignored)
|
||||
|
||||
Generated artifact. Overwritten on every run. Never hand-edited. The real
|
||||
`config/vigilar.toml` is read-only from the simulator's perspective.
|
||||
|
||||
### MediaMTX
|
||||
|
||||
Single static Go binary. Default configuration is sufficient — listens on
|
||||
RTSP port 8554, accepts `rtsp://127.0.0.1:8554/<path>` publishes from
|
||||
ffmpeg. No custom yaml required.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Fail loud and early. No silent fallbacks.
|
||||
|
||||
| Condition | Behavior |
|
||||
|---|---|
|
||||
| `ffmpeg` not on PATH | Print install hint, exit 1 |
|
||||
| `.sim/mediamtx` missing | Print "run scripts/download_mediamtx.sh", exit 1 |
|
||||
| `videos/` missing or any `videos/<id>.mp4` missing | List **all** missing files, exit 1 |
|
||||
| MediaMTX fails to bind 8554 | Dump `.sim/logs/mediamtx.log` tail, exit 1 |
|
||||
| ffmpeg child dies mid-run | Log to stderr, leave others running. Vigilar's reconnect logic handles the gap — this is a feature, not a bug. |
|
||||
| Ctrl-C / script death | `trap` kills mediamtx + all ffmpeg PIDs, removes `.sim/` |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
Manual verification — this is dev tooling, not production code.
|
||||
|
||||
1. Drop 5 mp4s matching camera ids into `videos/`, run
|
||||
`./scripts/sim_cameras.sh`. 5 URLs print.
|
||||
2. `ffprobe rtsp://127.0.0.1:8554/<id>` succeeds for each stream.
|
||||
3. In another terminal:
|
||||
`vigilar start --config config/vigilar.sim.toml`. The web UI shows
|
||||
5 camera tiles with live HLS. Waving at phone footage triggers motion
|
||||
events in the event log.
|
||||
4. Kill one ffmpeg process manually. Vigilar marks that camera as
|
||||
disconnected; reconnect loop engages. Restart the same ffmpeg
|
||||
invocation → stream recovers in the UI.
|
||||
5. Ctrl-C the sim script. `pgrep ffmpeg` and `pgrep mediamtx` both empty.
|
||||
`.sim/` removed.
|
||||
6. Scale test: populate 32 dummy mp4s matching 32 camera blocks. Script
|
||||
starts cleanly; CPU remains within reason (subjective; no hard target).
|
||||
|
||||
### Automated Check
|
||||
|
||||
One light test: `tests/unit/test_sim_script.py` runs
|
||||
`bash -n scripts/sim_cameras.sh` (syntax check) and, if `shellcheck` is
|
||||
available, runs it as well. No integration tests — the simulator exists to
|
||||
support manual testing.
|
||||
|
||||
## File Inventory
|
||||
|
||||
New files:
|
||||
- `scripts/sim_cameras.sh`
|
||||
- `scripts/download_mediamtx.sh`
|
||||
- `tests/unit/test_sim_script.py`
|
||||
|
||||
Modified files:
|
||||
- `.gitignore` — add `videos/`, `.sim/`, `config/vigilar.sim.toml`
|
||||
|
||||
No changes to `vigilar/` package code. The simulator is fully external.
|
||||
Reference in New Issue
Block a user