A live camera wall for a TV. Plug a small computer into a screen — a Raspberry Pi, a mini PC, anything that runs Docker — and it shows your cameras, all the time, without putting extra load on the recorder. Reolink natively, ONVIF for most other brands, plain RTSP for anything else.
An NVR will serve a few streams and then start dropping them. A Reolink allows two full-resolution viewers and ten low-resolution viewers per camera — and the owner's phone app needs one of them. A wall that naively opens sixteen full-resolution streams does not just run slowly: it breaks recording and locks you out of your own cameras.
So the agent decides, centrally, how few streams can be open and which tile gets which. Tiles use the low-resolution stream unless they are big on screen; one full-resolution stream plays at a time by default, leaving the phone app its slot; streams are held briefly rather than churned; and a screen physically cannot ask for more, because video is relayed by the agent, which refuses any stream it has not assigned.
cp .env.dev.example .env
docker compose up -d --build| URL | What |
|---|---|
| http://localhost:8080 | The camera wall |
| http://localhost:8080/?hud=1 | …with the status panel (or press i) |
| http://localhost:8080/admin | The settings |
| http://localhost:8081 | The built-in mock NVR |
Four test-pattern cameras appear within a few seconds, using the same RTSP paths, HTTP-FLV URLs and API as a real Reolink NVR — so you can see the whole thing working before you touch your own cameras. When you are ready for those, see installation, and read security before putting it on a network.
| Input | Action |
|---|---|
Click a tile, Enter, 1–9 |
Full screen — that tile switches to the full-resolution stream |
Click again, Esc |
Back to the grid |
← → |
Select a tile; moves between cameras while full screen |
[ ], PgUp PgDn |
Previous / next view |
i |
Status panel: stream, resolution, frames, dropped, delay, budget |
Views are sets of cameras in a layout: 13 built-in layouts from 1x1 to 5x5, feature layouts
like 1+5, and your own built by dragging cells together. auto follows the camera count, so
three cameras give 1+2 rather than 1+5 with three empty tiles.
A camera that sees a person, vehicle, animal or motion comes forward on its own — full screen, moved into the big tile, or simply highlighted, as in the picture above. It reacts to something arriving rather than to a car that has been parked on the drive since this morning. Touching the wall always wins: detection pauses while someone is using it.
Everything is configured in the browser, on a phone as comfortably as on a laptop. Nothing needs a text editor, and no setting requires a restart.
Each camera gets a name for the wall, a switch to keep it off the wall entirely, a video quality of its own — always HD, always SD, or automatic — and how its picture fills the tile. Underneath, what the NVR actually reports: resolution, codec, bitrate and keyframe interval, with advice when a slow keyframe interval is what makes switching to HD feel sluggish.
The other sections are General (what this device is, how it reaches the network, and how to restart or reset it), Sources (add or remove an NVR while it runs), Views, Layouts (a drag-to-merge grid editor), Display, Detection, This device (the stream budget) and Security.
The General section is what the device says about itself — version, uptime, temperature, disk, which network it is on and at what address — and what can be done to it: join a wifi network, prefer ethernet, restart, shut down, or start again from scratch. Reading it needs no password; changing anything does.
The first time you open it, a short guide walks you through protecting the settings, connecting your cameras, naming them and choosing what the wall shows.
![]() |
![]() |
Settings are shown but locked until you unlock them with the admin password, so the screen in the hallway cannot be reconfigured by anyone who walks past it.
Plugged in somewhere new, with no cable and no wifi it knows, there is no address for anyone to open — so the television becomes the way in. The device puts up a wifi network of its own and shows how to join it, with a code to scan for the network and another for the settings page.
Tell it about your wifi and its own network disappears, the screen goes back to the cameras,
and the settings move to http://<the device>.local:8080/admin.
git clone <this repository> ~/openviewport && cd ~/openviewport
sudo deploy/pi/install.shOne command sets up the whole appliance: the stack and a full-screen browser as services that
start at boot, a token so only the screen itself can watch, and the settings reachable from
your phone at http://<the pi>.local:8080/admin. See Raspberry Pi.
flowchart LR
NVR[NVR or cameras<br/>Reolink · ONVIF · RTSP] -- "API: one session, batched" --> AG
NVR -- "RTSP / HTTP-FLV<br/>one connection per stream" --> G2R
subgraph OpenViewport device
AG[agent<br/>discovery, layouts,<br/>stream budget, detection]
G2R[go2rtc<br/>stream relay<br/>loopback only]
R[renderer<br/>the browser wall]
end
AG -- "register streams at runtime" --> G2R
AG -- "wall state, WebSocket" --> R
R -- "commands and health" --> AG
G2R -- "video" --> AG
AG -- "video, authenticated,<br/>assigned streams only" --> R
The agent decides everything; the renderer draws what it is told and reports what it sees. Read architecture for the reasoning, or API to write a renderer of your own.
Security was a requirement rather than a later pass: go2rtc is never published, because its API hands out camera passwords; credentials never reach logs, API responses, the settings file or the screen; the pages run under a strict content security policy and refuse cross-site requests and DNS rebinding; every container is read-only and without capabilities; the wall is on loopback until you publish it deliberately. Security has the details.
- A camera wall that protects the recorder — 13 built-in layouts and your own, several views, instant full screen, and a stream budget that keeps one full-resolution stream at a time so the vendor's phone app still works.
- Reolink, ONVIF and plain RTSP, added and removed while it runs.
- Every camera set up on its own — name, hidden or not, always HD, always SD or automatic, how the picture fills its tile, and what its streams actually are.
- A camera comes forward when something happens — person, vehicle, animal or motion, full screen or promoted or simply marked, and only for things that arrive.
- An admin page that works on a phone, locked until you unlock it, with a setup guide on first use.
- Device control — network, restart, shut down and reset, from the same page.
- Onboarding with no network at all, over the device's own wifi and a code on the TV.
- A Raspberry Pi appliance — one installer, services at boot, a browser on the TV.
- Security as a requirement — a token to watch, a password to change, credentials kept out of logs and responses, hardened containers, and root confined to one small service.
- Tested — 607 unit tests and 37 browser tests that play real video, run on every change.
- Updating from the settings page, with an automatic rollback if the new version does not come up.
- A display schedule — the TV off at night and on in the morning, over HDMI-CEC.
- Raspberry Pi measurements, and a native renderer if the browser proves to be the limit.
- A ready-made Pi image, so the first boot is the setup screen.
- Push events from Reolink instead of polling for detections.
- More vendors, driven by what people report their hardware doing.
| Installation | Docker, your NVR, putting it on a screen |
| Raspberry Pi | The appliance: one installer, services at boot, a kiosk on the TV |
| Configuration | Every setting |
| Supported hardware | What works, which adapter, tested devices |
| Architecture | How and why |
| API | HTTP and WebSocket reference |
| Security | A safe installation, the admin lock, what is defended |
| Credentials | How passwords are stored, and the limits of that |
| Troubleshooting | When something is black or broken |
| Adding a vendor | Writing a source adapter |
| Building | Working on the code |
Hardware reports are especially welcome: the model, the firmware, and what it did. Everything is tested in containers, so nothing needs installing on your machine:
docker compose --profile test run --rm unit
docker compose --profile test run --rm lint
sh scripts/e2e.shContributing and building cover the code; SECURITY.md is how to report something that should not be public.
GNU AGPL-3.0. Use it, change it, run it at home freely. If you modify it and let other people use it — including over a network — those people are entitled to your changes under the same licence.
Not affiliated with, endorsed by or connected to Ubiquiti, Reolink, or any other manufacturer. Their product names appear here only to say which devices this software talks to.






