mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
191 lines
5.1 KiB
Markdown
191 lines
5.1 KiB
Markdown
# Issue 002: Multi-Rover WebRTC Media Fan-Out
|
|
|
|
## Summary
|
|
|
|
`/spectate` mounts several `RoverMediaPlayer` instances at once. In the fresh runtime
|
|
audit, Chromium reported 6 active `RTCPeerConnections`. That is a large baseline for
|
|
low-end clients, especially while the page is also handling telemetry, snapshots, logs,
|
|
chat, and SVG HUD maps.
|
|
|
|
## Severity
|
|
|
|
High.
|
|
|
|
This may not show up as React mutations, but it consumes CPU in browser media pipelines,
|
|
WebRTC negotiation, decoding, audio handling, and event dispatch. It also scales with the
|
|
number of rovers.
|
|
|
|
## Upstream Likelihood
|
|
|
|
Low to medium.
|
|
|
|
General `RoverMediaPlayer` improvements can help both `/` and `/spectate`, but the core
|
|
problem is spectate-specific: `/spectate` intentionally displays multiple rover media
|
|
players at once. The fix needs a spectator policy for active feeds.
|
|
|
|
## Affected Files
|
|
|
|
- `webui/src/components/SpectateVideo/index.jsx`
|
|
- `webui/src/components/RoverMediaPlayer/index.jsx`
|
|
- `webui/src/hooks/useVideoRequests.js`
|
|
- `webui/src/lib/whepPlayer.js`
|
|
- `webui/src/spectate/SpectatorApp/components/RoverRow.jsx`
|
|
- server-side WHEP/video request handling, if feed quality/rate selection is added
|
|
|
|
## Evidence
|
|
|
|
Artifact:
|
|
|
|
```sh
|
|
perf/results/2026-06-12T04-55-16-888Z-root-runtime/root-runtime-report.json
|
|
```
|
|
|
|
30s sample:
|
|
|
|
```txt
|
|
RTCPeerConnections: 6
|
|
JSEventListeners: 379
|
|
```
|
|
|
|
The listener list is dominated by normal media listeners:
|
|
|
|
```txt
|
|
audio canplay/ended/error/pause/play/stalled/waiting: 9 each
|
|
video media events: 3 each
|
|
```
|
|
|
|
That pattern fits several rover media players, likely with video and dedicated audio
|
|
connections.
|
|
|
|
The same audit also saw:
|
|
|
|
```txt
|
|
TaskDuration: 28.48s
|
|
ScriptDuration: 12.11s
|
|
Long tasks: 81
|
|
```
|
|
|
|
The earlier 8x throttled `/spectate` profile saw:
|
|
|
|
```txt
|
|
Task delta: 49.13s
|
|
Script delta: 19.58s
|
|
```
|
|
|
|
## Current Code Path
|
|
|
|
Each rover card mounts:
|
|
|
|
```txt
|
|
webui/src/components/SpectateVideo/index.jsx:17
|
|
<RoverMediaPlayer roverId={roverId} />
|
|
```
|
|
|
|
`RoverMediaPlayer` automatically creates video and audio request entries:
|
|
|
|
```txt
|
|
webui/src/components/RoverMediaPlayer/index.jsx
|
|
autoEntries = [
|
|
video rover entry,
|
|
optional dedicated audio entry,
|
|
]
|
|
```
|
|
|
|
It then requests sources through `useVideoRequests`, starts WHEP players, maintains
|
|
restart timers, maintains unmute/audio retry timers, and optionally falls back to
|
|
snapshots when no WHEP URL exists.
|
|
|
|
## Why This Matters
|
|
|
|
Six peer connections on a powerful desktop may feel fine. On weak laptops and mobile
|
|
devices, simultaneous WebRTC decode plus React UI churn can easily saturate the main
|
|
thread or media threads. The visible symptom is not necessarily one obvious function in
|
|
the CPU profile; it is degraded frame cadence, long tasks, and high browser-level media
|
|
work.
|
|
|
|
## Fix Strategies
|
|
|
|
### Option A: Cap Active Live Feeds On Mobile
|
|
|
|
For mobile/low-end layouts:
|
|
|
|
- one primary live WHEP feed
|
|
- secondary rover cards use snapshots or lower-rate previews
|
|
- promote a secondary rover to live only when selected/visible/focused
|
|
|
|
This is the highest-impact spectator-specific design change.
|
|
|
|
### Option B: Pause Offscreen Feeds
|
|
|
|
Use `IntersectionObserver` around each `SpectateVideo` or `RoverSpectatorCard`:
|
|
|
|
- start WHEP when the card is visible
|
|
- stop WHEP when the card is far offscreen
|
|
- keep a small grace period to avoid flapping while scrolling
|
|
|
|
This matters especially because portrait `/spectate` can scroll.
|
|
|
|
### Option C: Disable Dedicated Audio For Non-Focused Rovers
|
|
|
|
Dedicated audio is useful, but not necessarily for every rover at the same time.
|
|
|
|
Possible policy:
|
|
|
|
- audio enabled only for the selected/primary rover
|
|
- audio disabled for muted secondary rovers
|
|
- audio WHEP starts only after user interaction
|
|
|
|
This should reduce peer connections and audio retry timers.
|
|
|
|
### Option D: Request Lower Quality For Spectator Secondary Feeds
|
|
|
|
If the server/media stack supports it, add spectator quality tiers:
|
|
|
|
```txt
|
|
primary: normal FPS/resolution
|
|
secondary: low FPS/resolution or snapshot-only
|
|
mobile: lower default bitrate/resolution
|
|
```
|
|
|
|
### Option E: Stop Retry Loops When Hidden
|
|
|
|
When document visibility is hidden or a card is offscreen:
|
|
|
|
- stop restart timers
|
|
- stop audio retry intervals
|
|
- avoid requesting fresh WHEP URLs
|
|
|
|
## Recommended Path
|
|
|
|
After the HUD/telemetry issue, add a spectator feed activity policy:
|
|
|
|
1. Define "primary" versus "secondary" rover cards.
|
|
2. Keep only primary feeds fully live on mobile.
|
|
3. Use `IntersectionObserver` to avoid live playback for offscreen cards.
|
|
4. Disable secondary audio by default.
|
|
|
|
## Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
CPU_THROTTLE=6 VIEWPORT=390x844 MOBILE=1 SAMPLE_MS=30000 \
|
|
node perf/live-root-runtime-audit.mjs https://rover.otter.land/spectate perf/results
|
|
```
|
|
|
|
Expected improvements:
|
|
|
|
- `RTCPeerConnections` should drop below 6 on mobile.
|
|
- Media listener counts should drop.
|
|
- `TaskDuration` should drop.
|
|
- Frame p95/p99 should improve.
|
|
- User-visible video should still start reliably for the primary rover.
|
|
|
|
## Risks
|
|
|
|
- Spectators may expect all feeds live all the time on desktop.
|
|
- Switching feeds can introduce startup delay if WHEP is torn down too aggressively.
|
|
- Audio policy needs to avoid surprising users. Make the focused rover's audio behavior
|
|
clear and consistent.
|
|
|