chore: snapshot livekit-cameras after serial pin + speechbrain lengths fix
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# livekit-cameras
|
||||
|
||||
Publishes 20 Logitech C920 webcam streams (video + cleaned audio) to a
|
||||
LiveKit room.
|
||||
|
||||
Each camera becomes one LiveKit participant (`cam-01` .. `cam-20`) that
|
||||
publishes:
|
||||
- a camera video track (1280x720 @ 30 fps by default, MJPG from V4L2)
|
||||
- a microphone audio track (16 kHz mono, cleaned with speechbrain)
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
run.py orchestrator: discovery + per-camera worker processes
|
||||
run_worker.py one camera: ffmpeg capture -> LiveKit track publish
|
||||
discovery.py serial-based camera <-> ALSA card discovery
|
||||
audio_cleanup.py speechbrain SpectralMaskEnhancement (16 kHz) + fallback
|
||||
tokens.py LiveKit JWT generation
|
||||
config.py .env / environment configuration
|
||||
prefetch_model.py one-time speechbrain model download
|
||||
generate_udev_rules.py (re)generate udev pinning + slots.json from live state
|
||||
slots.json stable slot map: cam-NN -> USB serial, /dev/camNN, ALSA card
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
uv venv --python 3.12
|
||||
uv pip install -p .venv/bin/python -r pyproject.toml # or: uv sync
|
||||
cp .env.example .env # then fill in LIVEKIT_URL / API_KEY / API_SECRET
|
||||
.venv/bin/python prefetch_model.py # download speechbrain model once
|
||||
```
|
||||
|
||||
## Stable camera identity (cam-01 is always cam-01)
|
||||
|
||||
Each C920 has a unique built-in USB serial — that is the camera's UUID.
|
||||
udev rules (`/etc/udev/rules.d/99-c920-pin.rules`, generated from the live
|
||||
state) bind every slot to a serial, so identity survives reboots, hub
|
||||
reordering, and any connect sequence:
|
||||
|
||||
- video: `/dev/cam01` .. `/dev/cam20` (primary UVC node; `.meta` = metadata)
|
||||
- audio: fixed ALSA card per slot, `hw:2,0` .. `hw:21,0` (SOUND_CARD_INDEX)
|
||||
|
||||
`discovery.py` assigns identities from `slots.json` (serial lookup, with a
|
||||
port-based fallback for cameras not yet in the map). To re-map slots (e.g.
|
||||
after physically swapping cameras), regenerate:
|
||||
|
||||
```bash
|
||||
.venv/bin/python generate_udev_rules.py --write
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --subsystem-match=video4linux --action=add
|
||||
sudo udevadm trigger --subsystem-match=sound --action=add
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
.venv/bin/python run.py # all 20 cameras
|
||||
.venv/bin/python run.py --cameras 1,5 # subset
|
||||
.venv/bin/python run.py --once # status report after timeout
|
||||
```
|
||||
|
||||
## Audio cleanup
|
||||
|
||||
The mic audio is cleaned per chunk (default 200 ms) by
|
||||
`speechbrain/metricgan-plus-voicebank` (16 kHz, CPU). One worker process per
|
||||
camera loads the model once and reuses it; on this 28-core box all 20 stay
|
||||
real-time. `ENHANCE_MODE=off` (or a failed model load with `auto`) falls back
|
||||
to a lightweight high-pass + soft-clip DSP path so streams still work.
|
||||
|
||||
## Notes
|
||||
|
||||
- Video uses MJPG directly from the UVC interface (`ffmpeg -f v4l2
|
||||
-input_format mjpeg`) — cheap, and converts to BGRA for LiveKit.
|
||||
- Audio: ALSA mics on these C920s deliver 16 kHz **stereo** only; the worker
|
||||
downmixes to mono for LiveKit.
|
||||
- Camera <-> card pairing is resolved by matching USB port paths under /sys,
|
||||
so each mic belongs to its own camera.
|
||||
- Workers auto-restart (up to 10 times) if a camera drops.
|
||||
Reference in New Issue
Block a user