From-Scratch Build · Live Video
A web app that finds every live video stream on the local network and lets you send any one of them to any screen — or several screens at once — with a click. Built from scratch to learn how professional video-over-IP and remote control fit together.
What it is
NDI is a protocol that carries broadcast-quality video over an ordinary network — cameras, capture cards and software all announce themselves as named "sources" that any device can pick up. The problem this build solves is the boring but essential one: which source goes to which screen?
The app continuously discovers the NDI sources on the network and shows them in a simple web page alongside the list of display devices. You pick a stream, tick the screens you want it on, and hit route. Behind the scenes the server logs into each chosen display over SSH and tells it to start playing that exact stream — turning a rack of independent machines into one controllable video wall.
The core idea I wanted to learn: a router doesn't have to move the video itself. NDI streams travel device-to-device on their own; this service just needs to tell each display which source to grab. Coordination, not plumbing, is the whole job.
The stack
The point of this rebuild was the toolchain. Each layer was new to me; here is what each one actually does in the system.
Serves both the control page and a small REST API. Async by design, so discovering sources and dispatching commands don't block each other.
A Python binding to the NDI library that enumerates live sources on the network in real time — the list that populates the UI.
For each chosen display the server opens an SSH session and runs a launcher script. No agent to install — just a key and a shell.
A tiny script living on each display that, given a stream name, kills the old player and opens the new stream full-screen.
One flat file lists every output display by name, host and user. Stateless and trivial to edit — no database needed.
A minimal page that fetches sources and devices, lets you select, and posts the routing request — the visible control surface.
How a route happens
Routing one source to several screens is a short, deliberate sequence — and a lot of it happens in parallel:
The finder scans the network and returns every live NDI stream by name. The UI lists them.
You choose one stream and tick one or more displays from the device list, then submit.
The API opens an SSH connection to each selected display — concurrently, so ten screens switch as fast as one.
On each display the launcher script stops any current playback and opens the new stream full-screen.
Every targeted screen is now showing the chosen source — the NDI data flows directly, peer to peer.
Built-in authentication so the router can run on an open network. Left as future work; for now it sits behind a trusted LAN.
The REST API
The whole service reduces to three plain HTTP calls — easy to script, easy to reason about:
GET /api/ndi-sources — returns the live video streams currently visible on the network.GET /api/output-devices — returns the configured displays the router can send to.POST /api/route — takes a stream name and a set of target hosts, and makes it so.Because routing is just a POST, the same control surface could be driven by a button, a schedule or another program — the web UI is only the first client.
Reflection