clean up whep delivery

This commit is contained in:
legop3
2025-11-14 01:45:06 -05:00
parent 31e31bfe2d
commit c0dce561bd
9 changed files with 40 additions and 28 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ cd ~/MultiRoombaRover
sudo ./pi/install_roverd.sh --mediamtx sudo ./pi/install_roverd.sh --mediamtx
``` ```
Then point each rover's `/etc/roverd.yaml` at `ws://<server>:8080/rover`, set (or leave blank to auto-derive) `media.whepUrl` for the Pis mediaMTX instance (defaults to `http://<pi-ip>:8889/whep/rovercam`), enable the sensor stream from the UI, and drive with WASD. Then point each rover's `/etc/roverd.yaml` at `ws://<server>:8080/rover`, enable the sensor stream from the UI, and drive with WASD. `roverd` automatically advertises its local mediaMTX WHEP endpoint (derived from the Pis DHCP address), so no video URL configuration is required unless you want to override the defaults.
Use the “Restart Camera” button if you enable media management so roverd can bounce the mediamtx service remotely. Use the “Restart Camera” button if you enable media management so roverd can bounce the mediamtx 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.
-1
View File
@@ -14,7 +14,6 @@ battery:
urgent: 1650 urgent: 1650
maxWheelSpeed: 350 maxWheelSpeed: 350
media: media:
whepUrl: http://dummy1.local:8889/whep/rovercam
manage: false manage: false
service: mediamtx.service service: mediamtx.service
healthUrl: http://127.0.0.1:9997/v3/paths/list healthUrl: http://127.0.0.1:9997/v3/paths/list
-1
View File
@@ -14,7 +14,6 @@ battery:
urgent: 1650 urgent: 1650
maxWheelSpeed: 350 maxWheelSpeed: 350
media: media:
whepUrl: http://dummy2.local:8889/whep/rovercam
manage: false manage: false
service: mediamtx.service service: mediamtx.service
healthUrl: http://127.0.0.1:9997/v3/paths/list healthUrl: http://127.0.0.1:9997/v3/paths/list
Vendored
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -5,7 +5,7 @@ Each rover runs mediaMTX locally to capture the Pi camera, and the control serve
## Pi (publisher) ## Pi (publisher)
- mediaMTX samples the Pi camera (`paths.rovercam.source: rpiCamera`) and exposes the HTTP API on `http://127.0.0.1:9997`. - mediaMTX samples the Pi camera (`paths.rovercam.source: rpiCamera`) and exposes the HTTP API on `http://127.0.0.1:9997`.
- `/etc/roverd.yaml` contains `media.whepUrl`, pointing at the Pis own WHEP endpoint (e.g. `http://roomba-alpha.local:8889/whep/rovercam`). This is what the central server will pull. - `roverd` automatically detects the Pis own WHEP endpoint (e.g. `http://roomba-alpha.local:8889/whep/rovercam`) and reports a `bridgeWhepUrl` (converted to `whep://.../whep`) to the server. No manual per-rover video URL configuration is required.
- The `media.manage` flag keeps the local service alive via `systemctl` and hits the API for health checks (`media.healthUrl`, defaults to `http://127.0.0.1:9997/v3/paths/list`). - The `media.manage` flag keeps the local service alive via `systemctl` and hits the API for health checks (`media.healthUrl`, defaults to `http://127.0.0.1:9997/v3/paths/list`).
## Control server (viewer) ## Control server (viewer)
+33
View File
@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/url"
"os" "os"
"strings" "strings"
"time" "time"
@@ -59,6 +60,7 @@ type MediaConfig struct {
WhepPort int `yaml:"whepPort" json:"-"` WhepPort int `yaml:"whepPort" json:"-"`
WhepPath string `yaml:"whepPath" json:"-"` WhepPath string `yaml:"whepPath" json:"-"`
LegacyPublish string `yaml:"publishUrl,omitempty" json:"-"` LegacyPublish string `yaml:"publishUrl,omitempty" json:"-"`
BridgeWhepURL string `yaml:"-" json:"bridgeWhepUrl,omitempty"`
Manage bool `yaml:"manage"` Manage bool `yaml:"manage"`
Service string `yaml:"service"` Service string `yaml:"service"`
HealthURL string `yaml:"healthUrl"` HealthURL string `yaml:"healthUrl"`
@@ -141,9 +143,40 @@ func LoadConfig(path string) (*Config, error) {
scheme := "http" scheme := "http"
cfg.Media.WhepURL = fmt.Sprintf("%s://%s:%d%s", scheme, ip, effectivePort(cfg.Media.WhepPort), path) cfg.Media.WhepURL = fmt.Sprintf("%s://%s:%d%s", scheme, ip, effectivePort(cfg.Media.WhepPort), path)
} }
if bridge, err := buildBridgeURL(cfg.Media.WhepURL); err == nil {
cfg.Media.BridgeWhepURL = bridge
}
return &cfg, nil return &cfg, nil
} }
func buildBridgeURL(src string) (string, error) {
if src == "" {
return "", errors.New("empty whep url")
}
parsed, err := url.Parse(src)
if err != nil {
return "", err
}
if parsed.Host == "" {
return "", errors.New("missing host")
}
proto := "whep"
if parsed.Scheme == "https" {
proto = "wheps"
}
path := parsed.Path
if path == "" || path == "/" {
path = "/"
}
if !strings.HasSuffix(path, "/whep") {
if !strings.HasSuffix(path, "/") {
path += "/"
}
path += "whep"
}
return fmt.Sprintf("%s://%s%s", proto, parsed.Host, path), nil
}
func ensureLeadingSlash(path string) string { func ensureLeadingSlash(path string) string {
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
return "/" + path return "/" + path
-1
View File
@@ -15,7 +15,6 @@ battery:
urgent: 1650 urgent: 1650
maxWheelSpeed: 350 maxWheelSpeed: 350
media: media:
whepUrl: http://roomba-alpha.local:8889/whep/rovercam
manage: false manage: false
service: mediamtx.service service: mediamtx.service
healthUrl: http://127.0.0.1:9997/v3/paths/list healthUrl: http://127.0.0.1:9997/v3/paths/list
-1
View File
@@ -15,7 +15,6 @@ battery:
urgent: 1650 urgent: 1650
maxWheelSpeed: 350 maxWheelSpeed: 350
media: media:
whepUrl: http://roomba-alpha.local:8889/whep/rovercam
manage: false manage: false
service: mediamtx.service service: mediamtx.service
healthUrl: http://127.0.0.1:9997/v3/paths/list healthUrl: http://127.0.0.1:9997/v3/paths/list
+5 -22
View File
@@ -36,8 +36,12 @@ async function syncRover(record) {
await removePath(record?.id); await removePath(record?.id);
return; return;
} }
const source = normalizeSource(record.meta.media.whepUrl); const source = record.meta.media.bridgeWhepUrl;
if (!source) { if (!source) {
logger.warn(
'rover %s missing bridgeWhepUrl; video bridge disabled for this rover',
record.id
);
await removePath(record.id); await removePath(record.id);
return; return;
} }
@@ -81,27 +85,6 @@ async function removePath(roverId) {
} }
} }
function normalizeSource(raw) {
if (!raw) return null;
const trimmed = raw.trim();
if (!trimmed) return null;
try {
const parsed = new URL(/^[a-z]+:\/\//i.test(trimmed) ? trimmed : `http://${trimmed}`);
const protocol = parsed.protocol === 'https:' ? 'wheps' : 'whep';
let path = parsed.pathname || '/';
if (!path.endsWith('/whep')) {
if (!path.endsWith('/')) {
path += '/';
}
path += 'whep';
}
return `${protocol}://${parsed.host}${path}`;
} catch (err) {
logger.warn('invalid WHEP URL: %s (%s)', raw, err.message);
return null;
}
}
async function callApi(method, path, body) { async function callApi(method, path, body) {
const url = `${apiBase}${path}`; const url = `${apiBase}${path}`;
const res = await fetch(url, { const res = await fetch(url, {