Case · onboard systems

Live video from vessels that keep losing the link

An industrial control company needed to watch, from shore, the cameras on fishing vessels working hundreds of kilometres off the coast. The problem was not the video: it was the link, which drops several times a day. This is the path the signal takes and why it is built this way.

Why this case has no screenshots

The system lives in an industrial PC on board, in a satellite link and in a server on shore. A screenshot of the operator's screen would show a video player, which is exactly the easy part. What is really worth showing is the path the signal takes: what talks to what, over which protocol, and what happens when the boat loses its link.

The diagram draws itself, in six chapters. Watch the moment the link line breaks and turns red: that happens for real, and it explains almost every technical decision in the project.

ON BOARD · industrial PC THE LINK · satellite, 4G ON SHORE · server IP camera Dahua · Hikvision Agent Python · Go native SDK SQLite local queue Link loss and latency SLATE listens for SRT transcodes Mosquitto MQTT broker Core hexagonal API FastAPI PostgreSQL fleet and cameras Browser React plays HLS RTSP ONVIF SRT HLS MQTT

Swipe the diagram sideways to see all of it.

  1. 1 · The camera speaks more than one language

    The cameras on board give video over RTSP, but snapshots and events need the vendor's native SDK, which is a C library. The agent calls it from Python with ctypes. ONVIF is there to discover them and configure them.

  2. 2 · The agent decides what goes out, and when

    A process on the vessel's industrial PC, written in Python and with a version in Go. It keeps state in a local SQLite database because the boat spends hours without a link: whatever could not be sent waits there instead of being lost.

  3. 3 · The real problem: the link drops

    Between the boat and the coast there is satellite or intermittent 4G, with packet loss and high latency. A protocol on top of TCP stalls there. That is why the video goes out over SRT, which runs on UDP and recovers what was lost without blocking the rest. The agent pushes it with FFmpeg in sender mode.

  4. 4 · On shore, translate it for the browser

    The SLATE server listens in receiver mode, picks up the stream and transcodes it to HLS: segments and a playlist that any browser understands without plugins. The snapshots are stored there too.

  5. 5 · Control travels by another route

    Commands and telemetry do not travel with the video: they use MQTT over Mosquitto, with topics per device and per camera. It is light and it survives disconnections. The core is an API with a hexagonal architecture, with a domain that does not import infrastructure at all, and PostgreSQL for the fleet.

  6. 6 · And only then, the screen

    The operator opens the browser and sees the camera of a boat that is hundreds of kilometres away. Everything before it exists so that last step looks trivial.

The four decisions that hold it up

A whole fleet does not fit on one server

Video does not transcode itself: each server on shore handles a limited number of recordings at a time, and past that number it runs out of hardware. So growth is not buying a bigger server, it is adding one slave server per batch of recordings, each with its own group of vessels assigned to it.

That is how the system goes from watching a few boats to watching the whole fleet: adding boats means adding servers, with the same core, the same broker and the same screen for the operator. Everything else along the path — the agent on board, SRT, the local queue, MQTT — is already built for that: each boat talks to its own server, not to all of them.

Where each piece of the diagram comes from

The path is not an idealisation: every element in the drawing is taken from the code of the system, not from how it ought to be.

RTSP and ONVIF to the camerasaddresses and client in the device agent
Native SDK through ctypesthe C headers of the vendor's library
Local queue in SQLitethe agent's persistence layer
SRT output with FFmpegsrt://host:port?mode=caller in the agent
Listening on shoresrt://0.0.0.0:port?mode=listener in the video server
Transcoding to HLShls_time, hls_list_size and hls_flags parameters
MQTT topicsstreaming/device/{id}/camera/{uuid}/…
Hexagonal APIthe domain, application and infrastructure folders

Do you have a system that lives far from the keyboard?

Devices in the field, links that drop, data that cannot be lost and someone who needs to see it on a screen. It is the work I like most.

Let's talk →