Skip to content

Build1 publisher3 min readPublished

A four-thread container burns Rive graphics into a live SRT feed with no GPU in the box

VSporte's on-air overlay is now a Rive file plus JSON state, rasterized by Skia on CPU at 1080p and thirty frames a second on eight cores. The clean program feed never enters the container at all.

The Engineer · Build desk

Illustration accompanying A four-thread container burns Rive graphics into a live SRT feed with no GPU in the box

What happened

  • VSporte's on-air graphics moved out of vMix into a web overlay and then to Rive, leaving each graphic as a file with a state machine driven by plain data rather than by developer code.
  • The headless render path went through browser screenshots first, then a WebAssembly renderer on CPU, then a native build, each cheaper and more stable than the one before it.
  • What shipped is one Docker container taking an SRT stream in and pushing an SRT stream out with the graphics burned in, using four threads for state, decode, rasterize and composite.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • capability A graphics renderer that is a container reading a URL for its state can be scheduled on any host with eight spare cores, so a parallel broadcast no longer implies a rented workstation with a GPU in it.
  • constraint Anyone copying this build inherits a renderer backend Rive has already retired on iOS, Android and WASM, and Linux support is what the whole CPU path rests on.
  • decision Teams sizing the same move have to choose on missing fonts and missing automatic layout, not on frame rate, because that is where the native path differs from the browser one they are leaving.

Inside the container, one frame passes through three of the four threads. SrtReceiver decodes the incoming video into raw frames [9]. RiveRenderer runs the state machine and rasterizes the overlay into an RGBA buffer through Skia on CPU [10]. Compositor blends that buffer over the frame with premultiplied alpha, encodes with libx264, and pushes the result back out over SRT [11]. Thirty frames a second leaves 33 milliseconds for that whole chain [22]. Both the rasterizer and the encoder are spending it on the same eight cores [4][6].

The fourth thread is the one that makes the design portable. StateManager holds a websocket subscription to the graphics state, with an HTTPS polling fallback for when the socket will not establish, and it downloads the Rive file and its assets itself [7]. State is plain JSON naming the scene file, the state machine and the view model as key-value pairs [8]. In the Docker image, the most important environment variable is the URL it reads that state from [12].

Skia does the rasterizing, and it is the same 2D engine that draws Chrome's canvas and Android's UI [13]. Rive has its own renderer, and the author chose against it on purpose: that one is designed around the GPU, while the Skia backend was the mature path to a raster surface on CPU [14]. According to the writeup, Rive has retired that backend on iOS, Android and WASM, and it survives on Linux, which is where this container lives [15]. The author puts that on the risk list [15].

Eight cores at 1080p30 is a measurement of VSporte's own artwork. Their on-air graphics are vector shapes, text and simple transitions at 1080p [16], and Skia is built for vector artwork with a bounded number of shapes [13]. "The GPU was never required by the pixels. It was required by the tools that used to draw them," the author wrote [17]. Put full-frame video or a heavy particle effect in the overlay and a bounded shape count no longer describes the workload. The native path also gives up fonts and automatic layout [5], and the author says what an engine gives up decides the choice more often than raw throughput does [23].

The article is headlined "Rendering live broadcast graphics without a GPU, and what four hours actually cost", and the text does not state that figure [21]. The text supports something narrower and still useful: a 1080p overlay burned into a live SRT feed by CPU, in an image you can schedule on any host with cheap cores [6].

Routing is what makes the design acceptable to a broadcaster. The clean feed does not go through the container; it runs past, untouched, straight to the mixer, and the container burns graphics into the dirty path [18]. The clean copy is what feeds international partners, regional versions and ad insertion downstream, and the author says none of them were ever going to accept a workflow with one program feed and graphics welded into it [19]. The container sits inline in one path only, the one carrying graphics, so if it dies the mixer still has the clean feed [20].

Two earlier attempts came first: screenshots driven by a headless browser, then a WebAssembly renderer on CPU [3]. The benchmark that established 1080p30 on eight cores was run before the native build was committed to [4].

What to watch

  • Whether Rive retires the Skia backend on Linux, the one platform this container still depends on.
  • Whether the author publishes the four-hour cost figure the article's headline advertises.
  • Whether 1080p30 on eight cores holds under sustained load with libx264 encoding in the same container.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories