Building a Multi-Camera Dashboard: Mount, Cap, Destroy
Description and use
Building a multi-camera dashboard with capped live streams, intersection-based activation, and robust debugging for stable web app performance.
Problem
Managing multiple live IP camera streams on a web page without leaking players or exceeding decoding capacity.
Outcome
Implemented a capped, intersection-driven camera wall that prevents player leaks, limits concurrent decoders, and simplifies debugging, leading to stable performance on laptops and clear debugging steps.
Workflow
- 1. Implement Catalog API to return camera metadata without stream URLs.
- 2. Create Slot component that imports player SDK only when needed and tracks state (empty, starting, playing, stalled, dead).
- 3. Add Viewport controller using IntersectionObserver and a numeric live cap to start slots only when visible and under cap.
- 4. Implement Inspect promotion to focus a tile and evict or downgrade others without starting hidden live streams.
- 5. Add Teardown hooks (onUnmount, route leave, visibilitychange) with idempotent destroy() and cancel flag to avoid mounting on dead nodes.
- 6. Debug by counting SDK player instances, media requests, status polls, and ensuring React keys use cameraId, not index.
- 7. Enforce canLive flag before mounting to avoid frontend bugs.
- 8. Avoid global singleton players; each slot owns its instance.
- 9. Use config.liveCap (default 4) as hardcoded cap; rely on capability flags for other features.
- 10. Run full debug session: log cameraIds, React keys, freeze status polls, test with LIVE_CAP=1, open single camera route.
- 11. Adopt route structure: /sites/:siteId table, /sites/:siteId/cameras/:id single slot, /sites/:siteId/wall viewport + cap.
Evidence
When the page is “janky,” I count: How many player instances the SDK reports. How many media requests are in flight. Whether a 3-second status poll remounts slots (React key mistakes).
I do not want the slot to know how HLS versus FLV was obtained. Imou Open Platform is a fine backend behind bff.play
A layout I will merge: /sites/:siteId table only /sites/:siteId/cameras/:id one PlayerSlot /sites/:siteId/wall viewport + cap (flagged)
- Record date: 2026-09-15T07:47:02.716Z