Skip to main content

Overview

The LAN agent’s WebSocket video stream embeds AI detection results directly into the H.264 encapsulation header. When the on-camera inference pipeline produces a new detection, it is spliced as a TLV field into the next outgoing video frame on the same WebSocket — no separate detection channel. This guide covers:
  • The TLV encapsulation format and the JSON schema inside the AI_DETECTIONS field
  • Connecting to the live LAN H.264 WebSocket and reading both binary frames and the text init message
  • Drawing detection boxes on top of RhombusRealtimePlayer using a parallel detection-only WebSocket
  • A from-scratch parser reference for non-React consumers
If you only need a player, embed RhombusRealtimePlayer — it handles auth, WebCodecs decoding, and resolution negotiation. This guide is for adding a detection-overlay layer on top, or for clients that don’t use the React SDK.

Connecting to the LAN realtime stream

Get the WebSocket URL

Call POST /api/camera/getMediaUris and read:
  • lanLiveH264Uris (array of strings) — LAN URLs, when the client and camera share a network
  • wanLiveH264Uri (string) — WAN URL, routed through Rhombus
For the lower-resolution variant, swap /ws for /wsl in the path.

Authenticate

Both modes use a federated session token minted on your backend via POST /api/org/generateFederatedSessionToken. Never put your API key in browser code. The full token-minting backend example (Express, FastAPI, Next.js) lives in the React SDK guide — reuse it.

What the server sends

Immediately after the WebSocket upgrade and before any binary frames, the server sends a single text message describing the stream:
Read the dimensions if your renderer needs the source resolution. Bounding boxes are resolution-independent (permyriad units), so most overlays don’t need this. After the init message, every subsequent message is a binary frame containing the TLV-encoded encapsulation header followed by raw H.264 NAL data.

Encapsulation header (TLV format)

Each binary message contains a sequence of TLVs. Every TLV uses the same wire format:

TLV types

Wire layout

The frame-data TLV (0x00 or 0x01) is always the last entry — the LAN agent’s encoder explicitly inserts metadata TLVs ahead of the frame entry. A safe parser stops walking TLVs once it encounters a frame-data type.

Parsing the encapsulation header

Walk TLV fields until you hit type 0x00 or 0x01 (the frame-data entry):
The Rhombus React SDK uses an equivalent parser at parseRhombusH264Binary.ts — the canonical client-side reference.

Detection JSON schema

AI_DETECTIONS carries a JSON array of detection objects. The detection objects carry no timestamp of their own — every detection in a message was analyzed from the same frame, so use the enclosing frame’s TIMESTAMP TLV (type 0x02) as the analysis time.

Required fields

Optional fields

Example

Forward-compatible parsing. Future firmware releases will add LPR text (lp_chars, lp_confidence), pose skeletons (pose_permyriad_points — 38-joint, not the 17-joint COCO set), and re-identification embeddings. Treat all unrecognized fields as optional and ignore unknown keys, so your client keeps working when those fields land.

Drawing bounding boxes on a canvas

Bounding box coordinates are permyriad (0–10000) and resolution-independent. Convert to pixels using the canvas dimensions:
For a 1280×720 canvas and b: [1200, 3400, 4500, 8900], this yields (x=153.6, y=244.8, w=422.4, h=396.0).

Timing behavior

  • Detections are not present on every frame. The AI pipeline analyzes a subset of frames (typically 2–10 fps). Most frames carry no AI_DETECTIONS TLV.
  • Detections carry no per-detection timestamp. Align each detection set on the enclosing frame’s TIMESTAMP TLV (type 0x02) — the server wall-clock millisecond stamp the encoder writes for that frame. Because the on-camera inference pipeline and the encoder run independently, a detection rides whatever frame happens to leave the encoder next and may correspond to a frame captured slightly earlier; the stream does not expose that offset, so the carrier frame’s TIMESTAMP is the anchor to use, especially for VOD or buffered playback.
  • Persist between updates. To keep boxes visible between detection updates, hold the most recent set and keep redrawing it until a newer set arrives or a TTL elapses. A 2-second TTL is a safe default.

Extending RhombusRealtimePlayer with detection rendering

The React SDK’s RhombusRealtimePlayer doesn’t currently surface AI detections to the host application. Until it does, the simplest pattern is to open a second WebSocket to the same URL purely to read AI_DETECTIONS, and draw the result on a <canvas> overlaid on the player.
A parallel WebSocket doubles the egress for that camera. Use it only on the page that needs detections, and close it on unmount.
Resolve detectionWsUrl on your backend the same way the SDK does: call getMediaUris, pick the appropriate wanLiveH264Uri or LAN entry, then append ?x-auth-scheme=federated-token&x-auth-ft=<TOKEN> (both WAN and LAN) before passing the URL to the browser.

From-scratch parser reference

For non-React clients (a vanilla web page, Node, Electron), the same parser drives a minimal overlay. Decoding the H.264 itself requires WebCodecs (browser) or ffmpeg/libav (Node) and is out of scope, but reading detections from the WebSocket needs only the parser above:

HTTP streams vs WebSocket

The HTTP video/h264 stream variant strips the encapsulation header entirely and delivers only raw H.264 NAL data. Detections ride only on the WebSocket transport. Use the WebSocket URLs from getMediaUris (lanLiveH264Uris / wanLiveH264Uri) for any flow that needs detections.

Troubleshooting

Boxes appear in the wrong location Bbox coordinates are permyriad (0–10000), not pixels and not 0–1. Make sure the renderer divides by 10000 before multiplying by the canvas dimensions. Boxes appear to lag the video Detections have no timestamp of their own — align each set on the enclosing frame’s TIMESTAMP TLV (type 0x02). The detection rides whatever frame leaves the encoder next, so it can trail the analyzed frame slightly; the carrier frame’s TIMESTAMP is the only timing anchor the stream provides. Boxes vanish for a few hundred milliseconds, then reappear The AI pipeline produces results at 2–10 fps and detections do not ride every video frame. Persist the most-recent detection set with a TTL (e.g. 2 s) so the overlay stays stable between updates. Receiver only ever gets binary frames; never sees the init message Confirm your WebSocket handler accepts text frames before binary frames. The init is a single text message sent once per connection. LAN auth fails locally LAN auth is appended to the WebSocket URL as query parameters (the same as WAN), so it works from any origin including localhost. If LAN fails, the usual cause is network reachability: the browser must reach the camera’s LAN host directly (routing, firewall, and HTTPS-vs-HTTP mixed-content rules apply). If the LAN host is unreachable, connect via WAN instead, or proxy through your backend.

Next Steps

React SDK

Drop-in RhombusRealtimePlayer and RhombusBufferedPlayer components.

Streaming Video

HLS, shared streams, thumbnails, and frame capture.
Last modified on July 8, 2026