@displayxr/inline3d 1.27.0 → 1.29.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/CHANGELOG.md +224 -0
- package/README.md +6 -3
- package/call-full.d.ts +21 -0
- package/call.d.ts +454 -0
- package/dist/call.js +81 -0
- package/index.d.ts +34 -2
- package/js/call/capture.js +212 -0
- package/js/call/disparity.js +432 -0
- package/js/call/element.js +217 -0
- package/js/call/lift.js +323 -0
- package/js/call/qr.js +283 -0
- package/js/call/sdp.js +86 -0
- package/js/call/signaling.js +203 -0
- package/js/call/transport.js +589 -0
- package/js/call/ui.js +146 -0
- package/js/call/wire.js +434 -0
- package/js/inline3d-buffer-limit.js +198 -0
- package/js/inline3d-call-full.js +48 -0
- package/js/inline3d-call.js +1901 -0
- package/js/inline3d-model-playcanvas.js +6 -0
- package/js/inline3d-model.js +6 -0
- package/js/inline3d-player.js +649 -218
- package/js/inline3d-splat-playcanvas.js +134 -18
- package/js/inline3d-splat-video.js +328 -29
- package/js/inline3d-splat.js +5 -0
- package/js/inline3d-viewer.js +86 -10
- package/js/inline3d.js +257 -15
- package/js/lift/index.js +15 -0
- package/model.d.ts +10 -1
- package/package.json +29 -4
- package/player.d.ts +49 -4
- package/splat.d.ts +52 -3
- package/viewer.d.ts +12 -1
package/call.d.ts
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
// Type definitions for @displayxr/inline3d/call.
|
|
2
|
+
// PREVIEW tier — not covered by the 1.x semver promise. See docs/sdk-stability.md.
|
|
3
|
+
//
|
|
4
|
+
// P1 of docs/rfcs/0002-video-call.md: full-mesh WebRTC (<= 4 participants), pluggable signalling,
|
|
5
|
+
// side-by-side stereo from a stereo camera, convergence from `hello` + `hint`, SDK chrome or
|
|
6
|
+
// headless. P2a: mono peers lifted to 3D through `@displayxr/inline3d/lift` (`mono3D`).
|
|
7
|
+
// RFC 0003 C1: `mountCall(el, opts?)` (the one-line path, the wall from `sharedInline3D()`), the
|
|
8
|
+
// `warning` event (`lift-not-bundled`), `key`; `<dxr-call>` is `./call/element`, and
|
|
9
|
+
// `./call/full` is this entry with lift statically imported.
|
|
10
|
+
|
|
11
|
+
import type { Inline3D, Inline3DUnsupported, CreateInline3DOptions } from './index.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The `lift()` of `@displayxr/inline3d/lift`, as the call uses it: `lift(video, { mode: 'live',
|
|
15
|
+
* wall, ui: 'none', convergence, priority, quality })` → a handle. Only the members below are
|
|
16
|
+
* used; `setPriority` / `setConvergence` / `on` / `native` / `canvas` / `state` are optional.
|
|
17
|
+
*/
|
|
18
|
+
export interface CallLiftHandle {
|
|
19
|
+
remove(): void;
|
|
20
|
+
setPriority?(p: LiftStreamPriority): boolean;
|
|
21
|
+
setConvergence?(x: 'auto' | number): void;
|
|
22
|
+
on?(type: string, cb: (d: any) => void): () => void;
|
|
23
|
+
readonly native?: boolean;
|
|
24
|
+
readonly woven?: boolean;
|
|
25
|
+
readonly state?: string;
|
|
26
|
+
readonly canvas?: HTMLCanvasElement;
|
|
27
|
+
readonly stats?: { provider?: string | null } & Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export type CallLiftFunction = ((element: HTMLVideoElement, opts: Record<string, unknown> & {
|
|
30
|
+
mode: 'live';
|
|
31
|
+
wall: unknown;
|
|
32
|
+
ui: 'none';
|
|
33
|
+
convergence: 'auto' | number;
|
|
34
|
+
priority: LiftStreamPriority;
|
|
35
|
+
}) => Promise<CallLiftHandle>) & { liftCapabilities?: (o?: { webFallback?: boolean }) => Promise<any> };
|
|
36
|
+
|
|
37
|
+
/** A lifted stream's scheduling (the native provider's per-stream priority). */
|
|
38
|
+
export type LiftStreamPriority = 'high' | 'normal' | 'low' | 'paused';
|
|
39
|
+
|
|
40
|
+
/** Why a mono peer is not lifted (or `'lifted'`). */
|
|
41
|
+
export type Mono3DReason = 'unavailable' | 'off' | 'lifted' | 'budget' | 'failed';
|
|
42
|
+
|
|
43
|
+
/** What a participant sends: a side-by-side pair (left eye left) or one flat picture. */
|
|
44
|
+
export type CallFormat = 'sbs' | 'mono';
|
|
45
|
+
|
|
46
|
+
/** How this page draws a remote participant (RFC §3). */
|
|
47
|
+
export type CallRoute = 'woven-sbs' | 'flat-left' | 'flat' | 'lifted';
|
|
48
|
+
|
|
49
|
+
/** A remote participant's connection state as the tile shows it. */
|
|
50
|
+
/**
|
|
51
|
+
* `'unreachable'`: known to exist but no connection for ~10 s — usually a network that needs a
|
|
52
|
+
* relay (TURN). Sticky until it connects; retries continue in the background.
|
|
53
|
+
*/
|
|
54
|
+
export type PeerState = 'new' | 'connecting' | 'connected' | 'reconnecting' | 'unreachable' | 'left';
|
|
55
|
+
|
|
56
|
+
/** The out-of-band 3D flag, sent on the data channel when it opens. A peer that sends none is mono. */
|
|
57
|
+
export interface CallHello {
|
|
58
|
+
type: 'hello';
|
|
59
|
+
v: number;
|
|
60
|
+
format: CallFormat;
|
|
61
|
+
width: number | null;
|
|
62
|
+
height: number | null;
|
|
63
|
+
/** Camera baseline, mm. Needed (with `hfovDeg`) for automatic convergence. */
|
|
64
|
+
baselineMm: number | null;
|
|
65
|
+
/** Horizontal field of view of ONE eye, degrees. */
|
|
66
|
+
hfovDeg: number | null;
|
|
67
|
+
/** False for a raw stereo camera (the P1 default); true only after a calibrated rectify step. */
|
|
68
|
+
rectified: boolean;
|
|
69
|
+
sdk: string | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CallCalibration {
|
|
73
|
+
baselineMm?: number;
|
|
74
|
+
hfovDeg?: number;
|
|
75
|
+
rectified?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The signalling seam. Any object with `join()` works — bring your own accounts / push service.
|
|
80
|
+
* `join` rejects with an Error whose `code` is `'room-full'` when the room is at capacity.
|
|
81
|
+
*/
|
|
82
|
+
export interface SignalingAdapter {
|
|
83
|
+
readonly name?: string;
|
|
84
|
+
join(room: string, hooks: SignalingHooks): Promise<SignalingSession>;
|
|
85
|
+
}
|
|
86
|
+
export interface SignalingHooks {
|
|
87
|
+
/** This peer's id — stable across signalling reconnects. */
|
|
88
|
+
id: string;
|
|
89
|
+
maxPeers: number;
|
|
90
|
+
onPeerJoined?(id: string): void;
|
|
91
|
+
onPeerLeft?(id: string): void;
|
|
92
|
+
/** An opaque offer / answer / ICE blob relayed from `from`. */
|
|
93
|
+
onSignal?(from: string, data: unknown): void;
|
|
94
|
+
onDisconnect?(err?: Error): void;
|
|
95
|
+
/** A participant known to exist that signalling itself cannot reach (shown as an unreachable tile). */
|
|
96
|
+
onPeerUnreachable?(ghostId: string): void;
|
|
97
|
+
onPeerReachable?(ghostId: string): void;
|
|
98
|
+
onReconnect?(peerIds: string[]): void;
|
|
99
|
+
}
|
|
100
|
+
export interface SignalingSession {
|
|
101
|
+
id: string;
|
|
102
|
+
/** Peers already in the room when this one joined. */
|
|
103
|
+
peers: string[];
|
|
104
|
+
/** Short-lived TURN credentials minted by the server, if it has any. */
|
|
105
|
+
iceServers?: RTCIceServer[];
|
|
106
|
+
send(to: string, data: unknown): void;
|
|
107
|
+
leave(): void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The `dxr-signal/1` JSON-over-WebSocket client (protocol: signaling/README.md). `url` is your
|
|
112
|
+
* server's base, e.g. `wss://signal.example.com` or `ws://localhost:8787` — the reference servers
|
|
113
|
+
* are `signaling/worker.mjs` (Cloudflare) and `node signaling/dev-server.mjs`. There is no hosted
|
|
114
|
+
* default yet, so the URL is required.
|
|
115
|
+
*/
|
|
116
|
+
/** The hosted DisplayXR signalling server (`dxrSignaling()` with no URL). */
|
|
117
|
+
export const DXR_SIGNAL_DEFAULT: string;
|
|
118
|
+
export function dxrSignaling(url?: string, opts?: { WebSocket?: any; pingMs?: number; /** A publishable key for the hosted service (public by design; sent on connect). */ key?: string }): SignalingAdapter;
|
|
119
|
+
|
|
120
|
+
export type CallAccent = 'azure' | 'violet' | 'magenta' | 'sunset' | 'amber' | 'lime' | 'mint' | 'ice';
|
|
121
|
+
|
|
122
|
+
export interface CallOptions {
|
|
123
|
+
/**
|
|
124
|
+
* The inline-3D manager to put the tiles on. `mountCall` defaults it to the document's shared
|
|
125
|
+
* one (`sharedInline3D()`); `addCall` takes it as its first argument instead.
|
|
126
|
+
*/
|
|
127
|
+
wall?: Inline3D | Inline3DUnsupported | null;
|
|
128
|
+
/** Default `dxrSignaling()` (the hosted DisplayXR server). Or `dxrSignaling(url)` to self-host,
|
|
129
|
+
* or your own adapter. */
|
|
130
|
+
signaling?: SignalingAdapter;
|
|
131
|
+
/**
|
|
132
|
+
* A publishable key for the hosted signalling service (`pk_…`, RFC 0003 §5a). Public by design
|
|
133
|
+
* — it sits in page source — and only used when `signaling` is left at the default.
|
|
134
|
+
*/
|
|
135
|
+
key?: string;
|
|
136
|
+
/**
|
|
137
|
+
* `'auto'` (default): the room in this page's `#room=` fragment, else a new one on join. Or a
|
|
138
|
+
* room id / invite link. Room ids are >= 96 random bits (generated ones: 128), base64url.
|
|
139
|
+
*/
|
|
140
|
+
room?: 'auto' | string;
|
|
141
|
+
/** Overrides the server's STUN/TURN list. Default: public STUN + the server's TURN, if any. */
|
|
142
|
+
iceServers?: RTCIceServer[];
|
|
143
|
+
/**
|
|
144
|
+
* `'auto'` (default): a device delivering > 2.5:1 frames is a stereo pair, sent as `'sbs'`;
|
|
145
|
+
* otherwise the default webcam, `'mono'`. A camera held by another process (e.g. an eye
|
|
146
|
+
* tracker: NotReadableError) is skipped, never fatal. `'stereo'` prefers the pair (falls back
|
|
147
|
+
* to mono), `'mono'` never probes, a string is a deviceId, a MediaStream is used as given with
|
|
148
|
+
* `format` declaring what it is.
|
|
149
|
+
*/
|
|
150
|
+
camera?: 'auto' | 'stereo' | 'mono' | string | MediaStream;
|
|
151
|
+
/** The format of a page-supplied `camera` stream. Default `'mono'` — 3D-ness is never guessed. */
|
|
152
|
+
format?: CallFormat;
|
|
153
|
+
/** Sent in `hello`; enables automatic convergence on the receivers. */
|
|
154
|
+
calibration?: CallCalibration;
|
|
155
|
+
/**
|
|
156
|
+
* P2 seam: a calibrated rectification step for a raw stereo stream (needs the camera's
|
|
157
|
+
* intrinsics/extrinsics, which come from a plug-in or the runtime). Return a rectified SBS
|
|
158
|
+
* stream; the hello then says `rectified: true`. P1 ships none: the raw pair is sent.
|
|
159
|
+
*/
|
|
160
|
+
rectify?: (stream: MediaStream, info: { width: number; height: number; deviceId: string | null; label: string }) => MediaStream | Promise<MediaStream>;
|
|
161
|
+
/** Microphone (echo cancellation + noise suppression). Default true. */
|
|
162
|
+
audio?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Auto-convergence (default true): measure the disparity of the point between each SBS peer's
|
|
165
|
+
* eyes on the received frames and shift the two eyes by half of it each, so the remote person sits
|
|
166
|
+
* at the display plane. No calibration needed. The depth slider stays an offset on top; a peer's
|
|
167
|
+
* `hint` is used only while no measurement has succeeded. Lifted (mono) tiles are unaffected.
|
|
168
|
+
*/
|
|
169
|
+
autoConverge?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Lift mono peers to 3D on a 3D display (RFC §5). `'auto'` (default): import
|
|
172
|
+
* `@displayxr/inline3d/lift` lazily from this copy of the SDK, and use it when it reports a
|
|
173
|
+
* native provider or a WebGPU web fallback — otherwise mono peers stay flat (one log line, no
|
|
174
|
+
* error). `'off'` / `false`: flat. A function: your own `lift` (a bundled app imports
|
|
175
|
+
* `lift` from `@displayxr/inline3d/lift` and passes it here). Switch at runtime with
|
|
176
|
+
* `setMono3D()`.
|
|
177
|
+
*/
|
|
178
|
+
mono3D?: 'auto' | 'off' | false | CallLiftFunction;
|
|
179
|
+
/**
|
|
180
|
+
* Extra `lift()` options for lifted tiles — e.g. `models` (where the depth model is served from),
|
|
181
|
+
* `ort`, `quality`, `providers`. The call's own keys (`mode`, `wall`, `ui`, `convergence`,
|
|
182
|
+
* `priority`) always win.
|
|
183
|
+
*/
|
|
184
|
+
liftOptions?: Record<string, unknown>;
|
|
185
|
+
/** Concurrent lifted tiles. Default 4 (= the mesh maximum); further mono peers stay flat. */
|
|
186
|
+
maxLifted?: number;
|
|
187
|
+
/** Participants INCLUDING you. Default 4, clamped to 2..4 (full mesh). */
|
|
188
|
+
maxPeers?: number;
|
|
189
|
+
/** `'grid'` (default) or `'speaker'` (the active speaker spans the row). */
|
|
190
|
+
layout?: 'grid' | 'speaker';
|
|
191
|
+
/** SDK chrome: lobby, invite (link + QR), bottom bar, badges, banner. Default true. */
|
|
192
|
+
ui?: boolean;
|
|
193
|
+
/** Join without the lobby. Default: `!ui`. */
|
|
194
|
+
autoJoin?: boolean;
|
|
195
|
+
/** The small self view (mirrored; a stereo self view is mirrored AND eye-swapped). Default true. */
|
|
196
|
+
selfView?: boolean;
|
|
197
|
+
/** Tile aspect (w/h) = the woven buffer's per-eye aspect. Default 16/9. */
|
|
198
|
+
tileAspect?: number;
|
|
199
|
+
/** A named accent or any CSS colour, written to `--dxr-accent`. */
|
|
200
|
+
accent?: CallAccent | string;
|
|
201
|
+
/** Base URL of invite links (default: this page's URL, query kept, fragment replaced). */
|
|
202
|
+
inviteBase?: string;
|
|
203
|
+
/** Write `#room=` into this page's URL on join (so a reload rejoins). Default: `ui`. */
|
|
204
|
+
updateUrl?: boolean;
|
|
205
|
+
/** Where the "View in 3D with DisplayXR Browser" banner links. */
|
|
206
|
+
browserUrl?: string;
|
|
207
|
+
/** Re-open the inline-3D session if it ends without the page closing it. Default true. */
|
|
208
|
+
recoverSession?: boolean;
|
|
209
|
+
/** Scroll the call block to the top of the viewport once, on join, so every tile (and the self
|
|
210
|
+
* view) is on screen and woven. Default true (only with `ui`). */
|
|
211
|
+
scrollIntoView?: boolean;
|
|
212
|
+
/** `createInline3D()` options for the wall `mountCall` creates, and for a session re-open. */
|
|
213
|
+
wallOptions?: CreateInline3DOptions;
|
|
214
|
+
debug?: boolean;
|
|
215
|
+
log?: (tag: string, detail: object) => void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface CallQuality {
|
|
219
|
+
in: { width: number; height: number; fps: number; codec: string | null; kbps: number | null; dropped: number; decoder: string | null } | null;
|
|
220
|
+
out: { width: number; height: number; fps: number; codec: string | null; kbps: number | null; limitation: string | null; encoder: string | null } | null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface CallPeer {
|
|
224
|
+
readonly id: string;
|
|
225
|
+
readonly format: CallFormat;
|
|
226
|
+
readonly route: CallRoute | null;
|
|
227
|
+
readonly state: PeerState;
|
|
228
|
+
readonly muted: boolean;
|
|
229
|
+
readonly cameraOff: boolean;
|
|
230
|
+
readonly speaking: boolean;
|
|
231
|
+
readonly rectified: boolean;
|
|
232
|
+
readonly hello: CallHello | null;
|
|
233
|
+
readonly quality: CallQuality | null;
|
|
234
|
+
/** The per-eye convergence shift currently painted, source px. */
|
|
235
|
+
readonly convergencePx: number;
|
|
236
|
+
/** Auto-convergence: measured disparity (source px; null until a lock), mean ms per measurement, last method. */
|
|
237
|
+
readonly autoConverge: { disparityPx: number | null; ms: number | null; readMs: number | null; method: 'mode' | 'focus' | 'track' | null; via: 'videoframe' | 'canvas' | null };
|
|
238
|
+
/** A lifted tile: whether lift is showing yet, native or web, and its current priority. */
|
|
239
|
+
readonly lift: { readonly live: boolean; readonly native: boolean; readonly priority: LiftStreamPriority | null; readonly state: string | null } | null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface CallMono3DInfo {
|
|
243
|
+
readonly on: boolean;
|
|
244
|
+
readonly state: 'off' | 'idle' | 'loading' | 'ready' | 'unavailable';
|
|
245
|
+
/** Why it is unavailable: `'import-failed'`, `'no-lift-export'`, `'no-provider'`, `'no-webgpu'`. */
|
|
246
|
+
readonly reason: string | null;
|
|
247
|
+
/** From `liftCapabilities()` when the lift module has it; null when unknown. */
|
|
248
|
+
readonly native: boolean | null;
|
|
249
|
+
readonly provider: string | null;
|
|
250
|
+
readonly lifted: number;
|
|
251
|
+
readonly max: number;
|
|
252
|
+
/** A lift has gone live in this call. */
|
|
253
|
+
readonly proven: boolean;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface CallEvents {
|
|
257
|
+
peer: { id: string };
|
|
258
|
+
peerleft: { id: string; reason: string };
|
|
259
|
+
format: { id: string; format: CallFormat; route: CallRoute; mono3d: Mono3DReason | null; hello: CallHello | null };
|
|
260
|
+
/**
|
|
261
|
+
* Per peer (`id`) every 2 s; or, with `id: null` and `lift`, when lifted tiles on the web
|
|
262
|
+
* provider degrade the page's frame rate (~< 20 fps for 3 s) and when it recovers.
|
|
263
|
+
*/
|
|
264
|
+
quality: ({ id: string } & CallQuality) | ({ id: null; in: null; out: null; lift: { degraded: boolean; frameMs: number; tiles: number } });
|
|
265
|
+
speaker: { id: string | null };
|
|
266
|
+
/** Codes include `'camera-busy'`, `'no-camera'`, `'unreachable'` (error.peer = the tile id), `'room-full'`, `'session-ended'`. */
|
|
267
|
+
error: { code: string; message: string; error: Error | null };
|
|
268
|
+
/**
|
|
269
|
+
* Degraded, not broken. `'lift-not-bundled'`: a 2D participant is on a 3D display, `mono3D`
|
|
270
|
+
* is `'auto'`, and the lift module could not be imported from this build (a bundler that could
|
|
271
|
+
* not follow the computed import, or a copy of the SDK without `./lift`) — they stay flat. Fix:
|
|
272
|
+
* `import { lift } from '@displayxr/inline3d/lift'` and pass `mono3D: lift`, or import
|
|
273
|
+
* `@displayxr/inline3d/call/full`. Emitted once per call, the first time such a peer routes,
|
|
274
|
+
* with one `console.warn`. A display that honestly cannot lift is NOT this: that is
|
|
275
|
+
* `handle.mono3D.reason` (`'no-provider'` / `'no-webgpu'`) and an info line.
|
|
276
|
+
*/
|
|
277
|
+
warning: { code: 'lift-not-bundled' | string; message: string };
|
|
278
|
+
state: { id: string; state: PeerState };
|
|
279
|
+
joined: { room: string; id: string };
|
|
280
|
+
left: { room: string };
|
|
281
|
+
session: { wall: unknown };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface CallHandle {
|
|
285
|
+
readonly room: string | null;
|
|
286
|
+
readonly id: string;
|
|
287
|
+
readonly state: 'idle' | 'lobby' | 'joining' | 'in-call' | 'full' | 'left';
|
|
288
|
+
readonly wall: unknown;
|
|
289
|
+
readonly format: CallFormat | null;
|
|
290
|
+
readonly muted: boolean;
|
|
291
|
+
readonly depth: number;
|
|
292
|
+
readonly speaker: string | null;
|
|
293
|
+
/** `'busy'` = held by another app (e.g. eye tracking): the call runs audio-only until `retryCamera()`. */
|
|
294
|
+
readonly camera: 'ok' | 'busy' | 'none' | 'pending';
|
|
295
|
+
/** mono→3D status (see {@link CallMono3DInfo}). */
|
|
296
|
+
readonly mono3D: CallMono3DInfo;
|
|
297
|
+
/** Read-only snapshot of the remote participants. */
|
|
298
|
+
readonly peers: ReadonlyArray<CallPeer>;
|
|
299
|
+
join(): Promise<CallHandle>;
|
|
300
|
+
/** `…#room=<id>` — the room rides in the fragment, which never reaches a server. Null before a room exists. */
|
|
301
|
+
inviteLink(): string | null;
|
|
302
|
+
/** Mute (true), unmute (false) or toggle (no argument). Returns the new state. */
|
|
303
|
+
mute(on?: boolean): boolean;
|
|
304
|
+
cameraOff(on?: boolean): boolean;
|
|
305
|
+
setCamera(idOrStream: string | MediaStream, opts?: { format?: CallFormat }): Promise<{ format: CallFormat; width: number; height: number; label: string }>;
|
|
306
|
+
/**
|
|
307
|
+
* The ONE depth control, [-1, 1] (+ = push back). Stereo tiles: ± 5% of the eye width added to
|
|
308
|
+
* the automatic convergence. Lifted tiles: lift's convergence (0 → `'auto'`, else 0.5 + 0.5·v).
|
|
309
|
+
*/
|
|
310
|
+
setDepth(v: number | null): number;
|
|
311
|
+
/** Try the configured camera again (e.g. after the eye tracker released it). Keeps the current one on failure. */
|
|
312
|
+
retryCamera(): Promise<{ format: CallFormat; width: number; height: number; label: string }>;
|
|
313
|
+
/** Send `hint {subjectZmm}` (rate-limited to 5 Hz) if the page can estimate the face distance. */
|
|
314
|
+
sendHint(subjectZmm: number): boolean;
|
|
315
|
+
/** Turn mono→3D on/off at runtime (no argument toggles). Off releases every lift; tiles go flat. */
|
|
316
|
+
setMono3D(on?: boolean): boolean;
|
|
317
|
+
leave(): void;
|
|
318
|
+
on<K extends keyof CallEvents>(type: K, cb: (e: CallEvents[K]) => void): () => void;
|
|
319
|
+
off<K extends keyof CallEvents>(type: K, cb: (e: CallEvents[K]) => void): void;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function addCall(wall: unknown, container: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The one-line path (RFC 0003 §1): `addCall` with every argument optional. The wall is
|
|
326
|
+
* `opts.wall` if given, else the document's shared manager (`sharedInline3D(opts.wallOptions)` —
|
|
327
|
+
* the page's existing session, never a second one). Hosted signalling, `camera: 'auto'` and the
|
|
328
|
+
* SDK chrome by default; resolves once the lobby shows (or, with `autoJoin` / `ui: false`, once
|
|
329
|
+
* joined). `<dxr-call>` (`@displayxr/inline3d/call/element`) is this as markup.
|
|
330
|
+
*/
|
|
331
|
+
export function mountCall(el: HTMLElement, opts?: CallOptions): Promise<CallHandle>;
|
|
332
|
+
|
|
333
|
+
// ── <dxr-call> — the one-line path as markup ───────────────────────────────────────────────
|
|
334
|
+
//
|
|
335
|
+
// Importing this entry (or `./call/full`, or the CDN bundle `dist/call.js`) registers the element.
|
|
336
|
+
// It mounts on connect (`mountCall(this, { ...attrsToOpts(this), ...this.options })`), leaves on
|
|
337
|
+
// disconnect (a DOM move IS a teardown — woven-canvas rule 2), re-dispatches every call event as
|
|
338
|
+
// a bubbling, composed `CustomEvent` named `dxr-call:<event>` with the payload in `detail`, and
|
|
339
|
+
// exposes the handle as `el.call`. Attributes (read once, at connect): `room`, `signaling` (a
|
|
340
|
+
// URL), `key`, `camera`, `layout`, `accent`, `max-peers`, `no-ui`, `auto-join`, `mono3d="off"`,
|
|
341
|
+
// `no-audio`, `no-self-view`, `no-auto-converge`, `tile-aspect`, `invite-base`, `browser-url`,
|
|
342
|
+
// `debug`. Everything else is `el.options`, set before connect; options win over attributes.
|
|
343
|
+
|
|
344
|
+
export class DxrCallElement extends HTMLElement {
|
|
345
|
+
/** The mount seam: null = `mountCall` of `./call`; `./call/full` sets its own. */
|
|
346
|
+
static mount: ((el: HTMLElement, opts?: CallOptions) => Promise<CallHandle>) | null;
|
|
347
|
+
/** Non-string options (a `MediaStream`, an adapter, `lift`, a `wall`) — set BEFORE connecting. */
|
|
348
|
+
options: CallOptions | null;
|
|
349
|
+
/** The handle once mounted; null before, and again after disconnect. */
|
|
350
|
+
readonly call: CallHandle | null;
|
|
351
|
+
/** Resolves with the handle when the mount lands (null if the element left the DOM first); rejects if it failed. */
|
|
352
|
+
readonly ready: Promise<CallHandle | null> | null;
|
|
353
|
+
connectedCallback(): void;
|
|
354
|
+
disconnectedCallback(): void;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The attribute → option mapping the element applies at connect. Pure; unset attributes contribute nothing. */
|
|
358
|
+
export function attrsToOpts(source: { getAttribute(name: string): string | null } | ((name: string) => string | null)): Partial<CallOptions>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Register the element (default name `dxr-call`) once. True when this call registered it; false
|
|
362
|
+
* when the name was taken or there is no `customElements` registry. The entries call it for you.
|
|
363
|
+
*/
|
|
364
|
+
export function defineCallElement(name?: string, registry?: CustomElementRegistry): boolean;
|
|
365
|
+
|
|
366
|
+
/** `'dxr-call:'` — every DOM event the element dispatches starts with it. */
|
|
367
|
+
export const CALL_EVENT_PREFIX: 'dxr-call:';
|
|
368
|
+
|
|
369
|
+
/** The DOM events: one per {@link CallEvents} key, plus `dxr-call:ready` once the handle exists. */
|
|
370
|
+
export type DxrCallEventMap = { [K in keyof CallEvents as `dxr-call:${K}`]: CustomEvent<CallEvents[K]> } & {
|
|
371
|
+
'dxr-call:ready': CustomEvent<{ call: CallHandle }>;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
declare global {
|
|
375
|
+
interface HTMLElementTagNameMap {
|
|
376
|
+
'dxr-call': DxrCallElement;
|
|
377
|
+
}
|
|
378
|
+
interface HTMLElementEventMap extends DxrCallEventMap {}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ── pure helpers (exported for tests and advanced pages) ──────────────────────────────────
|
|
382
|
+
export function normalizeCallOptions(opts?: Partial<CallOptions>): Record<string, unknown>;
|
|
383
|
+
export function newRoomId(getRandomValues?: (a: Uint8Array) => Uint8Array): string;
|
|
384
|
+
export function isValidRoomId(room: unknown): boolean;
|
|
385
|
+
export function parseInviteLink(link: string | { hash?: string; href?: string } | null | undefined): string | null;
|
|
386
|
+
export function buildInviteLink(base: string, room: string): string;
|
|
387
|
+
export function normalizeHello(msg: unknown): CallHello | null;
|
|
388
|
+
export function makeHello(o?: { format?: CallFormat; width?: number; height?: number; baselineMm?: number; hfovDeg?: number; rectified?: boolean }): object;
|
|
389
|
+
export function routeFor(p: { format?: string | null; woven: boolean; mono3D?: 'auto' | 'off' | false | CallLiftFunction; lift?: boolean; overBudget?: boolean; failed?: boolean }): { route: CallRoute; mono3d?: Mono3DReason };
|
|
390
|
+
export function badgeFor(route: CallRoute | null): '3D' | '2D→3D' | '2D';
|
|
391
|
+
export function normalizeMono3D(v: unknown): 'auto' | 'off' | CallLiftFunction;
|
|
392
|
+
export function resolveLift(
|
|
393
|
+
mono3D: 'auto' | 'off' | false | CallLiftFunction,
|
|
394
|
+
o?: { importer?: () => Promise<any>; nav?: any; log?: (tag: string, o: object) => void; capsOpts?: object },
|
|
395
|
+
): Promise<{ lift: CallLiftFunction | null; capabilities: ((o?: object) => Promise<any>) | null; caps: any; source: 'injected' | 'module' | 'none'; reason: string | null }>;
|
|
396
|
+
export function liftConvergenceFor(depth: number | null | undefined): 'auto' | number;
|
|
397
|
+
export function liftPriorityFor(p: { id: string | null; speakerId: string | null; visible?: boolean }): LiftStreamPriority;
|
|
398
|
+
/** The hook for the native provider's per-stream priority: forwards to `handle.setPriority` (no-op without one). */
|
|
399
|
+
export function setLiftPriority(handle: CallLiftHandle | null | undefined, level: LiftStreamPriority): boolean;
|
|
400
|
+
export function defaultLiftSpecifier(base?: string): string;
|
|
401
|
+
export const LIFT_PRIORITY: Readonly<{ speaker: 'high'; other: 'normal'; hidden: 'paused' }>;
|
|
402
|
+
export function createFrameWatch(o?: { degradedMs?: number; recoveredMs?: number; holdMs?: number; alpha?: number }): {
|
|
403
|
+
feed(dtMs: number): 'degraded' | 'recovered' | null;
|
|
404
|
+
reset(): void;
|
|
405
|
+
readonly frameMs: number | null;
|
|
406
|
+
readonly degraded: boolean;
|
|
407
|
+
};
|
|
408
|
+
export interface CallLiftPool {
|
|
409
|
+
readonly size: number;
|
|
410
|
+
max: number;
|
|
411
|
+
has(id: string): boolean;
|
|
412
|
+
handle(id: string): CallLiftHandle | null;
|
|
413
|
+
priority(id: string): LiftStreamPriority | null;
|
|
414
|
+
canAcquire(id: string): boolean;
|
|
415
|
+
acquire(id: string, element: HTMLVideoElement, o?: { wall?: unknown; visible?: boolean; onError?: (e: unknown) => void }): Promise<CallLiftHandle | null>;
|
|
416
|
+
release(id: string): void;
|
|
417
|
+
releaseAll(): void;
|
|
418
|
+
setSpeaker(id: string | null): void;
|
|
419
|
+
setVisible(id: string, visible: boolean): void;
|
|
420
|
+
setDepth(v: number): 'auto' | number;
|
|
421
|
+
readonly anyWeb: boolean;
|
|
422
|
+
list(): Array<{ id: string; priority: LiftStreamPriority | null; pending: boolean; native: boolean }>;
|
|
423
|
+
}
|
|
424
|
+
export function createLiftPool(o: { lift: CallLiftFunction; max?: number; log?: (tag: string, o: object) => void; options?: Record<string, unknown> }): CallLiftPool;
|
|
425
|
+
export function convergenceShiftPx(p: { eyeWidthPx: number; hfovDeg?: number | null; baselineMm?: number | null; subjectZmm?: number | null }): number;
|
|
426
|
+
export function lowPass(prev: number, target: number, alpha?: number): number;
|
|
427
|
+
/** The disparity (left x − right x, input px) of the point between the eyes in a grayscale SBS frame. */
|
|
428
|
+
export function measureFocusDisparity(
|
|
429
|
+
img: ArrayLike<number>,
|
|
430
|
+
W: number,
|
|
431
|
+
H: number,
|
|
432
|
+
o?: { focus?: { x: number; y: number }; block?: number; dMin?: number; dMax?: number; dyMax?: number; minNcc?: number },
|
|
433
|
+
): { d: number; x: number; y: number; c: number; method: 'focus' | 'mode'; blocks: number } | null;
|
|
434
|
+
export function clampShift(px: number, eyeWidthPx: number, maxFraction?: number): number;
|
|
435
|
+
export function eyeCropRect(eyeW: number, eyeH: number, aspect: number, shift: number, eye: 0 | 1): { sx: number; sy: number; sw: number; sh: number };
|
|
436
|
+
export function mirrorSwapOps(W: number, H: number): Array<{ src: 'L' | 'R'; sx: number; sw: number; sy: number; sh: number; dx: number; dw: number; mirror: true }>;
|
|
437
|
+
export function mirrorSwapPixels<T extends ArrayLike<number>>(px: T, W: number, H: number): T;
|
|
438
|
+
export function maxBitrateKbps(format: CallFormat, remotePeers: number): number;
|
|
439
|
+
export function preferVideoCodecs(sdp: string, order?: readonly string[]): string;
|
|
440
|
+
export function sortCodecCapabilities<T extends { mimeType?: string }>(codecs: T[], order?: readonly string[]): T[];
|
|
441
|
+
export const VIDEO_CODEC_ORDER: readonly string[];
|
|
442
|
+
export function clampMaxPeers(n?: number): number;
|
|
443
|
+
export function qrEncode(text: string, opts?: { ecl?: 'L' | 'M'; mask?: number }): { version: number; size: number; mask: number; modules: boolean[][] };
|
|
444
|
+
export function roomKey(room: string): Promise<string>;
|
|
445
|
+
export const SIGNAL_PROTOCOL: string;
|
|
446
|
+
export const WIRE_VERSION: number;
|
|
447
|
+
export const CALL_SDK: string;
|
|
448
|
+
export const PLATE_TEXT: Readonly<{ unreachable: string; cameraBusy: string }>;
|
|
449
|
+
export function createLiveGate(o?: { need?: number; needNoPose?: number }): { feed(viewCount: number | null): boolean; readonly live: boolean };
|
|
450
|
+
export const CALL_ACCENTS: Readonly<Record<CallAccent, string>>;
|
|
451
|
+
export class MeshTransport {
|
|
452
|
+
constructor(o: { signaling: SignalingAdapter; id: string; maxPeers?: number; iceServers?: RTCIceServer[]; RTCPeerConnection?: any; log?: (tag: string, obj: object) => void });
|
|
453
|
+
readonly size: number;
|
|
454
|
+
}
|