← all builds

From-Scratch Build · Live Video

NDI Video Router

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.

PythonFastAPINDI SSHREST API

What it is

A patch bay for network video

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

Tools under the hood

The point of this rebuild was the toolchain. Each layer was new to me; here is what each one actually does in the system.

web framework

FastAPI

Serves both the control page and a small REST API. Async by design, so discovering sources and dispatching commands don't block each other.

discovery

NDI finder

A Python binding to the NDI library that enumerates live sources on the network in real time — the list that populates the UI.

remote control

SSH (Paramiko)

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.

playback

Display launcher

A tiny script living on each display that, given a stream name, kills the old player and opens the new stream full-screen.

config

Device JSON

One flat file lists every output display by name, host and user. Stateless and trivial to edit — no database needed.

frontend

Web UI

A minimal page that fetches sources and devices, lets you select, and posts the routing request — the visible control surface.

How a route happens

From a click to a screen lighting up

Routing one source to several screens is a short, deliberate sequence — and a lot of it happens in parallel:

  1. Discover sources live

    The finder scans the network and returns every live NDI stream by name. The UI lists them.

  2. Pick source & targets live

    You choose one stream and tick one or more displays from the device list, then submit.

  3. Server dispatches live

    The API opens an SSH connection to each selected display — concurrently, so ten screens switch as fast as one.

  4. Launcher runs live

    On each display the launcher script stops any current playback and opens the new stream full-screen.

  5. Wall updates live

    Every targeted screen is now showing the chosen source — the NDI data flows directly, peer to peer.

  6. Access control future

    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

Three endpoints do everything

The whole service reduces to three plain HTTP calls — easy to script, easy to reason about:

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

What building it taught me