@ksp-gonogo/kerbcast 1.4.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 +186 -0
- package/LICENSE +369 -0
- package/README.md +112 -0
- package/dist/__generated__/types.d.ts +529 -0
- package/dist/__generated__/types.js +50 -0
- package/dist/client.d.ts +433 -0
- package/dist/client.js +821 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/noise.d.ts +34 -0
- package/dist/noise.js +274 -0
- package/dist/panZoom.d.ts +190 -0
- package/dist/panZoom.js +302 -0
- package/dist/testing/index.d.ts +217 -0
- package/dist/testing/index.js +606 -0
- package/package.json +57 -0
- package/src/__generated__/types.ts +488 -0
- package/src/client.test.ts +1673 -0
- package/src/client.ts +1219 -0
- package/src/domStubs.test.ts +49 -0
- package/src/index.ts +29 -0
- package/src/noise.test.ts +340 -0
- package/src/noise.ts +317 -0
- package/src/panZoom.test.ts +404 -0
- package/src/panZoom.ts +405 -0
- package/src/testing/index.ts +689 -0
- package/src/testing.test.ts +156 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import type { AdaptiveShedPayload, CameraState, ClientMessage, ErrorPayload, Layer, QualityPreset, SettingsStatePayload } from "./__generated__/types";
|
|
2
|
+
import { CameraLifecycle } from "./__generated__/types";
|
|
3
|
+
/** Controls the digital-static noise overlay baked into `cam.mediaStream`. */
|
|
4
|
+
export interface NoiseConfig {
|
|
5
|
+
/** When false, the pipeline is bypassed and `mediaStream` is the raw WebRTC track. */
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Per-camera handle returned from {@link KerbcastClient.camera}.
|
|
10
|
+
* Stable identity per flight ID for the lifetime of the client.
|
|
11
|
+
*/
|
|
12
|
+
export interface KerbcastCameraHandle {
|
|
13
|
+
readonly flightId: number;
|
|
14
|
+
readonly state: CameraState | null;
|
|
15
|
+
readonly mediaStream: MediaStream | null;
|
|
16
|
+
/**
|
|
17
|
+
* True while the camera has a live source whose frames have stopped
|
|
18
|
+
* arriving (the noise pipeline's stall detector). Always false when the
|
|
19
|
+
* noise pipeline is unavailable (no `captureStream`) or bypassed.
|
|
20
|
+
* Transitions are emitted as `stall` events.
|
|
21
|
+
*/
|
|
22
|
+
readonly stalled: boolean;
|
|
23
|
+
/** Current {@link setShowStatic} setting. */
|
|
24
|
+
readonly showStatic: boolean;
|
|
25
|
+
setLayers(layers: Layer[]): Promise<void>;
|
|
26
|
+
setRenderSize(width: number, height: number): Promise<void>;
|
|
27
|
+
setFov(fov: number): Promise<void>;
|
|
28
|
+
setPan(yaw: number, pitch: number): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Set a persistent pan/tilt velocity (normalised, -1..1 per axis;
|
|
31
|
+
* +yaw = right, +pitch = up). Holds until superseded — send
|
|
32
|
+
* `setPanRate(0, 0)` to stop. Smoothing happens in the plugin's frame
|
|
33
|
+
* loop, so call this only when the input changes (e.g. arrow down /
|
|
34
|
+
* release), not on a timer.
|
|
35
|
+
*/
|
|
36
|
+
setPanRate(yawRate: number, pitchRate: number): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Set a persistent zoom velocity (normalised, -1..1; +1 = zoom in,
|
|
39
|
+
* FoV decreasing). Holds until superseded — send `setZoomRate(0)` to
|
|
40
|
+
* stop. Like {@link setPanRate}, call on input change (press-and-hold),
|
|
41
|
+
* not on a timer.
|
|
42
|
+
*/
|
|
43
|
+
setZoomRate(rate: number): Promise<void>;
|
|
44
|
+
setDegrade(level: number): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Request a resolution preset for this camera, or `null` for auto
|
|
47
|
+
* (clear the viewer clamp). Server-wide and last-write-wins across
|
|
48
|
+
* peers; the authoritative result arrives as a `change` event
|
|
49
|
+
* (`state.viewerQuality` + `state.qualityLimitedBy`). The effective
|
|
50
|
+
* resolution is min(operator ceiling, adaptive level, preset): a
|
|
51
|
+
* preset can only lower quality, and the sidecar's adaptive perf
|
|
52
|
+
* machinery keeps overriding it downward while throttled.
|
|
53
|
+
*/
|
|
54
|
+
setQuality(preset: QualityPreset | null): Promise<void>;
|
|
55
|
+
requestKeyframe(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Override noise settings for this camera only. Takes precedence over the
|
|
58
|
+
* client-level default in {@link KerbcastClientConfig.noise}.
|
|
59
|
+
*/
|
|
60
|
+
configure(options: {
|
|
61
|
+
noise?: Partial<NoiseConfig>;
|
|
62
|
+
}): void;
|
|
63
|
+
/**
|
|
64
|
+
* Choose whether animated static is drawn (default true). When false:
|
|
65
|
+
* a stalled source holds its last decoded frame with no noise (the
|
|
66
|
+
* consumer's chrome carries the staleness indicator via the `stall`
|
|
67
|
+
* event / {@link stalled}), and a sourceless path clears to black
|
|
68
|
+
* without noise so only the text overlay renders.
|
|
69
|
+
*/
|
|
70
|
+
setShowStatic(enabled: boolean): void;
|
|
71
|
+
on<K extends keyof KerbcastCameraEvents>(event: K, handler: (data: KerbcastCameraEvents[K]) => void): () => void;
|
|
72
|
+
}
|
|
73
|
+
/** Event payloads emitted by a {@link KerbcastCameraHandle}. */
|
|
74
|
+
export interface KerbcastCameraEvents {
|
|
75
|
+
/** Latest `CameraState` from the sidecar's `camera-state-changed`. */
|
|
76
|
+
change: CameraState;
|
|
77
|
+
/** Track arrival or teardown for this camera. `null` on disconnect. */
|
|
78
|
+
stream: MediaStream | null;
|
|
79
|
+
/** Frames stopped arriving on a live source (`true`) / resumed (`false`). */
|
|
80
|
+
stall: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** Configuration for {@link KerbcastClient} construction. */
|
|
83
|
+
export interface KerbcastClientConfig {
|
|
84
|
+
/** Sidecar host (matches the sidecar's `--http-bind`). */
|
|
85
|
+
host: string;
|
|
86
|
+
/** Sidecar port. */
|
|
87
|
+
port: number;
|
|
88
|
+
/**
|
|
89
|
+
* ICE servers to use. Defaults to Google's public STUN server,
|
|
90
|
+
* which is enough for LAN streaming.
|
|
91
|
+
*/
|
|
92
|
+
iceServers?: RTCIceServer[];
|
|
93
|
+
/**
|
|
94
|
+
* Default noise settings for all cameras. Individual cameras can override
|
|
95
|
+
* this via `cam.configure({ noise: ... })`. Noise is enabled by default.
|
|
96
|
+
*/
|
|
97
|
+
noise?: Partial<NoiseConfig>;
|
|
98
|
+
/**
|
|
99
|
+
* ICE gathering timeout in milliseconds. Passed to the default
|
|
100
|
+
* {@link BrowserKerbcastTransport}; ignored when a custom transport is
|
|
101
|
+
* provided (the transport owns gathering). Defaults to 2000 ms.
|
|
102
|
+
*
|
|
103
|
+
* On LAN topologies where the STUN server is unreachable (e.g. Steam Deck
|
|
104
|
+
* IPv6 LAN with no internet path), gathering can stall for the full STUN
|
|
105
|
+
* timeout. Host candidates alone are sufficient for LAN streaming, so this
|
|
106
|
+
* timeout lets the connect flow proceed once host candidates are gathered.
|
|
107
|
+
*/
|
|
108
|
+
iceGatheringTimeoutMs?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Override how the SDP offer/answer is exchanged. Defaults to a POST to the
|
|
111
|
+
* sidecar's HTTP `/offer`. A station screen injects a version that relays
|
|
112
|
+
* the offer through the main screen (which can reach the sidecar), so the
|
|
113
|
+
* station needs no direct sidecar address. The media itself still flows
|
|
114
|
+
* peer-to-sidecar (direct or via TURN) -- only the handshake is brokered.
|
|
115
|
+
*/
|
|
116
|
+
negotiate?: (offer: {
|
|
117
|
+
sdp: string;
|
|
118
|
+
cameras: number[];
|
|
119
|
+
slots?: number;
|
|
120
|
+
}) => Promise<{
|
|
121
|
+
sdp: string;
|
|
122
|
+
cameras: number[];
|
|
123
|
+
}>;
|
|
124
|
+
}
|
|
125
|
+
/** WebRTC connection state surface. */
|
|
126
|
+
export type KerbcastConnectionState = "disconnected" | "connecting" | "connected" | "failed";
|
|
127
|
+
/**
|
|
128
|
+
* Inbound RTP video statistics for one camera track, derived from the
|
|
129
|
+
* WebRTC stats report. Fields are `undefined` when the browser has not yet
|
|
130
|
+
* emitted that stat (e.g. `framesPerSecond` before the first full second).
|
|
131
|
+
* `packetsReceived` and `bytesReceived` default to 0 in the same case.
|
|
132
|
+
*
|
|
133
|
+
* Retrieve per-camera stats via {@link KerbcastClient.inboundVideoStats}.
|
|
134
|
+
*/
|
|
135
|
+
export interface InboundVideoStats {
|
|
136
|
+
packetsReceived: number;
|
|
137
|
+
bytesReceived: number;
|
|
138
|
+
framesReceived: number | undefined;
|
|
139
|
+
framesDecoded: number | undefined;
|
|
140
|
+
jitter: number | undefined;
|
|
141
|
+
framesPerSecond: number | undefined;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Camera summary returned by {@link KerbcastClient.discover} (`GET /cameras`).
|
|
145
|
+
*
|
|
146
|
+
* This is the shape the sidecar serialises from its internal `CameraInfo`
|
|
147
|
+
* struct. It is a discovery snapshot, not a live operating state: it carries
|
|
148
|
+
* capability and identity fields but not per-connection operator state
|
|
149
|
+
* (`layers`, `renderWidth`, `panYaw`, etc.) -- those arrive over the data
|
|
150
|
+
* channel after `connect()`.
|
|
151
|
+
*
|
|
152
|
+
* Deliberately separate from `CameraState` even though the two share several
|
|
153
|
+
* field names, because the shapes differ: `DiscoveredCamera` has `maxWidth`
|
|
154
|
+
* / `maxHeight` (physical sensor limits) which `CameraState` omits, while
|
|
155
|
+
* `CameraState` adds runtime fields (`layers`, `renderWidth`, `panYaw`, ...).
|
|
156
|
+
*/
|
|
157
|
+
export interface DiscoveredCamera {
|
|
158
|
+
flightId: number;
|
|
159
|
+
/** Part-destruction lifecycle. `"active"` for live cameras. */
|
|
160
|
+
lifecycle?: CameraLifecycle;
|
|
161
|
+
partName: string;
|
|
162
|
+
partTitle: string;
|
|
163
|
+
cameraName: string;
|
|
164
|
+
vesselName: string;
|
|
165
|
+
/** Physical sensor width ceiling (pixels). Distinct from `renderWidth` on
|
|
166
|
+
* `CameraState`, which is the currently-negotiated resolution. */
|
|
167
|
+
maxWidth: number;
|
|
168
|
+
/** Physical sensor height ceiling (pixels). */
|
|
169
|
+
maxHeight: number;
|
|
170
|
+
supportsZoom: boolean;
|
|
171
|
+
fov: number;
|
|
172
|
+
fovMin: number;
|
|
173
|
+
fovMax: number;
|
|
174
|
+
supportsPan: boolean;
|
|
175
|
+
panYawMin: number;
|
|
176
|
+
panYawMax: number;
|
|
177
|
+
panPitchMin: number;
|
|
178
|
+
panPitchMax: number;
|
|
179
|
+
/** Current encoder bitrate in bits per second. 0 when no encoder is running. */
|
|
180
|
+
encoderBitrateBps: number;
|
|
181
|
+
/** REMB-derived bandwidth target. 0 until the first feedback arrives. */
|
|
182
|
+
targetBitrateBps: number;
|
|
183
|
+
/** Effective degrade level (max across active subscribers). 0.0 to 1.0. */
|
|
184
|
+
degradeLevel: number;
|
|
185
|
+
}
|
|
186
|
+
/** Event payloads emitted by a {@link KerbcastClient}. */
|
|
187
|
+
export interface KerbcastClientEvents {
|
|
188
|
+
"state-change": KerbcastConnectionState;
|
|
189
|
+
"cameras-change": CameraState[];
|
|
190
|
+
"adaptive-shed": AdaptiveShedPayload;
|
|
191
|
+
error: ErrorPayload;
|
|
192
|
+
/**
|
|
193
|
+
* Fired whenever a `ping` keepalive arrives from the sidecar.
|
|
194
|
+
* Consumers can use this to reset their own staleness watchdogs
|
|
195
|
+
* without intercepting at the transport layer.
|
|
196
|
+
*/
|
|
197
|
+
ping: undefined;
|
|
198
|
+
/**
|
|
199
|
+
* Fired when a `settings-state` message arrives (after `Hello` and
|
|
200
|
+
* whenever the plugin-reported throttle state changes). Payload
|
|
201
|
+
* reflects what the plugin has applied, not just what was requested.
|
|
202
|
+
*/
|
|
203
|
+
"settings-change": SettingsStatePayload;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Pluggable WebRTC transport. Production code uses the default
|
|
207
|
+
* (browser `RTCPeerConnection`). Tests substitute an in-memory
|
|
208
|
+
* implementation to exercise the state machine without a real peer.
|
|
209
|
+
*/
|
|
210
|
+
export interface KerbcastTransport {
|
|
211
|
+
createPeer(iceServers: RTCIceServer[]): KerbcastPeer;
|
|
212
|
+
}
|
|
213
|
+
export interface KerbcastPeer {
|
|
214
|
+
addRecvOnlyTransceiver(): void;
|
|
215
|
+
createDataChannel(label: string): KerbcastDataChannel;
|
|
216
|
+
onTrack(handler: (track: MediaStreamTrack, idx: number, mid: string) => void): void;
|
|
217
|
+
onStateChange(handler: (state: KerbcastConnectionState) => void): void;
|
|
218
|
+
createOffer(): Promise<string>;
|
|
219
|
+
setLocalDescription(sdp: string): Promise<void>;
|
|
220
|
+
setRemoteAnswer(sdp: string): Promise<void>;
|
|
221
|
+
waitForIceComplete(): Promise<void>;
|
|
222
|
+
localSdp(): string;
|
|
223
|
+
close(): void;
|
|
224
|
+
/**
|
|
225
|
+
* Retrieve the raw WebRTC stats report. Optional so existing third-party
|
|
226
|
+
* transports and mocks that predate this method keep compiling without
|
|
227
|
+
* changes. Equivalent to `RTCPeerConnection.getStats(null)`.
|
|
228
|
+
*/
|
|
229
|
+
getStats?(): Promise<RTCStatsReport>;
|
|
230
|
+
}
|
|
231
|
+
export interface KerbcastDataChannel {
|
|
232
|
+
send(payload: string): void;
|
|
233
|
+
onOpen(handler: () => void): void;
|
|
234
|
+
onMessage(handler: (raw: string) => void): void;
|
|
235
|
+
onClose(handler: () => void): void;
|
|
236
|
+
}
|
|
237
|
+
/** Options for {@link BrowserKerbcastTransport}. */
|
|
238
|
+
export interface BrowserKerbcastTransportOptions {
|
|
239
|
+
/**
|
|
240
|
+
* Maximum time (ms) to wait for ICE gathering to complete before proceeding
|
|
241
|
+
* with whatever candidates have been gathered. Defaults to 2000 ms.
|
|
242
|
+
*
|
|
243
|
+
* On LAN paths where the STUN server is unreachable, gathering blocks until
|
|
244
|
+
* the STUN timeout elapses. Host candidates are all a local LAN stream
|
|
245
|
+
* needs, so this timeout lets connect proceed without waiting for STUN.
|
|
246
|
+
*/
|
|
247
|
+
iceGatheringTimeoutMs?: number;
|
|
248
|
+
}
|
|
249
|
+
export declare class BrowserKerbcastTransport implements KerbcastTransport {
|
|
250
|
+
private readonly opts;
|
|
251
|
+
constructor(opts?: BrowserKerbcastTransportOptions);
|
|
252
|
+
createPeer(iceServers: RTCIceServer[]): KerbcastPeer;
|
|
253
|
+
}
|
|
254
|
+
declare class TypedEmitter<E> {
|
|
255
|
+
private readonly listeners;
|
|
256
|
+
on<K extends keyof E>(event: K, handler: (data: E[K]) => void): () => void;
|
|
257
|
+
protected emit<K extends keyof E>(event: K, data: E[K]): void;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* High-level kerbcast sidecar client. Owns the WebRTC peer + the
|
|
261
|
+
* `kerbcast-control` data channel + the per-camera registry + the
|
|
262
|
+
* `MediaStream`s for each subscribed camera.
|
|
263
|
+
*
|
|
264
|
+
* Usage:
|
|
265
|
+
*
|
|
266
|
+
* ```ts
|
|
267
|
+
* const client = new KerbcastClient({ host: "192.168.1.74", port: 8088 });
|
|
268
|
+
* await client.connect();
|
|
269
|
+
*
|
|
270
|
+
* const cam = client.camera(2592004302);
|
|
271
|
+
* await cam.setFov(35);
|
|
272
|
+
* videoEl.srcObject = cam.mediaStream;
|
|
273
|
+
*
|
|
274
|
+
* cam.on("change", (state) => { ... });
|
|
275
|
+
* client.on("adaptive-shed", (event) => { ... });
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* Disconnect with {@link disconnect}. The handles returned by
|
|
279
|
+
* `camera()` remain valid across reconnects; their `mediaStream`
|
|
280
|
+
* goes null on disconnect and is replaced when the next connect's
|
|
281
|
+
* tracks arrive.
|
|
282
|
+
*/
|
|
283
|
+
export declare class KerbcastClient extends TypedEmitter<KerbcastClientEvents> {
|
|
284
|
+
private readonly cfg;
|
|
285
|
+
private readonly transport;
|
|
286
|
+
private peer;
|
|
287
|
+
private control;
|
|
288
|
+
private _state;
|
|
289
|
+
private _cameras;
|
|
290
|
+
private readonly handles;
|
|
291
|
+
private requestedOrder;
|
|
292
|
+
/** Set when `connect` is given an explicit slot pool. Switches track
|
|
293
|
+
* routing from legacy index order to mid-keyed dynamic slots. */
|
|
294
|
+
private dynamicMode;
|
|
295
|
+
/** Dynamic mode: transceiver mid -> the live track on that slot. */
|
|
296
|
+
private readonly trackByMid;
|
|
297
|
+
/** Dynamic mode: transceiver mid -> the camera bound to that slot
|
|
298
|
+
* (populated by SlotMap -- initial bindings announced on Hello). */
|
|
299
|
+
private readonly flightByMid;
|
|
300
|
+
/**
|
|
301
|
+
* Legacy mode: raw incoming track id -> flightId. Populated in handleTrack
|
|
302
|
+
* so inboundVideoStats() can match inbound-rtp report entries by
|
|
303
|
+
* trackIdentifier. Cleared on disconnect. Not used in dynamic mode (which
|
|
304
|
+
* matches by mid instead).
|
|
305
|
+
*/
|
|
306
|
+
private readonly flightByTrackId;
|
|
307
|
+
/** Sidecar version reported on `Hello`; null before handshake. */
|
|
308
|
+
private _sidecarVersion;
|
|
309
|
+
private _encoderBackend;
|
|
310
|
+
/** Last-received throttle state from `settings-state`. False until the first push. */
|
|
311
|
+
private _throttleMainScreen;
|
|
312
|
+
/**
|
|
313
|
+
* Mission-time capture clock from `settings-state`. `_captureUt` is null
|
|
314
|
+
* until the first clock arrives (and against an old sidecar that never
|
|
315
|
+
* sends one), so consumers treat it as "no clock". `_captureEpoch` bumps
|
|
316
|
+
* on discontinuities (revert / quickload / scene reload); only the change
|
|
317
|
+
* is meaningful. `_warpRate` defaults to 1 when the field is absent.
|
|
318
|
+
*/
|
|
319
|
+
private _captureUt;
|
|
320
|
+
private _captureEpoch;
|
|
321
|
+
private _warpRate;
|
|
322
|
+
constructor(cfg: KerbcastClientConfig, transport?: KerbcastTransport);
|
|
323
|
+
get state(): KerbcastConnectionState;
|
|
324
|
+
get cameras(): ReadonlyArray<CameraState>;
|
|
325
|
+
get sidecarVersion(): string | null;
|
|
326
|
+
get encoderBackend(): string | null;
|
|
327
|
+
/**
|
|
328
|
+
* Current effective state of the "Throttle KSP main render" setting.
|
|
329
|
+
* Reflects what the plugin has applied (from `settings-state` messages),
|
|
330
|
+
* not just what was last requested. False until the first push arrives.
|
|
331
|
+
*/
|
|
332
|
+
get throttleMainScreen(): boolean;
|
|
333
|
+
/**
|
|
334
|
+
* Mission-time capture clock, from the sidecar's ~1Hz `settings-state`.
|
|
335
|
+
* `captureUt` is the KSP universal time (seconds) the current video was
|
|
336
|
+
* captured at, or null when no clock is known (old plugin/sidecar, or
|
|
337
|
+
* before the first push). `epoch` bumps on a UT discontinuity so a
|
|
338
|
+
* consumer can flush and resync. `warpRate` lets a consumer interpolate
|
|
339
|
+
* `captureUt` between samples; defaults to 1.
|
|
340
|
+
*/
|
|
341
|
+
get clock(): {
|
|
342
|
+
captureUt: number | null;
|
|
343
|
+
epoch: number;
|
|
344
|
+
warpRate: number;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Send a `set-throttle-main-screen` command to the sidecar, which writes
|
|
348
|
+
* `global.control.json` for the plugin to apply. The plugin persists the
|
|
349
|
+
* change in the per-save difficulty parameter. The resulting
|
|
350
|
+
* `settings-state` broadcast reflects the applied value.
|
|
351
|
+
*/
|
|
352
|
+
setThrottleMainScreen(enabled: boolean): Promise<void>;
|
|
353
|
+
/**
|
|
354
|
+
* Pre-handshake camera discovery via the sidecar's HTTP `/cameras`
|
|
355
|
+
* endpoint. Useful when the consumer wants to pick which cameras
|
|
356
|
+
* to subscribe to before opening a peer connection.
|
|
357
|
+
*/
|
|
358
|
+
discover(): Promise<DiscoveredCamera[]>;
|
|
359
|
+
/**
|
|
360
|
+
* Open the WebRTC peer and subscribe to cameras. Idempotent: calling
|
|
361
|
+
* `connect` while already connected disconnects first.
|
|
362
|
+
*
|
|
363
|
+
* Two modes:
|
|
364
|
+
* - **Legacy** (no `opts.slots`): one transceiver per requested camera;
|
|
365
|
+
* an empty array subscribes to every currently-known camera. Tracks
|
|
366
|
+
* route by m-line index. Unchanged behaviour.
|
|
367
|
+
* - **Dynamic** (`opts.slots` set): negotiates a pool of `opts.slots`
|
|
368
|
+
* recv-only video slots. `requestedCameras` are the *initial*
|
|
369
|
+
* subscription bound to the first slots; spare slots are filled at
|
|
370
|
+
* runtime via {@link subscribe}. Tracks route by transceiver mid
|
|
371
|
+
* (the sidecar announces bindings via SlotMap), so switching a slot's
|
|
372
|
+
* camera needs no renegotiation.
|
|
373
|
+
*/
|
|
374
|
+
connect(requestedCameras?: number[], opts?: {
|
|
375
|
+
slots?: number;
|
|
376
|
+
}): Promise<void>;
|
|
377
|
+
/** Default signaling: POST the offer to the sidecar's HTTP `/offer`. */
|
|
378
|
+
private httpNegotiate;
|
|
379
|
+
disconnect(): void;
|
|
380
|
+
/**
|
|
381
|
+
* Stable per-flight-id handle. Creates a new handle on first call
|
|
382
|
+
* for a given ID; returns the same handle on subsequent calls.
|
|
383
|
+
*/
|
|
384
|
+
camera(flightId: number): KerbcastCameraHandle;
|
|
385
|
+
/**
|
|
386
|
+
* Dynamic mode only: bind a camera to a free slot on the already-connected
|
|
387
|
+
* peer. The camera's stream appears on its handle once the sidecar answers
|
|
388
|
+
* with a SlotMap; the sidecar emits an `error` event if no slot is free. No
|
|
389
|
+
* renegotiation.
|
|
390
|
+
*/
|
|
391
|
+
subscribe(flightId: number): Promise<void>;
|
|
392
|
+
/**
|
|
393
|
+
* Dynamic mode only: release a camera's slot. Its handle's `mediaStream`
|
|
394
|
+
* goes null once the sidecar confirms with a SlotMap.
|
|
395
|
+
*/
|
|
396
|
+
unsubscribe(flightId: number): Promise<void>;
|
|
397
|
+
/**
|
|
398
|
+
* Internal — resolves the effective noise enabled state for a camera,
|
|
399
|
+
* merging the per-camera override (if any) with the client-level default.
|
|
400
|
+
*/
|
|
401
|
+
_resolveNoise(override: Partial<NoiseConfig> | null): boolean;
|
|
402
|
+
/**
|
|
403
|
+
* Internal — sends a typed `ClientMessage` over the control
|
|
404
|
+
* channel. Drops silently if the channel hasn't opened yet (and
|
|
405
|
+
* logs to console). Public on the camera handle, but consumers
|
|
406
|
+
* shouldn't need to call this directly.
|
|
407
|
+
*/
|
|
408
|
+
_send(msg: ClientMessage): Promise<void>;
|
|
409
|
+
private getOrCreateHandle;
|
|
410
|
+
private handleTrack;
|
|
411
|
+
private handleServerMessage;
|
|
412
|
+
/**
|
|
413
|
+
* Re-source a resurrected camera's handle from the track the client still
|
|
414
|
+
* holds for its slot. Called when {@link KerbcastCameraHandle._setState}
|
|
415
|
+
* reports a Destroyed -> Active transition: the sidecar rebinds the same
|
|
416
|
+
* track without a new slot-map, so nothing else drives the handle back out
|
|
417
|
+
* of the sourceless (static) state it entered on destroy.
|
|
418
|
+
*/
|
|
419
|
+
private _resourceHandle;
|
|
420
|
+
private setState;
|
|
421
|
+
/**
|
|
422
|
+
* Inbound RTP video statistics keyed by flight ID. Returns an empty map
|
|
423
|
+
* when not connected or when the transport does not implement `getStats`.
|
|
424
|
+
*
|
|
425
|
+
* Iterates the stats report and matches `inbound-rtp` + `kind === "video"`
|
|
426
|
+
* entries to cameras. Legacy mode matches by `trackIdentifier`; dynamic
|
|
427
|
+
* mode falls back to the `mid` field when `trackIdentifier` is absent
|
|
428
|
+
* (modern browsers include it; some implementations omit it).
|
|
429
|
+
*/
|
|
430
|
+
inboundVideoStats(): Promise<Map<number, InboundVideoStats>>;
|
|
431
|
+
private tearDownStreams;
|
|
432
|
+
}
|
|
433
|
+
export {};
|