Grid tile limits
NovaVMS renders live video in a fixed grid. The cloud backend accepts six layouts; the browser is responsible for decoding each tile.
Grid layouts
Source: cloud/internal/grids/models.go (ValidLayouts, MaxCamerasPerPage). The frontend type system in frontend/src/lib/types.ts and the page-level layout list in LivePage.tsx currently advertise extra layouts (e.g. 4x4) that the backend rejects with a 400 — saving such a grid fails server-side. Stick to the six layouts below.
| Layout | Columns x rows | Tiles per page |
|---|---|---|
1x1 | 1 x 1 | 1 |
2x2 | 2 x 2 | 4 |
2x3 | 2 x 3 | 6 |
2x4 | 2 x 4 | 8 |
3x3 | 3 x 3 | 9 |
3x4 | 3 x 4 | 12 |
A saved grid may contain multiple pages; each page holds up to 12 tiles (the backend hard cap MaxCamerasPerPage = 12). Pages are paginated — only the active page decodes video.
Per-browser decoder limits
The cloud backend enforces concurrent-viewer limits in three tiers (cloud/internal/sfu/viewer_limiter.go):
| Tier | Default | Env var |
|---|---|---|
| Per camera | 10 (env) / 5 (code default) | MAX_VIEWERS_PER_CAMERA |
| Per organisation | 20 | (not exposed via env yet) |
| Global | 100 | (not exposed via env yet) |
The browser additionally caps simultaneous hardware-accelerated video decoders.
| Browser | WebRTC PeerConnections tested | MSE fallback tested | Notes |
|---|---|---|---|
| Chrome (desktop) | 12 | 12 | Backend hard cap caps a single page at 12 tiles. |
| Edge (desktop) | 12 | 12 | Same engine as Chrome. |
| Firefox (desktop) | 12 | 12 | MSE used for H.265. |
| Safari (macOS) | 12 | 12 | MSE fallback path not exercised as heavily in TTFF testing. |
| Safari (iOS) | 4 | 4 | iOS throttles concurrent video decoders. |
| Chrome (Android) | 6 | 6 | Varies by device hardware. |
Per-device observed limits depend on CPU and GPU; the backend per-camera and per-org viewer caps apply in addition.
Degradation behaviour
| Condition | Behaviour |
|---|---|
| Browser cannot decode the camera codec in WebRTC | Transport falls back to MSE/fMP4. See Live view transport. |
| Decoder count exceeded on the device | New tiles stay in the “Waiting for video” state until an existing tile is freed. |
| PeerConnection fails to establish after 5 s | Tile shows a reconnect indicator; retries in the background. |
| Per-camera viewer cap hit | New viewer receives a “Camera busy” rejection (ErrCameraBusy). |
| Per-org or global viewer cap hit | New viewer receives an “Organisation busy” or “Server busy” rejection. |
| Gateway offline | Tile renders the last thumbnail with an Offline badge. |
Related
- Live view transport — WebRTC-first, MSE-fallback model
- Supported browsers — codec compatibility matrix
- Live view does not show video — symptom-level fix