grandi 1.0.0 → 1.2.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 +150 -77
- package/dist/index.d.mts +418 -33
- package/dist/index.mjs +188 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +34 -32
- package/binding.gyp +0 -200
- package/lib/.clang-format +0 -297
- package/lib/grandi.cc +0 -96
- package/lib/grandi_find.cc +0 -365
- package/lib/grandi_find.h +0 -41
- package/lib/grandi_receive.cc +0 -1155
- package/lib/grandi_receive.h +0 -63
- package/lib/grandi_routing.cc +0 -460
- package/lib/grandi_routing.h +0 -37
- package/lib/grandi_send.cc +0 -960
- package/lib/grandi_send.h +0 -45
- package/lib/grandi_util.cc +0 -323
- package/lib/grandi_util.h +0 -137
- package/prebuilds/darwin-arm64/grandi.node +0 -0
- package/prebuilds/darwin-arm64/libndi.dylib +0 -0
- package/prebuilds/darwin-x64/grandi.node +0 -0
- package/prebuilds/darwin-x64/libndi.dylib +0 -0
- package/prebuilds/linux-arm/grandi.node +0 -0
- package/prebuilds/linux-arm/libndi.so.6 +0 -0
- package/prebuilds/linux-arm64/grandi.node +0 -0
- package/prebuilds/linux-arm64/libndi.so.6 +0 -0
- package/prebuilds/linux-ia32/grandi.node +0 -0
- package/prebuilds/linux-ia32/libndi.so.6 +0 -0
- package/prebuilds/linux-x64/grandi.node +0 -0
- package/prebuilds/linux-x64/libndi.so.6 +0 -0
- package/prebuilds/win32-ia32/Processing.NDI.Lib.x86.dll +0 -0
- package/prebuilds/win32-ia32/grandi.node +0 -0
- package/prebuilds/win32-x64/Processing.NDI.Lib.x64.dll +0 -0
- package/prebuilds/win32-x64/grandi.node +0 -0
- package/scripts/preinstall.mjs +0 -429
- package/src/index.ts +0 -141
- package/src/node-gyp-build.d.ts +0 -5
- package/src/types.ts +0 -228
package/README.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/grandi) [](https://github.com/tux-tn/grandi/actions/workflows/ci.yml)
|
|
2
|
+
|
|
1
3
|
# Grandi
|
|
4
|
+
|
|
2
5
|
Grandi is a TypeScript-first native Node.js binding for NewTek NDI™ 6. It exposes the full NDI 6 SDK surface area through a Promise-based API, providing strongly typed helpers for sending and receiving professional video, audio, metadata, and tally data from Node.js applications. For more information on NDI™ , see <https://www.ndi.tv/>.
|
|
3
6
|
|
|
4
7
|
## Why this fork?
|
|
@@ -30,16 +33,16 @@ NDI™ was conceived as a grand vision for IP media transport. The earliest bind
|
|
|
30
33
|
- Pull requests tackling these gaps (or other NDI 6.x+ features) are very welcome.
|
|
31
34
|
|
|
32
35
|
## Supported platforms
|
|
33
|
-
Grandi
|
|
36
|
+
Grandi publishes per-architecture packages (`@grandi/<os>-<arch>`) that bundle the native addon and the matching NDI SDK runtime. Additional platforms can be compiled from source if the NDI SDK supports them.
|
|
34
37
|
|
|
35
38
|
| Operating system | Architectures | Status | Notes |
|
|
36
39
|
| --- | --- | --- | --- |
|
|
37
|
-
| Windows | x86, x64 | ✅
|
|
38
|
-
| macOS | Universal (x64 + arm64) | ✅
|
|
39
|
-
| Linux | x86, x64, armv7l, arm64 | ✅
|
|
40
|
+
| Windows | x86, x64 | ✅ Published as `@grandi/win32-ia32` and `@grandi/win32-x64`. Visual Studio 2013 C runtime only needed when building locally. |
|
|
41
|
+
| macOS | Universal (x64 + arm64) | ✅ Published as `@grandi/darwin-universal`, built against the official universal NDI™ runtime. |
|
|
42
|
+
| Linux | x86, x64, armv7l, arm64 | ✅ Published as `@grandi/linux-x64`, `@grandi/linux-arm64`, `@grandi/linux-armv7l`. Built against the glibc-based NDI™ SDK; requires `libavahi-common.so.3`, `libavahi-client.so.3`, and the `avahi-daemon` service. |
|
|
40
43
|
|
|
41
44
|
## Installation
|
|
42
|
-
Install [Node.js](http://nodejs.org/) for your platform (tested against the current Long Term Support release).
|
|
45
|
+
Install [Node.js](http://nodejs.org/) for your platform (tested against the current Long Term Support release). The main package automatically pulls the platform-specific optional dependency `@grandi/<os>-<arch>` when it exists, and falls back to building from source otherwise. Add the dependency to your project:
|
|
43
46
|
|
|
44
47
|
```bash
|
|
45
48
|
npm install grandi
|
|
@@ -49,18 +52,18 @@ pnpm add grandi
|
|
|
49
52
|
yarn add grandi
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
On Windows, you only need the Visual Studio 2013 C run-times when building from source (e.g., running `npm install`
|
|
55
|
+
On Windows, you only need the Visual Studio 2013 C run-times when building from source (e.g., running `npm install` on an unsupported platform). You can grab them from <https://www.microsoft.com/en-us/download/details.aspx?id=40784>; consumers using the published `@grandi/win32-*` packages do **not** need to install them manually.
|
|
53
56
|
|
|
54
57
|
Grandi is designed to be `require`d or `import`ed from your own applications:
|
|
55
58
|
|
|
56
|
-
```
|
|
59
|
+
```ts
|
|
57
60
|
import grandi from "grandi";
|
|
58
61
|
// or
|
|
59
62
|
const grandi = require("grandi");
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
## Build & distribution
|
|
63
|
-
|
|
66
|
+
Each platform package under the `@grandi/` scope bundles the native addon (`grandi.node`) and the corresponding NDI SDK runtime for that OS/arch. The root `grandi` package loads the matching optional dependency first and only compiles locally when no published package is available. Maintainers regenerate those packages from the official NDI SDK by running `node scripts/preinstall.mjs` (to download and stage the SDK) followed by a version bump (e.g., `node scripts/bump-version.mjs <version>`).
|
|
64
67
|
|
|
65
68
|
## Using Grandi
|
|
66
69
|
This module allows a Node.js program to find, receive, and send NDI™ video, audio, metadata, and tally streams over IP networks. All calls are asynchronous and use JavaScript promises with all of the underlying work of NDI running on separate threads from the event loop. The following sections recap the most common workflows; for complete runnable demos, see `examples/simple-receiver.mjs` and `examples/simple-sender.mjs`.
|
|
@@ -68,7 +71,7 @@ This module allows a Node.js program to find, receive, and send NDI™ video, au
|
|
|
68
71
|
### Finding streams
|
|
69
72
|
`grandi.find` resolves to a finder handle that can block for network updates and expose the discovered sources:
|
|
70
73
|
|
|
71
|
-
```
|
|
74
|
+
```ts
|
|
72
75
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
73
76
|
import grandi from "grandi";
|
|
74
77
|
|
|
@@ -90,7 +93,7 @@ async function pickSource() {
|
|
|
90
93
|
|
|
91
94
|
`find` accepts a single options object. Common filters:
|
|
92
95
|
|
|
93
|
-
```
|
|
96
|
+
```ts
|
|
94
97
|
await grandi.find({
|
|
95
98
|
showLocalSources: true, // include sources on the same machine
|
|
96
99
|
groups: "studio3", // comma-separated list of group names
|
|
@@ -101,20 +104,20 @@ await grandi.find({
|
|
|
101
104
|
### Receiving streams
|
|
102
105
|
First, find a stream or construct a `Source` object:
|
|
103
106
|
|
|
104
|
-
```
|
|
107
|
+
```ts
|
|
105
108
|
const grandi = require("grandi");
|
|
106
109
|
const source = { name: "<source_name>", urlAddress: "<ip>:<port>" };
|
|
107
110
|
```
|
|
108
111
|
|
|
109
112
|
Create a receiver:
|
|
110
113
|
|
|
111
|
-
```
|
|
114
|
+
```ts
|
|
112
115
|
const receiver = await grandi.receive({ source });
|
|
113
116
|
```
|
|
114
117
|
|
|
115
118
|
Example receiver object:
|
|
116
119
|
|
|
117
|
-
```
|
|
120
|
+
```ts
|
|
118
121
|
{
|
|
119
122
|
embedded: [External],
|
|
120
123
|
video: [Function],
|
|
@@ -130,7 +133,7 @@ Example receiver object:
|
|
|
130
133
|
|
|
131
134
|
Configure receivers via options:
|
|
132
135
|
|
|
133
|
-
```
|
|
136
|
+
```ts
|
|
134
137
|
const receiver = await grandi.receive({
|
|
135
138
|
source,
|
|
136
139
|
colorFormat: grandi.COLOR_FORMAT_UYVY_RGBA,
|
|
@@ -143,7 +146,7 @@ const receiver = await grandi.receive({
|
|
|
143
146
|
|
|
144
147
|
#### Video
|
|
145
148
|
|
|
146
|
-
```
|
|
149
|
+
```ts
|
|
147
150
|
const timeout = 5000;
|
|
148
151
|
try {
|
|
149
152
|
for (let i = 0; i < 10; i++) {
|
|
@@ -157,7 +160,7 @@ try {
|
|
|
157
160
|
|
|
158
161
|
Example frame:
|
|
159
162
|
|
|
160
|
-
```
|
|
163
|
+
```ts
|
|
161
164
|
{
|
|
162
165
|
type: "video",
|
|
163
166
|
xres: 1920,
|
|
@@ -175,7 +178,7 @@ Example frame:
|
|
|
175
178
|
|
|
176
179
|
#### Audio
|
|
177
180
|
|
|
178
|
-
```
|
|
181
|
+
```ts
|
|
179
182
|
const audioFrame = await receiver.audio(
|
|
180
183
|
{
|
|
181
184
|
audioFormat: grandi.AUDIO_FORMAT_INT_16_INTERLEAVED,
|
|
@@ -187,7 +190,7 @@ const audioFrame = await receiver.audio(
|
|
|
187
190
|
|
|
188
191
|
Example result:
|
|
189
192
|
|
|
190
|
-
```
|
|
193
|
+
```ts
|
|
191
194
|
{
|
|
192
195
|
type: "audio",
|
|
193
196
|
audioFormat: grandi.AUDIO_FORMAT_INT_16_INTERLEAVED,
|
|
@@ -204,7 +207,7 @@ Example result:
|
|
|
204
207
|
|
|
205
208
|
#### Metadata
|
|
206
209
|
|
|
207
|
-
```
|
|
210
|
+
```ts
|
|
208
211
|
const metadataFrame = await receiver.metadata();
|
|
209
212
|
```
|
|
210
213
|
|
|
@@ -212,7 +215,7 @@ Returns a `{ type: "metadata", data: string, ... }` frame, typically XML.
|
|
|
212
215
|
|
|
213
216
|
#### Next available data
|
|
214
217
|
|
|
215
|
-
```
|
|
218
|
+
```ts
|
|
216
219
|
const prefs = { audioFormat: grandi.AUDIO_FORMAT_FLOAT_32_SEPARATE };
|
|
217
220
|
const payload = await receiver.data(prefs, 1000); // 1s timeout
|
|
218
221
|
if (payload.type === "video") {
|
|
@@ -222,10 +225,31 @@ if (payload.type === "video") {
|
|
|
222
225
|
}
|
|
223
226
|
```
|
|
224
227
|
|
|
228
|
+
### Frame synchronization (pull-based playback)
|
|
229
|
+
If you want smoother playback clocked to your own loop (e.g., GPU vsync) instead of reacting to pushed frames, wrap an existing receiver in an NDI frame-synchronizer:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const receiver = await grandi.receive({ source, colorFormat: grandi.ColorFormat.Fastest });
|
|
233
|
+
const fs = await grandi.framesync(receiver);
|
|
234
|
+
|
|
235
|
+
// Pull video; returns { type: "timeout" } until first frame arrives.
|
|
236
|
+
const video = await fs.video(grandi.FrameType.Progressive);
|
|
237
|
+
|
|
238
|
+
// Pull audio resampled to your requested cadence.
|
|
239
|
+
const audio = await fs.audio({ sampleRate: 48_000, noChannels: 2, noSamples: 1600 });
|
|
240
|
+
|
|
241
|
+
fs.destroy(); // destroy frame-sync first
|
|
242
|
+
receiver.destroy(); // then destroy the receiver
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Notes:
|
|
246
|
+
- `fs.video()` and `fs.audio()` always return immediately; they may duplicate/drop frames to match your call rate.
|
|
247
|
+
- Always destroy the frame-sync before destroying the receiver it wraps.
|
|
248
|
+
|
|
225
249
|
### Sending streams
|
|
226
250
|
Sending is fully supported. Call `grandi.send` with a `SendOptions` object to create a `Sender` instance capable of video, audio, metadata, and tally interactions:
|
|
227
251
|
|
|
228
|
-
```
|
|
252
|
+
```ts
|
|
229
253
|
const sender = await grandi.send({
|
|
230
254
|
name: "grandi-demo",
|
|
231
255
|
groups: "studio3",
|
|
@@ -281,32 +305,60 @@ Destroy senders and receivers explicitly when finished to release NDI resources.
|
|
|
281
305
|
- `grandi.isSupportedCPU()` checks whether the host CPU can run the NDI SDK.
|
|
282
306
|
- `grandi.initialize()` / `grandi.destroy()` control the lifetime of the underlying library in advanced scenarios.
|
|
283
307
|
|
|
308
|
+
## Benchmark
|
|
309
|
+
A loopback benchmark script measures end-to-end send/receive throughput and latency on your machine. It spins up a sender and receiver locally, so a working NDI setup with discovery is required.
|
|
310
|
+
|
|
311
|
+
- Build first if needed (`npm run build`), then run `npm run bench -- [options]`. The script defaults to 1080p30 with audio and colored output.
|
|
312
|
+
|
|
313
|
+
| Option | Default | Description |
|
|
314
|
+
| --- | --- | --- |
|
|
315
|
+
| `--mode realtime|throughput` | `realtime` | `realtime` keeps to the target FPS with clocking enabled; `throughput` sends as fast as possible. |
|
|
316
|
+
| `--duration <sec>` | `5` | Run length in seconds. |
|
|
317
|
+
| `--fps <num>` | `30` | Target frames per second. |
|
|
318
|
+
| `--width <px>` | `1920` | Video width in pixels. |
|
|
319
|
+
| `--height <px>` | `1080` | Video height in pixels. |
|
|
320
|
+
| `--no-audio` | audio on | Disable sending/receiving audio. |
|
|
321
|
+
| `--framesync` | off | Pull frames via the NDI frame-sync API for smoother capture. |
|
|
322
|
+
| `--gc-every <ms>` | `500` | Force `global.gc()` at this interval when `--expose-gc` is enabled. |
|
|
323
|
+
| `--no-color` | color on | Disable ANSI color output. |
|
|
324
|
+
|
|
325
|
+
`npm run bench` adds `--expose-gc` to keep buffer reuse stable during long runs.
|
|
326
|
+
|
|
327
|
+
Example (10s, 1080p30, realtime):
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
npm run bench -- --duration 10 --mode realtime --fps 30 --framesync
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Example (throughput stress, 720p, video only):
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npm run bench -- --mode throughput --width 1280 --height 720 --no-audio --no-color
|
|
337
|
+
```
|
|
338
|
+
|
|
284
339
|
## Contributing
|
|
285
340
|
Ready to hack on Grandi? Here’s the typical workflow.
|
|
286
341
|
|
|
287
342
|
1. **Install prerequisites**
|
|
288
343
|
- Node.js ≥ 20.19.5 and a working C/C++ toolchain for your platform.
|
|
289
344
|
- Git LFS is *not* required; the NDI SDK is fetched dynamically.
|
|
290
|
-
2. **Download the NDI SDK
|
|
291
|
-
- Run `
|
|
292
|
-
-
|
|
293
|
-
- Set `NDI_FORCE=1` if you need to run the downloader in an unpacked tarball (normally it only runs inside the git repo).
|
|
345
|
+
2. **Download the NDI SDK (maintainers)**
|
|
346
|
+
- Run `node scripts/build-addon.mjs` to download and unpack the official NDI SDK into `ndi/` and populate the per-arch package folders under `packages/` with the right runtime files.
|
|
347
|
+
- Consumers don’t need this step; they install the appropriate `@grandi/<os>-<arch>` package from npm automatically.
|
|
294
348
|
3. **TypeScript build**
|
|
295
349
|
- `npm run build` compiles `src/` via `tsdown`, emitting ESM/CJS bundles and declaration files in `dist/`.
|
|
296
|
-
- `npm run prebuild` packages native binaries for distribution (requires the SDK assets fetched earlier).
|
|
297
350
|
4. **Native addon rebuild**
|
|
298
351
|
- If you change C/C++ files under `lib/`, recompile with `npx node-gyp-build` (or simply re-run `npm install`). This uses the same loader that consumers invoke at runtime.
|
|
299
352
|
5. **Testing**
|
|
300
353
|
- `npm test` runs the full Vitest suite (unit + integration stubs).
|
|
301
354
|
- `npm run test:unit` focuses on pure JS/TS tests.
|
|
302
|
-
- `
|
|
355
|
+
- `npm run test:integration` exercises the native bindings against a real NDI environment;
|
|
303
356
|
- `npm run test:coverage` provides coverage data via `@vitest/coverage-v8`.
|
|
304
357
|
6. **Linting & formatting**
|
|
305
358
|
- `npm run lint` / `npm run format` cover the TypeScript/JavaScript sources via Biome.
|
|
306
359
|
- `npm run format:cpp` formats the native sources with `clang-format`.
|
|
307
360
|
7. **Manual verification**
|
|
308
361
|
- `node examples/simple-sender.mjs` and `node examples/simple-receiver.mjs` are quick smoke tests for the send/receive API.
|
|
309
|
-
- Use `NDI_FORCE=1 npm install` if you need to reassemble the NDI SDK artifacts even when they are already present.
|
|
310
362
|
|
|
311
363
|
Before opening a pull request, make sure the linter, formatter, and tests all pass, and include context for any platform-specific considerations (e.g., SDK versions, OS dependencies).
|
|
312
364
|
|
|
@@ -314,70 +366,91 @@ Before opening a pull request, make sure the linter, formatter, and tests all pa
|
|
|
314
366
|
This section documents every exported method and type surfaced by the module. Refer to `src/index.ts` and `src/types.ts` for authoritative definitions.
|
|
315
367
|
|
|
316
368
|
### Module exports
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
369
|
+
|
|
370
|
+
| Export | Returns | Purpose |
|
|
371
|
+
| --- | --- | --- |
|
|
372
|
+
| `find(options?: FindOptions)` | `Promise<Finder>` | Discover available NDI sources, optionally filtering by groups, local visibility, or explicit IPs. Resolves to a finder whose `sources()` method exposes the latest snapshot. |
|
|
373
|
+
| `receive(options: ReceiveOptions)` | `Promise<Receiver>` | Create a receiver bound to a specific `Source` with optional color format, bandwidth, interlaced, and naming tweaks. |
|
|
374
|
+
| `send(options: SendOptions)` | `Promise<Sender>` | Create a sender that can push video, audio, metadata, and tally updates into the NDI network. |
|
|
375
|
+
| `routing(params: { name?: string; groups?: string })` | `Promise<Routing>` | Build an NDI router that can switch downstream destinations to new sources via `routing.change`. |
|
|
376
|
+
| `initialize()` / `destroy()` | `boolean` | Manually initialize or tear down the shared NDI state; normally handled automatically by `send`/`receive`. |
|
|
377
|
+
| `version()` | `string` | Report the bundled NDI SDK version. |
|
|
378
|
+
| `isSupportedCPU()` | `boolean` | Guard call to confirm the host CPU meets the NDI SDK requirements. |
|
|
379
|
+
| `ColorFormat`, `AudioFormat`, `Bandwidth`, `FrameType`, `FourCC` | enums | Enumerations re-exported for convenience (also mirrored as constants on the default export). |
|
|
380
|
+
| `default` | `typeof grandi` | The `grandi` object containing the methods above plus constant aliases such as `COLOR_FORMAT_FASTEST`, `BANDWIDTH_HIGHEST`, etc. |
|
|
326
381
|
|
|
327
382
|
### Receiver interface
|
|
328
383
|
`Receiver` instances expose:
|
|
329
384
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
385
|
+
| Call | Returns | Purpose |
|
|
386
|
+
| --- | --- | --- |
|
|
387
|
+
| `video(timeoutMs?: number)` | `Promise<ReceivedVideoFrame>` | Resolve with the next available video frame. |
|
|
388
|
+
| `audio(optionsOrTimeout?: AudioReceiveOptions \| number, timeoutMs?: number)` | `Promise<ReceivedAudioFrame>` | Fetch audio, overriding format/reference level per call if desired. |
|
|
389
|
+
| `metadata(timeoutMs?: number)` | `Promise<ReceivedMetadataFrame>` | Receive metadata frames (XML strings). |
|
|
390
|
+
| `data(optionsOrTimeout?: AudioReceiveOptions \| number, timeoutMs?: number)` | `Promise<ReceiverDataFrame>` | Return whichever payload (video/audio/metadata/source change/status change) arrives first. |
|
|
391
|
+
| `tally(state: ReceiverTallyState)` | `boolean` | Push tally states (program/preview) upstream to the sender. |
|
|
392
|
+
| `destroy()` | `boolean` | Release the underlying NDI receiver resources. |
|
|
393
|
+
|
|
394
|
+
Properties: `embedded`, `source`, `colorFormat`, `bandwidth`, `allowVideoFields`, `name`.
|
|
337
395
|
|
|
338
396
|
### Sender interface
|
|
339
397
|
`Sender` instances surface:
|
|
340
398
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
399
|
+
| Call | Returns | Purpose |
|
|
400
|
+
| --- | --- | --- |
|
|
401
|
+
| `video(frame: VideoFrame)` | `Promise<void>` | Send a video frame into the NDI network. |
|
|
402
|
+
| `audio(frame: AudioFrame)` | `Promise<void>` | Send an audio frame. |
|
|
403
|
+
| `metadata(data: string)` | `boolean` | Push metadata strings (XML). |
|
|
404
|
+
| `tally()` | `SenderTally` | Retrieve current tally state (program/preview booleans plus `changed`). |
|
|
405
|
+
| `connections()` | `number` | Report how many receivers are actively connected. |
|
|
406
|
+
| `sourcename()` | `string` | Fully qualified source name visible on the network. |
|
|
407
|
+
| `destroy()` | `boolean` | Dispose the sender and release network resources. |
|
|
408
|
+
|
|
409
|
+
Properties: `embedded`, `name`, `groups`, `clockVideo`, `clockAudio`.
|
|
349
410
|
|
|
350
411
|
### Routing interface
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
412
|
+
|
|
413
|
+
| Call | Returns | Purpose |
|
|
414
|
+
| --- | --- | --- |
|
|
415
|
+
| `change(source: Source)` | `boolean` | Switch downstream destinations to a new source. |
|
|
416
|
+
| `clear()` | `boolean` | Disconnect the current source. |
|
|
417
|
+
| `connections()` | `number` | Monitor downstream subscriptions. |
|
|
418
|
+
| `sourcename()` | `string` | Current routed source name. |
|
|
419
|
+
| `destroy()` | `boolean` | Cleanly shut down the router. |
|
|
356
420
|
|
|
357
421
|
### Finder interface
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
422
|
+
|
|
423
|
+
| Call | Returns | Purpose |
|
|
424
|
+
| --- | --- | --- |
|
|
425
|
+
| `sources()` | `Source[]` | Latest snapshot of discovered sources. |
|
|
426
|
+
| `wait(timeoutMs?: number)` | `boolean` | Block for network updates up to the given timeout. |
|
|
427
|
+
| `destroy()` | `boolean` | Dispose the finder and background threads. |
|
|
361
428
|
|
|
362
429
|
### Options and helper types
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
430
|
+
|
|
431
|
+
| Type | Shape | Notes |
|
|
432
|
+
| --- | --- | --- |
|
|
433
|
+
| `Source` | `{ name: string; urlAddress?: string }` | Identifies an NDI endpoint. |
|
|
434
|
+
| `FindOptions` | `{ showLocalSources?: boolean; groups?: string; extraIPs?: string; }` | Filters applied when discovering sources. |
|
|
435
|
+
| `ReceiveOptions` | `{ source: Source; colorFormat?: ColorFormat; bandwidth?: Bandwidth; allowVideoFields?: boolean; name?: string; }` | Required to create receivers with optional overrides. |
|
|
436
|
+
| `SendOptions` | `{ name: string; groups?: string; clockVideo?: boolean; clockAudio?: boolean; }` | Configure sender identity and sync behavior. |
|
|
437
|
+
| `AudioReceiveOptions` | `{ audioFormat?: AudioFormat; referenceLevel?: number; }` | Used when calling `receiver.audio` or `receiver.data`. |
|
|
438
|
+
| `ReceiverTallyState` | `{ onProgram?: boolean; onPreview?: boolean; }` | Provided to `receiver.tally` to reflect monitoring state. |
|
|
439
|
+
| `SenderTally` | `{ changed: boolean; on_program: boolean; on_preview: boolean; }` | Returned by `sender.tally`. |
|
|
440
|
+
| `PtpTimestamp` | `[seconds, nanoseconds]` | Tuple aligning with the NDI SDK timestamp APIs. |
|
|
441
|
+
| `Timecode` | `bigint \| number \| PtpTimestamp` | Matches how timecodes are expressed throughout the SDK. |
|
|
372
442
|
|
|
373
443
|
### Frame payload types
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
444
|
+
|
|
445
|
+
| Type | Description |
|
|
446
|
+
| --- | --- |
|
|
447
|
+
| `VideoFrame` | Outbound frame with resolution, frame rate (`frameRateN`/`frameRateD`), aspect ratio, `fourCC`, `frameFormatType`, `lineStrideBytes`, `data`, optional `timecode`, `timestamp`, `metadata`. |
|
|
448
|
+
| `ReceivedVideoFrame` | `VideoFrame` plus `type: "video"` and non-optional `timecode`/`timestamp`. |
|
|
449
|
+
| `AudioFrame` | Outbound audio payload including sample rate, channels/samples, stride, `data`, `fourCC`, optional timing/metadata. |
|
|
450
|
+
| `ReceivedAudioFrame` | Inbound audio payload with `audioFormat`, `referenceLevel`, `timecode`, `timestamp`. |
|
|
451
|
+
| `ReceivedMetadataFrame` | Metadata payload (`type: "metadata"`, `length`, `timecode`, `timestamp`, `data` string). |
|
|
452
|
+
| `ReceiverDataFrame` | Discriminated union of `ReceivedVideoFrame \| ReceivedAudioFrame \| ReceivedMetadataFrame \| SourceChangeEvent \| StatusChangeEvent`. |
|
|
453
|
+
| `SourceChangeEvent` / `StatusChangeEvent` | Internal notifications delivered via `receiver.data` when the remote sender changes or becomes unavailable. |
|
|
381
454
|
|
|
382
455
|
### Enum reference
|
|
383
456
|
- `ColorFormat` — enumerates `BGRX_BGRA`, `UYVY_BGRA`, `RGBX_RGBA`, `UYVY_RGBA`, `Fastest`, `Best`, and `BGRX_BGRA_FLIPPED`. The `grandi.COLOR_FORMAT_*` constants map to these values.
|
|
@@ -396,7 +469,7 @@ Apart from the exceptions below, this software is released under the Apache 2.0
|
|
|
396
469
|
The software uses libraries provided under a royalty-free license from NewTek, Inc. (see the [NDI SDK License](https://ndi.link/ndisdk_license) for full terms):
|
|
397
470
|
|
|
398
471
|
- The `ndi/include` includes files are licensed separately by NewTek under the MIT license.
|
|
399
|
-
- The DLL and library files are provided for installation convenience and are covered by the NewTek license in the `
|
|
472
|
+
- The DLL and library files are provided for installation convenience and are covered by the NewTek license in the scoped packages under `packages/` (published as `@grandi/*`).
|
|
400
473
|
|
|
401
474
|
## Trademarks
|
|
402
475
|
NDI™ is a trademark of NewTek, Inc.
|