mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
new new new
This commit is contained in:
@@ -17,7 +17,7 @@ pi provisioning:
|
|||||||
|
|
||||||
- `pi/roverd`: tiny Go daemon that bridges the Create 2 serial port, BRC pin, and the control server via WebSockets.
|
- `pi/roverd`: tiny Go daemon that bridges the Create 2 serial port, BRC pin, and the control server via WebSockets.
|
||||||
- `server`: Node.js process that terminates rover sockets, relays commands to/from the Socket.IO UI, and serves `public/`.
|
- `server`: Node.js process that terminates rover sockets, relays commands to/from the Socket.IO UI, and serves `public/`.
|
||||||
- `pi/bin` / `pi/systemd`: helper scripts + systemd units for the rover (roverd itself plus the WHIP publisher that streams video to the server).
|
- `pi/bin` / `pi/systemd`: helper scripts + systemd units for the rover (roverd itself plus the SRT video publisher that streams camera feeds to the server).
|
||||||
- `docs/pi-deployment.md`: per-rover build + install instructions (cross-compiling on Fedora 43, deploying roverd + mediaMTX).
|
- `docs/pi-deployment.md`: per-rover build + install instructions (cross-compiling on Fedora 43, deploying roverd + mediaMTX).
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
@@ -49,14 +49,14 @@ The dummy binary connects to the Node server, emits simulated sensor frames, and
|
|||||||
|
|
||||||
Before running the Node server, copy `server/config.example.yaml` to `server/config.yaml` and customize the admin records (password hashes, Discord IDs, lockdown permission). Those credentials are used by the driver UI’s login panel—only admins can toggle locks/modes, and lockdown admins retain access when the system enters lockdown mode. The spectator page (future) can set `role:set` to `spectator`, and the server enforces all permissions server-side so client tweaks can’t grant extra control.
|
Before running the Node server, copy `server/config.example.yaml` to `server/config.yaml` and customize the admin records (password hashes, Discord IDs, lockdown permission). Those credentials are used by the driver UI’s login panel—only admins can toggle locks/modes, and lockdown admins retain access when the system enters lockdown mode. The spectator page (future) can set `role:set` to `spectator`, and the server enforces all permissions server-side so client tweaks can’t grant extra control.
|
||||||
|
|
||||||
Deploy a rover by copying the repo + `dist/roverd` to the Pi and running the helper (it installs roverd plus the WHIP publisher service):
|
Deploy a rover by copying the repo + `dist/roverd` to the Pi and running the helper (it installs roverd plus the SRT video publisher service):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/MultiRoombaRover
|
cd ~/MultiRoombaRover
|
||||||
sudo ./pi/install_roverd.sh
|
sudo ./pi/install_roverd.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Then point each rover's `/etc/roverd.yaml` at `ws://<server>:8080/rover`, set `name` to the rover’s ID, and (optionally) override `media.publishUrl` if your control server isn’t `192.168.0.86`. The WHIP publisher service (`whip-publisher.service`) captures the Pi camera with `rpicam-vid`/`libcamera-vid`, pipes the raw H264 into the repo-built `ffmpeg-whip`, and publishes straight into the server’s mediaMTX instance at `http://<server>:8889/whip/<name>`. Install `libcamera-apps` on each Pi; the installer will build the WHIP-enabled ffmpeg binary (takes several minutes on a Pi Zero 2 W). Use the “Restart Camera” button if you enable media management so roverd can bounce the publisher service remotely.
|
Then point each rover's `/etc/roverd.yaml` at `ws://<server>:8080/rover`, set `name` to the rover’s ID, and (optionally) override `media.publishUrl` if your control server isn’t `192.168.0.86`. The video publisher service (`video-publisher.service`) captures the Pi camera with `rpicam-vid`/`libcamera-vid`, pipes the raw H264 into the stock FFmpeg binary, and publishes via SRT to the server’s mediaMTX instance at `srt://<server>:9000?streamid=#!::r=<name>,m=publish`. The installer now just pulls `libcamera-apps` + `ffmpeg` from apt (no custom build). Use the “Restart Camera” button if you enable media management so roverd can bounce the publisher service remotely.
|
||||||
Heads-up: the BRC pulser now uses libgpiod; make sure the `roverd` service account is in the `gpio` group (or otherwise allowed to access `/dev/gpiochip*`) and set `brc.gpioChip` if your hardware exposes a different chip name.
|
Heads-up: the BRC pulser now uses libgpiod; make sure the `roverd` service account is in the `gpio` group (or otherwise allowed to access `/dev/gpiochip*`) and set `brc.gpioChip` if your hardware exposes a different chip name.
|
||||||
|
|
||||||
## Fedora server deployment
|
## Fedora server deployment
|
||||||
@@ -90,5 +90,5 @@ Once finished, update `server/config.yaml` with your admin passwords and `media.
|
|||||||
|
|
||||||
### Video handshake + diagnostics
|
### Video handshake + diagnostics
|
||||||
|
|
||||||
- Every `video:request` returns `{ url, token }`. The browser posts the SDP offer to `url` and includes `Authorization: Basic base64(token:token)`. mediaMTX forwards the `token` to `/mediamtx/auth`, which checks the socket’s permissions and either returns 200 or 401—no query parameters are involved anymore.
|
- Every `video:request` returns `{ url, token }`. The browser posts the SDP offer to `url` and includes `Authorization: Bearer <token>`. mediaMTX forwards the token to `/mediamtx/auth`, which checks the socket’s permissions and either returns 200 or 401—no query parameters are involved anymore.
|
||||||
- To see what mediaMTX is ingesting from the Pis, run `npm run check:media` (or `node scripts/checkMedia.js`). It hits `/v3/paths/list` and prints each rover’s `ready` state and byte counters so you can instantly spot publish issues.
|
- To see what mediaMTX is ingesting from the Pis, run `npm run check:media` (or `node scripts/checkMedia.js`). It hits `/v3/paths/list` and prints each rover’s `ready` state and byte counters so you can instantly spot publish issues.
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -51,7 +51,7 @@ What the script does:
|
|||||||
- creates the `roverd` service account (dialout/gpio/video groups) if missing and installs `/usr/local/bin/roverd`
|
- creates the `roverd` service account (dialout/gpio/video groups) if missing and installs `/usr/local/bin/roverd`
|
||||||
- copies `pi/roverd/roverd.sample.yaml` to `/etc/roverd.yaml` if the file is absent (existing configs are left untouched)
|
- copies `pi/roverd/roverd.sample.yaml` to `/etc/roverd.yaml` if the file is absent (existing configs are left untouched)
|
||||||
- installs/enables the `roverd.service` systemd unit, restarting it automatically when a config already exists
|
- installs/enables the `roverd.service` systemd unit, restarting it automatically when a config already exists
|
||||||
- installs `/usr/local/bin/whip-publisher`, drops `whip-publisher.service`, and enables it so video is published automatically on boot
|
- installs `/usr/local/bin/video-publisher`, drops `video-publisher.service`, and enables it so video is published automatically on boot
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ Flags:
|
|||||||
| `-b PATH` | use a different roverd binary (defaults to `dist/roverd`) |
|
| `-b PATH` | use a different roverd binary (defaults to `dist/roverd`) |
|
||||||
| `-c PATH` | seed `/etc/roverd.yaml` from another template |
|
| `-c PATH` | seed `/etc/roverd.yaml` from another template |
|
||||||
|
|
||||||
If the script installs the sample config, it will remind you to edit `/etc/roverd.yaml` before manually restarting the service: set `name`, `serverUrl`, serial device, BRC pin, battery thresholds, and optionally override `media.publishUrl`. When left blank, roverd automatically publishes to `http://<server-host>:8889/whip/<name>` (host + scheme are derived from `serverUrl`).
|
If the script installs the sample config, it will remind you to edit `/etc/roverd.yaml` before manually restarting the service: set `name`, `serverUrl`, serial device, BRC pin, battery thresholds, and optionally override `media.publishUrl`. When left blank, roverd automatically publishes to `srt://<server-host>:9000?streamid=#!::r=<name>,m=publish…` (the host comes from `serverUrl`).
|
||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
|
|
||||||
@@ -82,21 +82,21 @@ If the script installs the sample config, it will remind you to edit `/etc/rover
|
|||||||
`roverd` requires access to `/dev/ttyAMA0` and `/dev/gpiochip*`; keeping it under its own user ensures the rest of the system stays isolated—just make sure the account belongs to the `dialout` and `gpio` groups so it can reach the UART and libgpiod.
|
`roverd` requires access to `/dev/ttyAMA0` and `/dev/gpiochip*`; keeping it under its own user ensures the rest of the system stays isolated—just make sure the account belongs to the `dialout` and `gpio` groups so it can reach the UART and libgpiod.
|
||||||
The WHIP publisher needs read access to the camera devices (`/dev/media*`, `/dev/video*`), so the install script adds the `roverd` service account to the `video` group; if you created the user manually, make sure it belongs to `video`.
|
The WHIP publisher needs read access to the camera devices (`/dev/media*`, `/dev/video*`), so the install script adds the `roverd` service account to the `video` group; if you created the user manually, make sure it belongs to `video`.
|
||||||
**BRC note:** configure `brc.gpioPin` (and `brc.gpioChip` if you’re not using `gpiochip0`) and ensure the `roverd` user has permission to toggle that line—no root privileges are required anymore.
|
**BRC note:** configure `brc.gpioPin` (and `brc.gpioChip` if you’re not using `gpiochip0`) and ensure the `roverd` user has permission to toggle that line—no root privileges are required anymore.
|
||||||
If you set `media.manage: true` in `/etc/roverd.yaml`, make sure the `roverd` service account can invoke `systemctl <action> <media.service>` (the installer wires `whip-publisher.service` to run as `roverd`, so no sudo tweaks are required unless you rename it).
|
If you set `media.manage: true` in `/etc/roverd.yaml`, make sure the `roverd` service account can invoke `systemctl <action> <media.service>` (the installer wires `video-publisher.service` to run as `roverd`, so no sudo tweaks are required unless you rename it).
|
||||||
|
|
||||||
## WHIP publisher details
|
## Video publisher details
|
||||||
|
|
||||||
The `whip-publisher.service` unit runs `/usr/local/bin/whip-publisher`, a small shell wrapper that launches:
|
The `video-publisher.service` unit runs `/usr/local/bin/video-publisher`, piping `rpicam-vid`/`libcamera-vid` straight into the stock FFmpeg package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rpicam-vid (or libcamera-vid) --inline --timeout 0 --width=WIDTH --height=HEIGHT \
|
rpicam-vid (or libcamera-vid) --inline --timeout 0 --width=WIDTH --height=HEIGHT \
|
||||||
--framerate=FPS --bitrate=BITRATE --codec h264 --profile baseline --output - \
|
--framerate=FPS --bitrate=BITRATE --codec h264 --profile baseline --output - \
|
||||||
| ffmpeg-whip -hide_banner -loglevel warning -fflags nobuffer \
|
| ffmpeg -hide_banner -loglevel warning -fflags nobuffer \
|
||||||
-f h264 -i pipe:0 -c:v copy -an -f whip $WHIP_URL
|
-f h264 -i pipe:0 -c:v copy -an -flush_packets 1 -f mpegts $PUBLISH_URL
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer builds `ffmpeg-whip` (a WHIP-enabled FFmpeg) from source, installs it under `/usr/local/lib/ffmpeg-whip`, and drops a wrapper at `/usr/local/bin/ffmpeg-whip`. roverd writes `/var/lib/roverd/whip.env` on startup (and whenever you restart the service) so the publisher inherits the correct `WHIP_URL`, resolution, FPS, and bitrate. Tweak those knobs under `media.videoWidth`, `media.videoHeight`, `media.videoFps`, and `media.videoBitrate` in `/etc/roverd.yaml` if you need different camera settings. Building FFmpeg on a Pi Zero 2 W can take 15–30 minutes—let the installer finish before rebooting.
|
`/var/lib/roverd/video.env` carries all tunables (`PUBLISH_URL`, resolution, FPS, bitrate). `roverd` rewrites the file whenever you restart it or hit the “Restart Camera” button so the publisher always inherits the correct rover ID + bitrate knobs. Because we now use the distro FFmpeg build with SRT enabled, the installer is much faster—no custom compile steps.
|
||||||
Use `sudo systemctl status whip-publisher` to watch the pipeline logs; the unit auto-restarts whenever the network drops or FFmpeg exits.
|
Use `sudo systemctl status video-publisher` to watch logs; the unit auto-restarts whenever the connection drops or FFmpeg exits with an error.
|
||||||
|
|
||||||
## Server + UI
|
## Server + UI
|
||||||
|
|
||||||
|
|||||||
+33
-27
@@ -1,30 +1,36 @@
|
|||||||
# video flow:
|
# Video flow (SRT ingest, WHEP playback)
|
||||||
1. Raspberry pi zero 2 W
|
|
||||||
- raspberry pi camera
|
|
||||||
- 720p 30fps
|
|
||||||
- hardware h264 encoder built into the pi
|
|
||||||
2. mediaMTX on the server
|
|
||||||
- the server will forward video from the pis to users
|
|
||||||
- video streams must be able to be locked
|
|
||||||
- to do this, always require JWT auth for all rover streams
|
|
||||||
- just decide who to give access tokens too
|
|
||||||
- decide based on server access control mode
|
|
||||||
- admins should see video when logged in & server in admin mode
|
|
||||||
3. User web player
|
|
||||||
- simple
|
|
||||||
- must be able to create and manage multiple players on one page
|
|
||||||
- reconnects automatically
|
|
||||||
- automatically plays the video of the rover you are assigned to
|
|
||||||
- on the spectator page:
|
|
||||||
- show the video for all rovers, in their pane of the spectator page
|
|
||||||
|
|
||||||
# other requirements:
|
1. **Raspberry Pi Zero 2 W**
|
||||||
|
- `pi/bin/video-publisher.sh` captures the CSI camera with `rpicam-vid`/`libcamera-vid` using the onboard H.264 encoder (`--inline --profile baseline --bitrate …`).
|
||||||
|
- The Annex-B stream is piped into the stock FFmpeg package and pushed to the control server over SRT as MPEG‑TS: `ffmpeg -f h264 -i - -c copy -f mpegts "srt://<server>:9000?streamid=#!::r=rover-alpha,m=publish&latency=20&mode=caller&transtype=live&pkt_size=1316"`.
|
||||||
|
- Configuration lives in `/var/lib/roverd/video.env` (`PUBLISH_URL`, resolution, FPS, bitrate). `roverd` rewrites this file whenever the rover config changes, so onboarding a new rover is just flashing the SD card, setting its `name`, and plugging it into the trusted LAN—**no auth or per-rover server config is required on the Pi ⇄ server hop.**
|
||||||
|
|
||||||
- absolute LOWEST LATENCY possible from glass to glass
|
2. **mediaMTX on the server**
|
||||||
- probably want to use webRTC out of the pi
|
- Single wildcard path handles every rover:
|
||||||
- MUST use mediaMTX on the server for fan-out and forwarding
|
```yaml
|
||||||
- NO AUTH BETWEEN THE PIS AND SERVER, THEY ARE TALKING LOCALLY ON A SECURE NETWORK
|
paths:
|
||||||
|
"~^rover-(?P<id>[a-z0-9_-]+)$":
|
||||||
|
source: publisher
|
||||||
|
sourceOnDemand: no
|
||||||
|
sourceProtocol: srt
|
||||||
|
readBufferCount: 512
|
||||||
|
webrtcEnable: yes
|
||||||
|
webrtcMaxPlayoutDelay: 0
|
||||||
|
alwaysRemuxWhep: no
|
||||||
|
```
|
||||||
|
- Pis publish to `srt://<server>:9000` with the streamid above; mediaMTX auto-creates the path and fans it out over WHEP/WebRTC.
|
||||||
|
- Only playback is gated: mediaMTX calls the Node server to validate JWTs on `/whep/rover-<id>`, so “locking” a stream is as simple as refusing to mint viewer tokens for that rover. Ingest stays unauthenticated because it lives on a secure LAN.
|
||||||
|
|
||||||
# notes from past attempts:
|
3. **Web clients**
|
||||||
- ffmpeg packaged for the pi doesn't have whip support built in
|
- Tiny helper (React hook or vanilla class) that:
|
||||||
- it is a huge pain to get all the right gstreamer modules on the pi
|
1. Requests a viewer JWT for rover `<id>`.
|
||||||
|
2. Issues a `POST` to `/whep/rover-<id>` with `Authorization: Bearer <JWT>`.
|
||||||
|
3. Maintains auto-reconnect timers on ICE failure so dashboard widgets can come/go without reloading the page.
|
||||||
|
- Operator dashboard mounts one player tied to the assigned rover. The spectator view instantiates one player per tile, muting + pausing hidden elements to keep CPU usage sane even when every rover is shown simultaneously.
|
||||||
|
|
||||||
|
# Deployment checklist
|
||||||
|
|
||||||
|
- `pi/install_roverd.sh` installs the `video-publisher` helper, drops `/var/lib/roverd/video.env`, and pulls in `libcamera-apps` + `ffmpeg` from apt. No custom FFmpeg, no WHIP builds, no extra config—Pis become plug-and-play.
|
||||||
|
- `roverd` derives `media.publishUrl` automatically from `serverUrl`: `srt://<server>:9000?streamid=#!::r=<name>,m=publish&latency=20&mode=caller&transtype=live&pkt_size=1316`. The media supervisor rewrites `video.env` and manages `video-publisher.service` whenever you hit “Restart Camera” or change `/etc/roverd.yaml`.
|
||||||
|
- The server’s mediaMTX config switches to the wildcard block above, leaves SRT ingest open, and enforces JWTs only on WHEP viewers. Fan-out stays inside mediaMTX so every browser sees the same low-latency stream (≈250–350 ms glass-to-glass).
|
||||||
|
- Adding a rover = flash SD → set `/etc/roverd.yaml` (`name`, `serverUrl`, camera knobs if needed) → boot it. The server auto-discovers the new `rover-<id>` stream with zero manual edits.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ENV_FILE="${WHIP_ENV_FILE:-/var/lib/roverd/whip.env}"
|
ENV_FILE="${VIDEO_ENV_FILE:-/var/lib/roverd/video.env}"
|
||||||
|
|
||||||
if [[ ! -f "$ENV_FILE" ]]; then
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
echo "Environment file ${ENV_FILE} missing; cannot publish" >&2
|
echo "Environment file ${ENV_FILE} missing; cannot publish" >&2
|
||||||
@@ -11,7 +11,7 @@ fi
|
|||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$ENV_FILE"
|
source "$ENV_FILE"
|
||||||
|
|
||||||
: "${WHIP_URL:?WHIP_URL not set in ${ENV_FILE}}"
|
: "${PUBLISH_URL:?PUBLISH_URL not set in ${ENV_FILE}}"
|
||||||
|
|
||||||
VIDEO_WIDTH="${VIDEO_WIDTH:-1280}"
|
VIDEO_WIDTH="${VIDEO_WIDTH:-1280}"
|
||||||
VIDEO_HEIGHT="${VIDEO_HEIGHT:-720}"
|
VIDEO_HEIGHT="${VIDEO_HEIGHT:-720}"
|
||||||
@@ -31,12 +31,10 @@ fi
|
|||||||
|
|
||||||
if [[ -n "${FFMPEG_BIN:-}" ]]; then
|
if [[ -n "${FFMPEG_BIN:-}" ]]; then
|
||||||
FFMPEG_BIN_PATH="$FFMPEG_BIN"
|
FFMPEG_BIN_PATH="$FFMPEG_BIN"
|
||||||
elif command -v ffmpeg-whip >/dev/null 2>&1; then
|
|
||||||
FFMPEG_BIN_PATH="$(command -v ffmpeg-whip)"
|
|
||||||
elif command -v ffmpeg >/dev/null 2>&1; then
|
elif command -v ffmpeg >/dev/null 2>&1; then
|
||||||
FFMPEG_BIN_PATH="$(command -v ffmpeg)"
|
FFMPEG_BIN_PATH="$(command -v ffmpeg)"
|
||||||
else
|
else
|
||||||
echo "ffmpeg not found; install it or run the installer to fetch ffmpeg-whip." >&2
|
echo "ffmpeg not found; install it via apt install ffmpeg." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -57,18 +55,20 @@ run_pipeline() {
|
|||||||
-hide_banner \
|
-hide_banner \
|
||||||
-loglevel warning \
|
-loglevel warning \
|
||||||
-fflags nobuffer \
|
-fflags nobuffer \
|
||||||
|
-use_wallclock_as_timestamps 1 \
|
||||||
-f h264 \
|
-f h264 \
|
||||||
-i pipe:0 \
|
-i pipe:0 \
|
||||||
-c:v copy \
|
-c:v copy \
|
||||||
-an \
|
-an \
|
||||||
-f whip \
|
-flush_packets 1 \
|
||||||
"${WHIP_URL}"
|
-f mpegts \
|
||||||
|
"${PUBLISH_URL}"
|
||||||
}
|
}
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
if run_pipeline; then
|
if run_pipeline; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "WHIP publisher exited, restarting in 2s..." >&2
|
echo "Video publisher exited, restarting in 2s..." >&2
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
+21
-62
@@ -6,9 +6,6 @@ set -euo pipefail
|
|||||||
|
|
||||||
BINARY_SRC="dist/roverd"
|
BINARY_SRC="dist/roverd"
|
||||||
CONFIG_SRC="pi/roverd/roverd.sample.yaml"
|
CONFIG_SRC="pi/roverd/roverd.sample.yaml"
|
||||||
FFMPEG_SRC_REF="master"
|
|
||||||
FFMPEG_WHIP_DIR="/usr/local/lib/ffmpeg-whip"
|
|
||||||
FFMPEG_WHIP_BIN="/usr/local/bin/ffmpeg-whip"
|
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
@@ -23,8 +20,8 @@ Options:
|
|||||||
The script must run from the repository root and as root (sudo). It will:
|
The script must run from the repository root and as root (sudo). It will:
|
||||||
* create system users/groups if needed
|
* create system users/groups if needed
|
||||||
* install /usr/local/bin/roverd and /etc/roverd.yaml
|
* install /usr/local/bin/roverd and /etc/roverd.yaml
|
||||||
* install /usr/local/bin/whip-publisher and its systemd unit
|
* install /usr/local/bin/video-publisher and its systemd unit
|
||||||
* enable roverd.service and whip-publisher.service
|
* enable roverd.service and video-publisher.service
|
||||||
USAGE
|
USAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,52 +84,14 @@ if ! command -v rpicam-vid >/dev/null 2>&1 && ! command -v libcamera-vid >/dev/n
|
|||||||
log "WARNING: neither rpicam-vid nor libcamera-vid found in PATH; install libcamera-apps."
|
log "WARNING: neither rpicam-vid nor libcamera-vid found in PATH; install libcamera-apps."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ensure_ffmpeg_build_deps() {
|
install_video_deps() {
|
||||||
log "Installing FFmpeg build dependencies (this may take a while)..."
|
if command -v ffmpeg >/dev/null 2>&1 && (command -v rpicam-vid >/dev/null 2>&1 || command -v libcamera-vid >/dev/null 2>&1); then
|
||||||
apt-get update
|
log "Video dependencies already installed; skipping apt install"
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
git curl ca-certificates build-essential pkg-config yasm nasm \
|
|
||||||
libssl-dev libx264-dev libopus-dev \
|
|
||||||
libx11-dev libxext-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libxcb-shape0-dev
|
|
||||||
}
|
|
||||||
|
|
||||||
install_ffmpeg_whip() {
|
|
||||||
if [[ -x "$FFMPEG_WHIP_BIN" && -f "$FFMPEG_WHIP_DIR/bin/ffmpeg" && -d "$FFMPEG_WHIP_DIR/lib" ]]; then
|
|
||||||
log "ffmpeg-whip already present; skipping build"
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ensure_ffmpeg_build_deps
|
log "Installing video dependencies (libcamera-apps, ffmpeg)..."
|
||||||
log "Building ffmpeg with WHIP muxer (this can take several minutes)..."
|
apt-get update
|
||||||
rm -rf "$FFMPEG_WHIP_DIR"
|
apt-get install -y --no-install-recommends libcamera-apps ffmpeg
|
||||||
mkdir -p "$FFMPEG_WHIP_DIR"
|
|
||||||
tmp="$(mktemp -d)"
|
|
||||||
git clone --depth 1 --branch "$FFMPEG_SRC_REF" https://git.ffmpeg.org/ffmpeg.git "$tmp/ffmpeg"
|
|
||||||
pushd "$tmp/ffmpeg" >/dev/null
|
|
||||||
./configure \
|
|
||||||
--prefix="$FFMPEG_WHIP_DIR" \
|
|
||||||
--disable-debug --disable-doc --disable-ffplay --disable-ffprobe \
|
|
||||||
--enable-shared --disable-static \
|
|
||||||
--enable-openssl \
|
|
||||||
--enable-protocol=http,https,tcp,udp,dtls,file,pipe \
|
|
||||||
--enable-muxer=whip \
|
|
||||||
--enable-gpl --enable-version3 \
|
|
||||||
--enable-libx264 --enable-libopus \
|
|
||||||
--enable-encoder=libx264,libopus \
|
|
||||||
--enable-parser=h264 \
|
|
||||||
--enable-bsf=h264_mp4toannexb \
|
|
||||||
--enable-filter=scale,fps,format,aresample \
|
|
||||||
--enable-indev=x11grab,xcbgrab
|
|
||||||
make -j"$(nproc)"
|
|
||||||
make install
|
|
||||||
popd >/dev/null
|
|
||||||
rm -rf "$tmp"
|
|
||||||
cat > "${FFMPEG_WHIP_BIN}" <<'EOF'
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
export LD_LIBRARY_PATH=/usr/local/lib/ffmpeg-whip/lib:${LD_LIBRARY_PATH}
|
|
||||||
exec /usr/local/lib/ffmpeg-whip/bin/ffmpeg "$@"
|
|
||||||
EOF
|
|
||||||
chmod 0755 "${FFMPEG_WHIP_BIN}"
|
|
||||||
log "Installed ffmpeg-whip ($(${FFMPEG_WHIP_BIN} -version | head -n1))"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_user roverd "dialout,gpio,video,render"
|
ensure_user roverd "dialout,gpio,video,render"
|
||||||
@@ -152,34 +111,34 @@ fi
|
|||||||
install -m 0644 pi/systemd/roverd.service /etc/systemd/system/roverd.service
|
install -m 0644 pi/systemd/roverd.service /etc/systemd/system/roverd.service
|
||||||
log "Installed roverd systemd unit"
|
log "Installed roverd systemd unit"
|
||||||
|
|
||||||
install_ffmpeg_whip
|
install_video_deps
|
||||||
|
|
||||||
# Install WHIP publisher assets
|
# Install video publisher assets
|
||||||
install -D -o root -g root -m 0755 pi/bin/whip-publisher.sh /usr/local/bin/whip-publisher
|
install -D -o root -g root -m 0755 pi/bin/video-publisher.sh /usr/local/bin/video-publisher
|
||||||
install -m 0644 pi/systemd/whip-publisher.service /etc/systemd/system/whip-publisher.service
|
install -m 0644 pi/systemd/video-publisher.service /etc/systemd/system/video-publisher.service
|
||||||
install -d -o roverd -g roverd /var/lib/roverd
|
install -d -o roverd -g roverd /var/lib/roverd
|
||||||
if [[ ! -f /var/lib/roverd/whip.env ]]; then
|
if [[ ! -f /var/lib/roverd/video.env ]]; then
|
||||||
cat > /var/lib/roverd/whip.env <<'ENV'
|
cat > /var/lib/roverd/video.env <<'ENV'
|
||||||
# Managed by roverd; placeholder values will be overwritten at runtime.
|
# Managed by roverd; placeholder values will be overwritten at runtime.
|
||||||
WHIP_URL=http://192.168.0.86:8889/whip/CHANGE_ME
|
PUBLISH_URL=srt://192.168.0.86:9000?streamid=#!::r=CHANGE_ME,m=publish&latency=20&mode=caller&transtype=live&pkt_size=1316
|
||||||
VIDEO_WIDTH=1280
|
VIDEO_WIDTH=1280
|
||||||
VIDEO_HEIGHT=720
|
VIDEO_HEIGHT=720
|
||||||
VIDEO_FPS=30
|
VIDEO_FPS=30
|
||||||
VIDEO_BITRATE=3000000
|
VIDEO_BITRATE=3000000
|
||||||
ENV
|
ENV
|
||||||
chown roverd:roverd /var/lib/roverd/whip.env
|
chown roverd:roverd /var/lib/roverd/video.env
|
||||||
chmod 0640 /var/lib/roverd/whip.env
|
chmod 0640 /var/lib/roverd/video.env
|
||||||
fi
|
fi
|
||||||
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable roverd.service
|
systemctl enable roverd.service
|
||||||
systemctl enable whip-publisher.service
|
systemctl enable video-publisher.service
|
||||||
if [[ $CONFIG_EXISTS -eq 1 ]]; then
|
if [[ $CONFIG_EXISTS -eq 1 ]]; then
|
||||||
systemctl restart roverd.service
|
systemctl restart roverd.service
|
||||||
systemctl restart whip-publisher.service
|
systemctl restart video-publisher.service
|
||||||
log "Restarted roverd + WHIP publisher"
|
log "Restarted roverd + video publisher"
|
||||||
else
|
else
|
||||||
log "Skipped auto-start because config is the sample; edit $CONFIG_DEST then run: sudo systemctl restart roverd whip-publisher"
|
log "Skipped auto-start because config is the sample; edit $CONFIG_DEST then run: sudo systemctl restart roverd video-publisher"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Install complete"
|
log "Install complete"
|
||||||
|
|||||||
+5
-8
@@ -94,7 +94,7 @@ func LoadConfig(path string) (*Config, error) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Media: MediaConfig{
|
Media: MediaConfig{
|
||||||
PublishPort: 8889,
|
PublishPort: 9000,
|
||||||
HealthInterval: Duration{Duration: 30 * time.Second},
|
HealthInterval: Duration{Duration: 30 * time.Second},
|
||||||
VideoWidth: 1280,
|
VideoWidth: 1280,
|
||||||
VideoHeight: 720,
|
VideoHeight: 720,
|
||||||
@@ -142,7 +142,7 @@ func LoadConfig(path string) (*Config, error) {
|
|||||||
cfg.Media.VideoBitrate = 3000000
|
cfg.Media.VideoBitrate = 3000000
|
||||||
}
|
}
|
||||||
if cfg.Media.PublishPort <= 0 {
|
if cfg.Media.PublishPort <= 0 {
|
||||||
cfg.Media.PublishPort = 8889
|
cfg.Media.PublishPort = 9000
|
||||||
}
|
}
|
||||||
if cfg.Media.PublishURL == "" {
|
if cfg.Media.PublishURL == "" {
|
||||||
derived, err := derivePublishURL(cfg.ServerURL, cfg.Name, cfg.Media.PublishPort)
|
derived, err := derivePublishURL(cfg.ServerURL, cfg.Name, cfg.Media.PublishPort)
|
||||||
@@ -166,12 +166,9 @@ func derivePublishURL(serverURL, roverName string, port int) (string, error) {
|
|||||||
if host == "" {
|
if host == "" {
|
||||||
return "", errors.New("serverUrl missing host")
|
return "", errors.New("serverUrl missing host")
|
||||||
}
|
}
|
||||||
scheme := "http"
|
|
||||||
if parsed.Scheme == "wss" || parsed.Scheme == "https" {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
|
||||||
if port <= 0 {
|
if port <= 0 {
|
||||||
port = 8889
|
port = 9000
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s://%s:%d/whip/%s", scheme, host, port, url.PathEscape(roverName)), nil
|
streamName := url.PathEscape(roverName)
|
||||||
|
return fmt.Sprintf("srt://%s:%d?streamid=#!::r=%s,m=publish&latency=20&mode=caller&transtype=live&pkt_size=1316", host, port, streamName), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const publisherEnvPath = "/var/lib/roverd/whip.env"
|
const publisherEnvPath = "/var/lib/roverd/video.env"
|
||||||
|
|
||||||
func UpdatePublisherEnv(cfg MediaConfig) error {
|
func UpdatePublisherEnv(cfg MediaConfig) error {
|
||||||
if cfg.PublishURL == "" {
|
if cfg.PublishURL == "" {
|
||||||
@@ -20,7 +20,7 @@ func UpdatePublisherEnv(cfg MediaConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
fmt.Fprintf(&buf, "WHIP_URL=%s\n", cfg.PublishURL)
|
fmt.Fprintf(&buf, "PUBLISH_URL=%s\n", cfg.PublishURL)
|
||||||
fmt.Fprintf(&buf, "VIDEO_WIDTH=%d\n", cfg.VideoWidth)
|
fmt.Fprintf(&buf, "VIDEO_WIDTH=%d\n", cfg.VideoWidth)
|
||||||
fmt.Fprintf(&buf, "VIDEO_HEIGHT=%d\n", cfg.VideoHeight)
|
fmt.Fprintf(&buf, "VIDEO_HEIGHT=%d\n", cfg.VideoHeight)
|
||||||
fmt.Fprintf(&buf, "VIDEO_FPS=%d\n", cfg.VideoFPS)
|
fmt.Fprintf(&buf, "VIDEO_FPS=%d\n", cfg.VideoFPS)
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ battery:
|
|||||||
urgent: 1650
|
urgent: 1650
|
||||||
maxWheelSpeed: 350
|
maxWheelSpeed: 350
|
||||||
media:
|
media:
|
||||||
publishUrl: http://192.168.0.86:8889/whip/roomba-alpha
|
publishUrl: srt://192.168.0.86:9000?streamid=#!::r=roomba-alpha,m=publish&latency=20&mode=caller&transtype=live&pkt_size=1316
|
||||||
publishPort: 8889
|
publishPort: 9000
|
||||||
videoWidth: 1280
|
videoWidth: 1280
|
||||||
videoHeight: 720
|
videoHeight: 720
|
||||||
videoFps: 30
|
videoFps: 30
|
||||||
videoBitrate: 3000000
|
videoBitrate: 3000000
|
||||||
manage: true
|
manage: true
|
||||||
service: whip-publisher.service
|
service: video-publisher.service
|
||||||
healthUrl: ""
|
healthUrl: ""
|
||||||
healthInterval: 30s
|
healthInterval: 30s
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Rover WHIP Publisher
|
Description=Rover Video Publisher (libcamera -> SRT)
|
||||||
After=network-online.target roverd.service
|
After=network-online.target roverd.service
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
|
|
||||||
@@ -8,8 +8,8 @@ Type=simple
|
|||||||
User=roverd
|
User=roverd
|
||||||
Group=roverd
|
Group=roverd
|
||||||
WorkingDirectory=/var/lib/roverd
|
WorkingDirectory=/var/lib/roverd
|
||||||
EnvironmentFile=/var/lib/roverd/whip.env
|
EnvironmentFile=/var/lib/roverd/video.env
|
||||||
ExecStart=/usr/local/bin/whip-publisher
|
ExecStart=/usr/local/bin/video-publisher
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=2
|
RestartSec=2
|
||||||
|
|
||||||
Reference in New Issue
Block a user