@realnation/builder-shared-sdk 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -3
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/oss.js +1 -1
- package/dist/runtime/capability.d.ts +45 -0
- package/dist/runtime/capability.d.ts.map +1 -0
- package/dist/runtime/capability.js +59 -0
- package/dist/runtime/connection.d.ts +92 -0
- package/dist/runtime/connection.d.ts.map +1 -0
- package/dist/runtime/connection.js +247 -0
- package/dist/runtime/decode.d.ts +40 -0
- package/dist/runtime/decode.d.ts.map +1 -0
- package/dist/runtime/decode.js +106 -0
- package/dist/runtime/facade.d.ts +165 -0
- package/dist/runtime/facade.d.ts.map +1 -0
- package/dist/runtime/facade.js +354 -0
- package/dist/runtime/index.d.ts +42 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +70 -0
- package/dist/runtime/mock.d.ts +58 -0
- package/dist/runtime/mock.d.ts.map +1 -0
- package/dist/runtime/mock.js +276 -0
- package/dist/runtime/protocol.d.ts +231 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +19 -0
- package/dist/runtime/vue/index.d.ts +26 -0
- package/dist/runtime/vue/index.d.ts.map +1 -0
- package/dist/runtime/vue/index.js +47 -0
- package/dist/runtime/vue/player.d.ts +49 -0
- package/dist/runtime/vue/player.d.ts.map +1 -0
- package/dist/runtime/vue/player.js +95 -0
- package/package.json +24 -3
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure decoding helpers. No state, no I/O — all directly unit-testable.
|
|
3
|
+
*/
|
|
4
|
+
import { GRID_H, GRID_W } from './protocol.js';
|
|
5
|
+
function base64ToBytes(b64) {
|
|
6
|
+
// atob in browsers, Buffer under node/vitest.
|
|
7
|
+
const g = globalThis;
|
|
8
|
+
if (typeof g.atob === 'function') {
|
|
9
|
+
const bin = g.atob(b64);
|
|
10
|
+
const out = new Uint8Array(bin.length);
|
|
11
|
+
for (let i = 0; i < bin.length; i++)
|
|
12
|
+
out[i] = bin.charCodeAt(i);
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
if (g.Buffer)
|
|
16
|
+
return new Uint8Array(g.Buffer.from(b64, 'base64'));
|
|
17
|
+
throw new Error('no base64 decoder available');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Unpacks the 1-bit mask.
|
|
21
|
+
*
|
|
22
|
+
* MSB is the leftmost pixel of each byte — the same order the runtime packs
|
|
23
|
+
* (qs_silhouette_v1). Getting this backwards mirrors every row, which looks
|
|
24
|
+
* plausible on a symmetric scene and is why the bit order is asserted in tests.
|
|
25
|
+
*/
|
|
26
|
+
export function decodeSilhouette(msg) {
|
|
27
|
+
const w = msg.w || GRID_W;
|
|
28
|
+
const h = msg.h || GRID_H;
|
|
29
|
+
const bytes = base64ToBytes(msg.mask);
|
|
30
|
+
const cells = new Uint8Array(w * h);
|
|
31
|
+
const expected = Math.ceil((w * h) / 8);
|
|
32
|
+
if (bytes.length < expected) {
|
|
33
|
+
throw new Error(`silhouette mask truncated: got ${bytes.length} bytes, need ${expected}`);
|
|
34
|
+
}
|
|
35
|
+
for (let i = 0; i < w * h; i++) {
|
|
36
|
+
const byte = bytes[i >> 3];
|
|
37
|
+
const bit = 7 - (i & 7);
|
|
38
|
+
cells[i] = (byte >> bit) & 1;
|
|
39
|
+
}
|
|
40
|
+
const frame = {
|
|
41
|
+
w,
|
|
42
|
+
h,
|
|
43
|
+
cells,
|
|
44
|
+
coverage: msg.coverage ?? countSet(cells) / (w * h),
|
|
45
|
+
nearest: msg.nearest,
|
|
46
|
+
farthest: msg.farthest,
|
|
47
|
+
ts: msg.ts,
|
|
48
|
+
at(cx, cy) {
|
|
49
|
+
if (cx < 0 || cy < 0 || cx >= w || cy >= h)
|
|
50
|
+
return false;
|
|
51
|
+
return cells[cy * w + cx] === 1;
|
|
52
|
+
},
|
|
53
|
+
hit(x, y) {
|
|
54
|
+
return frame.at(Math.floor(x * w), Math.floor(y * h));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return frame;
|
|
58
|
+
}
|
|
59
|
+
function countSet(cells) {
|
|
60
|
+
let n = 0;
|
|
61
|
+
for (let i = 0; i < cells.length; i++)
|
|
62
|
+
n += cells[i];
|
|
63
|
+
return n;
|
|
64
|
+
}
|
|
65
|
+
/* --- QSBN binary frame header (design/02 §9) ------------------------------ */
|
|
66
|
+
export const QSBN_HEADER_BYTES = 16;
|
|
67
|
+
const QSBN_MAGIC = 0x5153424e; // "QSBN"
|
|
68
|
+
/**
|
|
69
|
+
* FNV-1a over the id string. Ids longer than 8 bytes are hashed by both sides
|
|
70
|
+
* with this same function, so the header stays fixed-width without giving up
|
|
71
|
+
* the ability to detect a mispaired frame.
|
|
72
|
+
*/
|
|
73
|
+
export function fnv1a64(s) {
|
|
74
|
+
let hash = 0xcbf29ce484222325n;
|
|
75
|
+
const prime = 0x100000001b3n;
|
|
76
|
+
const mask = 0xffffffffffffffffn;
|
|
77
|
+
for (let i = 0; i < s.length; i++) {
|
|
78
|
+
hash = (hash ^ BigInt(s.charCodeAt(i) & 0xff)) & mask;
|
|
79
|
+
hash = (hash * prime) & mask;
|
|
80
|
+
}
|
|
81
|
+
return hash;
|
|
82
|
+
}
|
|
83
|
+
/** The id as it appears in a QSBN header: raw big-endian bytes, or the hash. */
|
|
84
|
+
export function binaryCorrelationId(id) {
|
|
85
|
+
if (id.length > 8)
|
|
86
|
+
return fnv1a64(id);
|
|
87
|
+
let v = 0n;
|
|
88
|
+
for (let i = 0; i < id.length; i++)
|
|
89
|
+
v = (v << 8n) | BigInt(id.charCodeAt(i) & 0xff);
|
|
90
|
+
return v;
|
|
91
|
+
}
|
|
92
|
+
export function parseBinaryFrame(buf) {
|
|
93
|
+
if (buf.byteLength < QSBN_HEADER_BYTES) {
|
|
94
|
+
throw new Error(`binary frame shorter than header: ${buf.byteLength} bytes`);
|
|
95
|
+
}
|
|
96
|
+
const view = new DataView(buf);
|
|
97
|
+
const magic = view.getUint32(0, false);
|
|
98
|
+
if (magic !== QSBN_MAGIC) {
|
|
99
|
+
throw new Error('binary frame missing QSBN magic');
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
version: view.getUint16(4, false),
|
|
103
|
+
id: view.getBigUint64(8, false),
|
|
104
|
+
payload: buf.slice(QSBN_HEADER_BYTES),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface: subscriptions, semantic commands, video.
|
|
3
|
+
*
|
|
4
|
+
* Two things are deliberately absent and must stay absent:
|
|
5
|
+
* - `publish` — a client publishing would break the runtime's control boundary.
|
|
6
|
+
* - a raw `request(name)` — that leaks an internal concept back out and
|
|
7
|
+
* re-enables the polling antipattern the command table exists to prevent.
|
|
8
|
+
* Adding a command means adding a named method here, not opening an escape hatch.
|
|
9
|
+
*/
|
|
10
|
+
import type { Transport, ConnectionState } from './connection.js';
|
|
11
|
+
import type { SilhouetteFrame } from './decode.js';
|
|
12
|
+
import type { BodyUpdate, DeviceConnected, DeviceError, GestureUpdate, PointerUpdate, RequestError, RuntimeConfig } from './protocol.js';
|
|
13
|
+
export type Unsubscribe = () => void;
|
|
14
|
+
export type GestureName = GestureUpdate['gesture'];
|
|
15
|
+
/** Motion (IMU). Carried as a generic event until the schema grows a definition. */
|
|
16
|
+
export interface MotionUpdate {
|
|
17
|
+
type: 'motion:update';
|
|
18
|
+
euler?: {
|
|
19
|
+
yaw: number;
|
|
20
|
+
pitch: number;
|
|
21
|
+
roll: number;
|
|
22
|
+
};
|
|
23
|
+
quat?: {
|
|
24
|
+
w: number;
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
z: number;
|
|
28
|
+
};
|
|
29
|
+
accel?: {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
z: number;
|
|
33
|
+
};
|
|
34
|
+
battery?: {
|
|
35
|
+
voltage?: number;
|
|
36
|
+
percent?: number;
|
|
37
|
+
};
|
|
38
|
+
ts: number;
|
|
39
|
+
}
|
|
40
|
+
export declare class RuntimeCommandError extends Error {
|
|
41
|
+
readonly code: RequestError['code'];
|
|
42
|
+
constructor(err: RequestError);
|
|
43
|
+
}
|
|
44
|
+
export interface SnapshotResult {
|
|
45
|
+
mime: string;
|
|
46
|
+
data: string;
|
|
47
|
+
}
|
|
48
|
+
export interface DeviceInfo {
|
|
49
|
+
id: string;
|
|
50
|
+
plugin: string;
|
|
51
|
+
status: string;
|
|
52
|
+
}
|
|
53
|
+
export interface RuntimeStatus {
|
|
54
|
+
version: string;
|
|
55
|
+
uptimeMs: number;
|
|
56
|
+
clients: number;
|
|
57
|
+
}
|
|
58
|
+
export interface CameraConfig {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
fps: number;
|
|
62
|
+
}
|
|
63
|
+
export interface CapabilityChange {
|
|
64
|
+
capabilities: unknown[];
|
|
65
|
+
lost: unknown[];
|
|
66
|
+
}
|
|
67
|
+
/** Aligns skeleton data to the video's playback clock (design/03 §4.5). */
|
|
68
|
+
export interface VideoAligner {
|
|
69
|
+
current(): BodyUpdate | null;
|
|
70
|
+
setOffset(ms: number): void;
|
|
71
|
+
readonly offsetMs: number;
|
|
72
|
+
dispose(): void;
|
|
73
|
+
}
|
|
74
|
+
interface VideoElementLike {
|
|
75
|
+
currentTime: number;
|
|
76
|
+
}
|
|
77
|
+
export declare class RuntimeFacade {
|
|
78
|
+
#private;
|
|
79
|
+
protected readonly transport: Transport;
|
|
80
|
+
private pending;
|
|
81
|
+
/** Ids whose JSON response said binary:true, in arrival order. */
|
|
82
|
+
private binaryQueue;
|
|
83
|
+
private requestSeq;
|
|
84
|
+
private disposers;
|
|
85
|
+
private readonly bodyCh;
|
|
86
|
+
private readonly gestureCh;
|
|
87
|
+
private readonly gestureByName;
|
|
88
|
+
private readonly pointerCh;
|
|
89
|
+
private readonly silhouetteCh;
|
|
90
|
+
private readonly motionCh;
|
|
91
|
+
private readonly deviceErrCh;
|
|
92
|
+
private readonly deviceConnCh;
|
|
93
|
+
private readonly playerLostCh;
|
|
94
|
+
private readonly capChangeCh;
|
|
95
|
+
private readonly stateCh;
|
|
96
|
+
/** Last `device:connected.config` seen. Read-through only — see effectiveConfig. */
|
|
97
|
+
private lastConfig;
|
|
98
|
+
private trackedPlayers;
|
|
99
|
+
private readonly setTimer;
|
|
100
|
+
private readonly clearTimer;
|
|
101
|
+
constructor(transport: Transport, timers?: {
|
|
102
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
103
|
+
clearTimer?: (h: unknown) => void;
|
|
104
|
+
});
|
|
105
|
+
private dispatch;
|
|
106
|
+
private settle;
|
|
107
|
+
private dispatchBinary;
|
|
108
|
+
readonly camera: {
|
|
109
|
+
snapshot: () => Promise<SnapshotResult>;
|
|
110
|
+
getConfig: () => Promise<CameraConfig>;
|
|
111
|
+
start: () => Promise<void>;
|
|
112
|
+
stop: () => Promise<void>;
|
|
113
|
+
stream: () => Promise<MediaStream>;
|
|
114
|
+
stopStream: () => void;
|
|
115
|
+
};
|
|
116
|
+
readonly device: {
|
|
117
|
+
list: () => Promise<DeviceInfo[]>;
|
|
118
|
+
onError: (fn: (e: DeviceError) => void) => Unsubscribe;
|
|
119
|
+
onConnected: (fn: (e: DeviceConnected) => void) => Unsubscribe;
|
|
120
|
+
onPlayerLost: (fn: () => void) => Unsubscribe;
|
|
121
|
+
};
|
|
122
|
+
status(): Promise<RuntimeStatus>;
|
|
123
|
+
/**
|
|
124
|
+
* Live retuning. Applied by the runtime — the SDK never recomputes Pointer
|
|
125
|
+
* locally, or the browser and runtime would disagree about where the hand is.
|
|
126
|
+
*/
|
|
127
|
+
reconfigure(config: RuntimeConfig): void;
|
|
128
|
+
/** Values the runtime reported as actually in effect, for HUDs. Read-only. */
|
|
129
|
+
get effectiveConfig(): Record<string, unknown>;
|
|
130
|
+
readonly body: {
|
|
131
|
+
onUpdate: (fn: (b: BodyUpdate) => void) => Unsubscribe;
|
|
132
|
+
};
|
|
133
|
+
readonly gesture: {
|
|
134
|
+
onUpdate: (fn: (g: GestureUpdate) => void) => Unsubscribe;
|
|
135
|
+
on: (name: GestureName, fn: (g: GestureUpdate) => void) => Unsubscribe;
|
|
136
|
+
};
|
|
137
|
+
readonly pointer: {
|
|
138
|
+
onUpdate: (fn: (p: PointerUpdate) => void) => Unsubscribe;
|
|
139
|
+
};
|
|
140
|
+
readonly silhouette: {
|
|
141
|
+
onUpdate: (fn: (f: SilhouetteFrame) => void) => Unsubscribe;
|
|
142
|
+
};
|
|
143
|
+
readonly motion: {
|
|
144
|
+
onUpdate: (fn: (m: MotionUpdate) => void) => Unsubscribe;
|
|
145
|
+
};
|
|
146
|
+
onCapabilityChanged(fn: (c: CapabilityChange) => void): Unsubscribe;
|
|
147
|
+
onStateChange(fn: (s: ConnectionState) => void): Unsubscribe;
|
|
148
|
+
private pc;
|
|
149
|
+
/**
|
|
150
|
+
* Establishes the WebRTC stream. The whole signalling exchange is hidden;
|
|
151
|
+
* callers get a MediaStream.
|
|
152
|
+
*/
|
|
153
|
+
private openStream;
|
|
154
|
+
private closeStream;
|
|
155
|
+
/**
|
|
156
|
+
* Delays skeleton delivery to match the video's playback clock.
|
|
157
|
+
*
|
|
158
|
+
* Skeletons arrive one to two frames ahead of the picture, so drawing an
|
|
159
|
+
* overlay straight from body.onUpdate puts the lines in front of the player.
|
|
160
|
+
*/
|
|
161
|
+
alignToVideo(video: VideoElementLike, offsetMs?: number): VideoAligner;
|
|
162
|
+
dispose(): void;
|
|
163
|
+
}
|
|
164
|
+
export {};
|
|
165
|
+
//# sourceMappingURL=facade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../src/runtime/facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { decodeSilhouette, parseBinaryFrame, binaryCorrelationId } from './decode.js';
|
|
2
|
+
export class RuntimeCommandError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(err) {
|
|
5
|
+
super(err.message ?? err.code);
|
|
6
|
+
this.name = 'RuntimeCommandError';
|
|
7
|
+
this.code = err.code;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Per-command timeout. Snapshot is slow enough that the default would trip. */
|
|
11
|
+
const TIMEOUTS = {
|
|
12
|
+
'camera.snapshot': 15000,
|
|
13
|
+
};
|
|
14
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
15
|
+
/** Emitter with unsubscribe. One handler set per channel. */
|
|
16
|
+
class Channel {
|
|
17
|
+
handlers = new Set();
|
|
18
|
+
add(fn) {
|
|
19
|
+
this.handlers.add(fn);
|
|
20
|
+
return () => {
|
|
21
|
+
this.handlers.delete(fn);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
emit(v) {
|
|
25
|
+
for (const fn of this.handlers)
|
|
26
|
+
fn(v);
|
|
27
|
+
}
|
|
28
|
+
get size() {
|
|
29
|
+
return this.handlers.size;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class RuntimeFacade {
|
|
33
|
+
transport;
|
|
34
|
+
pending = new Map();
|
|
35
|
+
/** Ids whose JSON response said binary:true, in arrival order. */
|
|
36
|
+
binaryQueue = [];
|
|
37
|
+
requestSeq = 0;
|
|
38
|
+
disposers = [];
|
|
39
|
+
bodyCh = new Channel();
|
|
40
|
+
gestureCh = new Channel();
|
|
41
|
+
gestureByName = new Map();
|
|
42
|
+
pointerCh = new Channel();
|
|
43
|
+
silhouetteCh = new Channel();
|
|
44
|
+
motionCh = new Channel();
|
|
45
|
+
deviceErrCh = new Channel();
|
|
46
|
+
deviceConnCh = new Channel();
|
|
47
|
+
playerLostCh = new Channel();
|
|
48
|
+
capChangeCh = new Channel();
|
|
49
|
+
stateCh = new Channel();
|
|
50
|
+
/** Last `device:connected.config` seen. Read-through only — see effectiveConfig. */
|
|
51
|
+
lastConfig = {};
|
|
52
|
+
trackedPlayers = new Set();
|
|
53
|
+
setTimer;
|
|
54
|
+
clearTimer;
|
|
55
|
+
constructor(transport, timers) {
|
|
56
|
+
this.transport = transport;
|
|
57
|
+
this.setTimer = timers?.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
58
|
+
this.clearTimer = timers?.clearTimer ?? ((h) => clearTimeout(h));
|
|
59
|
+
this.disposers.push(transport.onMessage((m) => this.dispatch(m)));
|
|
60
|
+
this.disposers.push(transport.onBinary((b) => this.dispatchBinary(b)));
|
|
61
|
+
this.disposers.push(transport.onStateChange((s) => this.stateCh.emit(s)));
|
|
62
|
+
}
|
|
63
|
+
/* --- inbound ----------------------------------------------------------- */
|
|
64
|
+
dispatch(msg) {
|
|
65
|
+
switch (msg.type) {
|
|
66
|
+
case 'body:update': {
|
|
67
|
+
const body = msg;
|
|
68
|
+
this.trackedPlayers.add(body.player);
|
|
69
|
+
this.bodyCh.emit(body);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case 'gesture:update': {
|
|
73
|
+
const g = msg;
|
|
74
|
+
this.gestureCh.emit(g);
|
|
75
|
+
this.gestureByName.get(g.gesture)?.emit(g);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case 'pointer:update':
|
|
79
|
+
this.pointerCh.emit(msg);
|
|
80
|
+
break;
|
|
81
|
+
case 'depth:silhouette':
|
|
82
|
+
this.silhouetteCh.emit(decodeSilhouette(msg));
|
|
83
|
+
break;
|
|
84
|
+
case 'motion:update':
|
|
85
|
+
this.motionCh.emit(msg);
|
|
86
|
+
break;
|
|
87
|
+
case 'device:connected': {
|
|
88
|
+
const dc = msg;
|
|
89
|
+
if (dc.config)
|
|
90
|
+
this.lastConfig = dc.config;
|
|
91
|
+
this.deviceConnCh.emit(dc);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'device:error': {
|
|
95
|
+
const de = msg;
|
|
96
|
+
this.deviceErrCh.emit(de);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case 'player:lost':
|
|
100
|
+
this.trackedPlayers.clear();
|
|
101
|
+
this.playerLostCh.emit();
|
|
102
|
+
break;
|
|
103
|
+
case 'capability:changed':
|
|
104
|
+
this.capChangeCh.emit(msg);
|
|
105
|
+
break;
|
|
106
|
+
case 'response':
|
|
107
|
+
this.settle(msg);
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
break; // unknown types are ignored, never fatal
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
settle(res) {
|
|
114
|
+
const entry = this.pending.get(res.id);
|
|
115
|
+
if (!entry)
|
|
116
|
+
return; // late response after timeout; nothing to settle
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
this.clearTimer(entry.timer);
|
|
119
|
+
this.pending.delete(res.id);
|
|
120
|
+
entry.reject(new RuntimeCommandError(res.error ?? { code: 'INTERNAL' }));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (res.binary) {
|
|
124
|
+
// Hold the promise open: the payload arrives in the next binary frame.
|
|
125
|
+
entry.awaitingBinary = { result: res.result ?? {} };
|
|
126
|
+
this.binaryQueue.push(res.id);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.clearTimer(entry.timer);
|
|
130
|
+
this.pending.delete(res.id);
|
|
131
|
+
entry.resolve(res.result ?? {});
|
|
132
|
+
}
|
|
133
|
+
dispatchBinary(buf) {
|
|
134
|
+
const id = this.binaryQueue.shift();
|
|
135
|
+
if (id === undefined)
|
|
136
|
+
return; // unsolicited frame
|
|
137
|
+
const entry = this.pending.get(id);
|
|
138
|
+
if (!entry?.awaitingBinary)
|
|
139
|
+
return;
|
|
140
|
+
let frame;
|
|
141
|
+
try {
|
|
142
|
+
frame = parseBinaryFrame(buf);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
this.clearTimer(entry.timer);
|
|
146
|
+
this.pending.delete(id);
|
|
147
|
+
entry.reject(err);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// The header carries the id precisely so a reordered frame is detected
|
|
151
|
+
// rather than silently handed to the wrong caller.
|
|
152
|
+
if (frame.id !== binaryCorrelationId(id)) {
|
|
153
|
+
this.clearTimer(entry.timer);
|
|
154
|
+
this.pending.delete(id);
|
|
155
|
+
entry.reject(new Error(`binary frame id mismatch for request ${id}`));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const declared = entry.awaitingBinary.result.bytes;
|
|
159
|
+
if (typeof declared === 'number' && declared !== frame.payload.byteLength) {
|
|
160
|
+
this.clearTimer(entry.timer);
|
|
161
|
+
this.pending.delete(id);
|
|
162
|
+
entry.reject(new Error(`binary payload length ${frame.payload.byteLength} != declared ${declared}`));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.clearTimer(entry.timer);
|
|
166
|
+
this.pending.delete(id);
|
|
167
|
+
entry.resolve({ ...entry.awaitingBinary.result, payload: frame.payload });
|
|
168
|
+
}
|
|
169
|
+
/* --- commands ---------------------------------------------------------- */
|
|
170
|
+
/**
|
|
171
|
+
* A true `#private` method, not a TypeScript `private` one.
|
|
172
|
+
*
|
|
173
|
+
* TS `private` is erased at compile time: the method still sits on the
|
|
174
|
+
* prototype and `runtime['request']('camera.snapshot')` works at runtime. That
|
|
175
|
+
* is exactly the escape hatch P3-4 exists to close, and once someone reaches
|
|
176
|
+
* for it, `setInterval(() => request('body'), 33)` follows. `#` is enforced by
|
|
177
|
+
* the language, so the only way to issue a command is a named method below.
|
|
178
|
+
*/
|
|
179
|
+
#request(name, args = {}) {
|
|
180
|
+
const id = `r-${++this.requestSeq}-${Math.random().toString(36).slice(2, 8)}`;
|
|
181
|
+
const timeoutMs = TIMEOUTS[name] ?? DEFAULT_TIMEOUT_MS;
|
|
182
|
+
return new Promise((resolve, reject) => {
|
|
183
|
+
const timer = this.setTimer(() => {
|
|
184
|
+
this.pending.delete(id);
|
|
185
|
+
const qi = this.binaryQueue.indexOf(id);
|
|
186
|
+
if (qi >= 0)
|
|
187
|
+
this.binaryQueue.splice(qi, 1);
|
|
188
|
+
reject(new RuntimeCommandError({ code: 'TIMEOUT', message: `${name} timed out after ${timeoutMs}ms` }));
|
|
189
|
+
}, timeoutMs);
|
|
190
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
191
|
+
this.transport.send({ type: 'request', id, name, args });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
camera = {
|
|
195
|
+
snapshot: () => this.#request('camera.snapshot').then((r) => r),
|
|
196
|
+
getConfig: () => this.#request('camera.config').then((r) => r),
|
|
197
|
+
start: () => this.#request('camera.start').then(() => undefined),
|
|
198
|
+
stop: () => this.#request('camera.stop').then(() => undefined),
|
|
199
|
+
stream: () => this.openStream(),
|
|
200
|
+
stopStream: () => this.closeStream(),
|
|
201
|
+
};
|
|
202
|
+
device = {
|
|
203
|
+
list: () => this.#request('device.list').then((r) => (r.devices ?? r)),
|
|
204
|
+
onError: (fn) => this.deviceErrCh.add(fn),
|
|
205
|
+
onConnected: (fn) => this.deviceConnCh.add(fn),
|
|
206
|
+
onPlayerLost: (fn) => this.playerLostCh.add(fn),
|
|
207
|
+
};
|
|
208
|
+
status() {
|
|
209
|
+
return this.#request('runtime.status').then((r) => r);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Live retuning. Applied by the runtime — the SDK never recomputes Pointer
|
|
213
|
+
* locally, or the browser and runtime would disagree about where the hand is.
|
|
214
|
+
*/
|
|
215
|
+
reconfigure(config) {
|
|
216
|
+
this.transport.send({ type: 'reconfigure', config });
|
|
217
|
+
}
|
|
218
|
+
/** Values the runtime reported as actually in effect, for HUDs. Read-only. */
|
|
219
|
+
get effectiveConfig() {
|
|
220
|
+
return this.lastConfig;
|
|
221
|
+
}
|
|
222
|
+
/* --- subscriptions ----------------------------------------------------- */
|
|
223
|
+
body = {
|
|
224
|
+
onUpdate: (fn) => this.bodyCh.add(fn),
|
|
225
|
+
};
|
|
226
|
+
gesture = {
|
|
227
|
+
onUpdate: (fn) => this.gestureCh.add(fn),
|
|
228
|
+
on: (name, fn) => {
|
|
229
|
+
let ch = this.gestureByName.get(name);
|
|
230
|
+
if (!ch) {
|
|
231
|
+
ch = new Channel();
|
|
232
|
+
this.gestureByName.set(name, ch);
|
|
233
|
+
}
|
|
234
|
+
return ch.add(fn);
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
pointer = {
|
|
238
|
+
onUpdate: (fn) => this.pointerCh.add(fn),
|
|
239
|
+
};
|
|
240
|
+
silhouette = {
|
|
241
|
+
onUpdate: (fn) => this.silhouetteCh.add(fn),
|
|
242
|
+
};
|
|
243
|
+
motion = {
|
|
244
|
+
onUpdate: (fn) => this.motionCh.add(fn),
|
|
245
|
+
};
|
|
246
|
+
onCapabilityChanged(fn) {
|
|
247
|
+
return this.capChangeCh.add(fn);
|
|
248
|
+
}
|
|
249
|
+
onStateChange(fn) {
|
|
250
|
+
return this.stateCh.add(fn);
|
|
251
|
+
}
|
|
252
|
+
/* --- video ------------------------------------------------------------- */
|
|
253
|
+
pc = null;
|
|
254
|
+
/**
|
|
255
|
+
* Establishes the WebRTC stream. The whole signalling exchange is hidden;
|
|
256
|
+
* callers get a MediaStream.
|
|
257
|
+
*/
|
|
258
|
+
async openStream() {
|
|
259
|
+
const RTC = globalThis.RTCPeerConnection;
|
|
260
|
+
if (!RTC)
|
|
261
|
+
throw new Error('WebRTC is not available in this environment');
|
|
262
|
+
const pc = new RTC({ iceServers: [] }); // local-only: no STUN needed
|
|
263
|
+
this.pc = pc;
|
|
264
|
+
pc.addTransceiver('video', { direction: 'recvonly' });
|
|
265
|
+
const stream = new MediaStream();
|
|
266
|
+
const track = new Promise((resolve) => {
|
|
267
|
+
pc.ontrack = (ev) => {
|
|
268
|
+
stream.addTrack(ev.track);
|
|
269
|
+
resolve(stream);
|
|
270
|
+
};
|
|
271
|
+
});
|
|
272
|
+
pc.onicecandidate = (ev) => {
|
|
273
|
+
if (ev.candidate) {
|
|
274
|
+
void this.#request('camera.ice', { candidate: ev.candidate.toJSON() }).catch(() => {
|
|
275
|
+
/* trickle ICE is best-effort; the offer/answer already carries host candidates */
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const offer = await pc.createOffer();
|
|
280
|
+
await pc.setLocalDescription(offer);
|
|
281
|
+
const res = await this.#request('camera.offer', { sdp: offer.sdp, type: offer.type });
|
|
282
|
+
await pc.setRemoteDescription({
|
|
283
|
+
type: 'answer',
|
|
284
|
+
sdp: res.sdp,
|
|
285
|
+
});
|
|
286
|
+
return track;
|
|
287
|
+
}
|
|
288
|
+
closeStream() {
|
|
289
|
+
this.pc?.close();
|
|
290
|
+
this.pc = null;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Delays skeleton delivery to match the video's playback clock.
|
|
294
|
+
*
|
|
295
|
+
* Skeletons arrive one to two frames ahead of the picture, so drawing an
|
|
296
|
+
* overlay straight from body.onUpdate puts the lines in front of the player.
|
|
297
|
+
*/
|
|
298
|
+
alignToVideo(video, offsetMs = 0) {
|
|
299
|
+
const queue = [];
|
|
300
|
+
const MAX = 120; // ~4s at 30fps; enough for any plausible offset
|
|
301
|
+
let baseTs = null;
|
|
302
|
+
let baseTime = null;
|
|
303
|
+
let offset = offsetMs;
|
|
304
|
+
const off = this.bodyCh.add((b) => {
|
|
305
|
+
if (baseTs === null) {
|
|
306
|
+
baseTs = b.ts;
|
|
307
|
+
baseTime = video.currentTime;
|
|
308
|
+
}
|
|
309
|
+
queue.push(b);
|
|
310
|
+
if (queue.length > MAX)
|
|
311
|
+
queue.shift();
|
|
312
|
+
});
|
|
313
|
+
return {
|
|
314
|
+
current() {
|
|
315
|
+
if (queue.length === 0 || baseTs === null || baseTime === null)
|
|
316
|
+
return null;
|
|
317
|
+
// Map playback position back onto the skeleton clock.
|
|
318
|
+
const wanted = baseTs + (video.currentTime - baseTime) * 1000 - offset;
|
|
319
|
+
let best = queue[0];
|
|
320
|
+
let bestDist = Math.abs(best.ts - wanted);
|
|
321
|
+
for (let i = 1; i < queue.length; i++) {
|
|
322
|
+
const d = Math.abs(queue[i].ts - wanted);
|
|
323
|
+
if (d <= bestDist) {
|
|
324
|
+
best = queue[i];
|
|
325
|
+
bestDist = d;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return best;
|
|
329
|
+
},
|
|
330
|
+
setOffset(ms) {
|
|
331
|
+
offset = ms;
|
|
332
|
+
},
|
|
333
|
+
get offsetMs() {
|
|
334
|
+
return offset;
|
|
335
|
+
},
|
|
336
|
+
dispose() {
|
|
337
|
+
off();
|
|
338
|
+
queue.length = 0;
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/* --- lifecycle --------------------------------------------------------- */
|
|
343
|
+
dispose() {
|
|
344
|
+
for (const d of this.disposers)
|
|
345
|
+
d();
|
|
346
|
+
this.disposers = [];
|
|
347
|
+
for (const [, entry] of this.pending) {
|
|
348
|
+
this.clearTimer(entry.timer);
|
|
349
|
+
entry.reject(new Error('runtime disposed'));
|
|
350
|
+
}
|
|
351
|
+
this.pending.clear();
|
|
352
|
+
this.closeStream();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ConnectionOptions, ConnectionState, Transport } from './connection.js';
|
|
2
|
+
import type { ConnectStatus } from './capability.js';
|
|
3
|
+
import { RuntimeFacade } from './facade.js';
|
|
4
|
+
import type { HelloMessage } from './protocol.js';
|
|
5
|
+
export interface CreateRuntimeOptions extends Omit<ConnectionOptions, 'require'> {
|
|
6
|
+
/** 'live' talks to the runtime over WebSocket; 'mock' synthesises the same envelope. */
|
|
7
|
+
source?: 'live' | 'mock';
|
|
8
|
+
require?: HelloMessage['require'];
|
|
9
|
+
/** Frame rate for the mock source. Ignored when live. */
|
|
10
|
+
mockFps?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class Runtime extends RuntimeFacade {
|
|
13
|
+
/** Named for the handshake result, not the runtime.status command above it. */
|
|
14
|
+
private lastConnectStatus;
|
|
15
|
+
constructor(transport: Transport, timers?: ConnectionOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Opens the connection and returns the runtime's verdict.
|
|
18
|
+
*
|
|
19
|
+
* Waits for exactly one message type. The protocol guarantees hello is always
|
|
20
|
+
* answered with ready — success or not — so there is no race between "wait for
|
|
21
|
+
* ready" and "wait for an error message".
|
|
22
|
+
*/
|
|
23
|
+
connect(): Promise<ConnectStatus>;
|
|
24
|
+
/** The last handshake result. Null before connect() resolves. */
|
|
25
|
+
get connectStatus(): ConnectStatus | null;
|
|
26
|
+
get state(): ConnectionState;
|
|
27
|
+
close(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function createRuntime(options?: CreateRuntimeOptions): Runtime;
|
|
30
|
+
export { WsConnection, defaultUrl } from './connection.js';
|
|
31
|
+
export { MockTransport } from './mock.js';
|
|
32
|
+
export { RuntimeFacade } from './facade.js';
|
|
33
|
+
export { toConnectStatus, explainMiss, describeGate } from './capability.js';
|
|
34
|
+
export type { ConnectStatus } from './capability.js';
|
|
35
|
+
export { decodeSilhouette, parseBinaryFrame, fnv1a64, binaryCorrelationId, QSBN_HEADER_BYTES } from './decode.js';
|
|
36
|
+
export type { SilhouetteFrame, BinaryFrame } from './decode.js';
|
|
37
|
+
export { RuntimeCommandError } from './facade.js';
|
|
38
|
+
export type { Unsubscribe, GestureName, MotionUpdate, SnapshotResult, DeviceInfo, RuntimeStatus, CameraConfig, CapabilityChange, VideoAligner, } from './facade.js';
|
|
39
|
+
export type { ConnectionState, ConnectionOptions, Transport } from './connection.js';
|
|
40
|
+
export type { BodyUpdate, Capability, CapabilityMiss, CalibBox, DepthSilhouette, DeviceConnected, DeviceError, GestureUpdate, HelloMessage, Joint, PointerUpdate, ReadyMessage, RequestError, RequestName, RuntimeConfig, RuntimeNamespace, ServerMessage, Timestamp, } from './protocol.js';
|
|
41
|
+
export { SCHEMA_HASH, RUNTIME_URL, GRID_W, GRID_H } from './protocol.js';
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC;IAC9E,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAClC,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,OAAQ,SAAQ,aAAa;IACxC,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB,CAA8B;gBAE3C,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,iBAAiB;IAO5D;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IAMvC,iEAAiE;IACjE,IAAI,aAAa,IAAI,aAAa,GAAG,IAAI,CAExC;IAED,IAAI,KAAK,IAAI,eAAe,CAE3B;IAED,KAAK,IAAI,IAAI;CAId;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAazE;AAMD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClH,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrF,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,eAAe,EACf,eAAe,EACf,WAAW,EACX,aAAa,EACb,YAAY,EACZ,KAAK,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
|