Real-time pose streaming (WebSocket — on-demand)
Dedicated WebSocket endpoint for 60fps skeleton streaming.
The WebSocket server is NOT started at boot. Call POST /v1/pose/stream/start first — the response includes the actual port and endpoint URL. The server prefers port 10005 but uses an OS-assigned port if 10005 is occupied.
Endpoint: ws://127.0.0.1:<port> (separate port, not on 9998) Auth: Bearer token via the Sec-WebSocket-Protocol upgrade header. Open with new WebSocket(url, ['bearer.toolpiper.v1', 'bearer.<token>', 'corr.<uuid>']) — see docs/archive/local-http-auth-bearer-only-paths.md § Phase 2. Max concurrent: 4 streams
Lifecycle
- POST /v1/pose/stream/start — starts the WebSocket server, returns { port, endpoint }
- Connect to the returned endpoint URL
- GET /v1/pose/stream/status — check running state and connection count
- POST /v1/pose/stream/stop — stops the server (or let it run)
Client → Server
| Message | WS Type | Description |
|---|---|---|
| {"type":"config", ...} | text | Configure session (optional — defaults apply) |
| Raw JPEG/PNG bytes | binary | One frame per message |
| {"type":"record","action":"start"} | text | Start recording to ring buffer |
| {"type":"record","action":"stop"} | text | Stop recording |
| {"type":"eos"} | text | End session |
Config message (all fields optional)
{
"type": "config",
"backend": "apple-vision",
"topology": "openpose-18",
"format": "compact-binary",
"include_hands": false,
"include_face": false,
"detect_3d": false,
"confidence_threshold": 0.3,
"canvas_width": 1024,
"canvas_height": 1024,
"renderer": "openpose",
"stats_interval_ms": 1000
}
Server → Client
| Message | WS Type | Description |
|---|---|---|
| {"type":"config_ack", "joint_order":[...]} | text | Resolved config + joint order for binary decoding |
| Pose data | binary or text | Format depends on config (see Output Formats) |
| {"type":"stats", ...} | text | Periodic fps, drop rate, avg detection time |
| {"type":"record_ack", ...} | text | Recording start/stop confirmation |
| {"type":"error","message":"..."} | text | Error |
Output Formats
compact-binary (default) — Binary WS message. Pre-allocated buffer, zero heap alloc per frame. 236 bytes for 1 person / OpenPose-18 / 2D:
- Header (16B): magic 0x504F5345, version, flags, num_people, num_joints, detection_ms, frame_seq
- Per-person: optional bbox (4×f32), confidence (f32), per-joint x/y/confidence (3×f32 each, +z if 3D)
compact-json — Text WS message. Short keys, flat arrays, pixel-space coords. {"f":42,"ms":8.3,"n":1,"p":[[512.0,307.2,0.95,...]]}
verbose-json — Text WS message. Named joints, full metadata. {"type":"pose","frame_seq":42,"detection_ms":8.3,"people":[{"keypoints":{"nose":{"x":0.5,"y":0.3,"c":0.95},...}}]}
image — Binary WS message (PNG). Rendered skeleton via selected renderer. Cannot sustain 60fps.
Request
- Base URL: http://localhost:9998
- URL: http://localhost:9998/v1/pose/stream
- Auth: HTTP bearer
Changes
No recorded changes to this operation across all 1 revision of this API.