Skip to Content
This documentation is provided with the HEAT environment and is relevant for this HEAT instance only.
RunnersVideo UtilsVideo Utils Runner

Video Utils Runner

The Video Utils runner (video-utils) turns the H.264 video that Capture streams inside its protobuf payloads into something the HEAT dashboard can play back in sync with the session clock. Its main template, video-atlas-encode, composites every admitted headset stream into one atlas (an N-up grid) and publishes it as a ladder of Media Source Extensions (MSE) fragments plus a manifest.

Read this before adding video to a session template: the output is not a downloadable video file, and the node has to sit at a specific place in the DAG.

What the output is, and is not

  • The atlas is an MSE fragment ladder: one init segment plus one fragmented-MP4 media segment per time window (8 seconds by default), described by a JSON manifest. The dashboard’s VideoPlayback component fetches fragments on demand and feeds them to a MediaSource buffer, seeks by window, and follows the realm clock.
  • There is no single MP4 to download and play in a desktop player. Individual fragments are not self-contained files, the manifest is the only thing that gives them meaning, and the windows can be rewritten in place as data arrives. If a downloadable recording is a requirement, that is a separate export feature, not this node.
  • Playback is progressive and contiguous by design: every window between the first and last carries media, and a seat with no data in a window is rendered black rather than leaving a hole, because a hole in an MSE buffer stalls the browser rather than showing “no video”.

Node template selection

TemplatePurposeLimitationsDetails
video-atlas-encodeDecode admitted StreamComponent H.264 streams straight from the capture protobuf blobs, composite them into one atlas, and publish an MSE fragment ladder plus manifest for the VideoPlayback dashboard component.Reads the capture input node directly (not hydrated CSV or JSON); single parent; CPU bound (decode dominates); requires retention.skipArchive; output is for the HEAT dashboard only.video-atlas-encode
adaptive-video-encodeCatalogue the StreamComponent video streams present in the capture (names, codec, dimensions, chunk counts) and push the catalogue onto a child input node.Catalogue only: the HLS and DASH ladder it was designed for was never built, so it produces no playable video. Use it to see what video a capture holds before configuring the atlas.adaptive-video-encode

Which node

Expected topology

The atlas node must be a direct child of the capture input node that receives the protobuf uploads. It decodes the StreamComponent access units itself. Do not hang it off hydrate-protobuf or any tabular node: those strip the video, and an intermediate “extracted video” node would only duplicate tens of gigabytes.

Three publishing shapes are in use:

  • Standalone dimension: an Arbex script reads the manifest (the atlas node’s current output), republishes it as one value channel, and builds a single-row layout with a VideoPlayback column. Good for a smoke test and for a dedicated video view.

  • Inside an existing dashboard: an Arbex script wraps the manifest under a key (for example output.videoAtlas) so a json-merge can carry it beside the other producers, and the dashboard script publishes the value channel and mounts the column. The manifest must pass through unchanged: each window’s inputHash is the token the player uses to notice a rewritten window.

  • Inside a ComposableChart: the video_playback slice renders the seat tiles in the chart stack, follows the chart realm clock, and is bounded to that clock (a vignette realm plays only its vignette), pairing with the playback scrubber and events_lane slices. See the ComposableChart slice reference.

The channel id and name must be identical, because the components resolve the channel by both.

From capture to pixels

Each seat is a tile in the atlas. The player draws each tile into its own canvas region, so one hardware decoder serves every seat at once.

Memory and CPU

  • Decode is the dominant cost, and it is irreducible: every source frame is a reference frame, so nothing can be skipped. Cost scales linearly with the number of admitted streams. The default admits the left eye only; the right eye is nearly the same view and would double the work.
  • Memory is bounded by the atlas, not by sources times seats. Decoding is tick-synchronous: for each output tick the node decodes only what that tick needs from each stream, scales it into its tile, and releases it. On the full 24th August export this took peak memory from about 3.8 GB to about 250 MB for the same output. The atlas is capped at 1920 by 1088.
  • Each window decodes independently with a short lead-in (default 1 second) so a window’s bytes depend only on the data inside its gather range. That is what makes the result order-independent, at a cost of roughly one eighth extra decode.
  • The runner declares cpuLimit: 4000m and memoryLimit: 4096Mi. The encoder is pinned to four threads (see the node page for why), so four cores is the useful ceiling per task.
  • On the dashboard side the player keeps a bounded number of windows in the MSE buffer and evicts the oldest, so browser memory does not grow with session length.