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_DETECTIONSfield - Connecting to the live LAN H.264 WebSocket and reading both binary frames and the text init message
- Drawing detection boxes on top of
RhombusRealtimePlayerusing 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
CallPOST /api/camera/getMediaUris and read:
lanLiveH264Uris(array of strings) — LAN URLs, when the client and camera share a networkwanLiveH264Uri(string) — WAN URL, routed through Rhombus
/ws for /wsl in the path.
Authenticate
Both modes use a federated session token minted on your backend viaPOST /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:Encapsulation header (TLV format)
Each binary message contains a sequence of TLVs. Every TLV uses the same wire format:TLV types
Wire layout
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 type0x00 or 0x01 (the frame-data entry):
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: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_DETECTIONSTLV. - Detections carry no per-detection timestamp. Align each detection set on the enclosing frame’s
TIMESTAMPTLV (type0x02) — 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’sTIMESTAMPis 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.
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 HTTPvideo/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’sTIMESTAMP 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.