@matbee/remotemedia-native 0.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/index.js ADDED
@@ -0,0 +1,138 @@
1
+ // @remotemedia/native - Node.js bindings for RemoteMedia zero-copy IPC
2
+ //
3
+ // This module provides zero-copy IPC between Node.js, Python, and Rust
4
+ // via iceoryx2 shared memory.
5
+
6
+ const { existsSync, readFileSync } = require('fs');
7
+ const { join } = require('path');
8
+
9
+ const { platform, arch } = process;
10
+
11
+ let nativeBinding = null;
12
+ let loadError = null;
13
+
14
+ // Platform-specific binary name mapping
15
+ function getPlatformTriple() {
16
+ const archMap = {
17
+ 'x64': 'x86_64',
18
+ 'arm64': 'aarch64',
19
+ };
20
+
21
+ const platformMap = {
22
+ 'darwin': 'apple-darwin',
23
+ 'linux': 'unknown-linux-gnu',
24
+ 'win32': 'pc-windows-msvc',
25
+ };
26
+
27
+ const mappedArch = archMap[arch];
28
+ const mappedPlatform = platformMap[platform];
29
+
30
+ if (!mappedArch || !mappedPlatform) {
31
+ return null;
32
+ }
33
+
34
+ return `${mappedArch}-${mappedPlatform}`;
35
+ }
36
+
37
+ // Try to load the native binding
38
+ function loadNativeBinding() {
39
+ const triple = getPlatformTriple();
40
+
41
+ if (!triple) {
42
+ throw new Error(
43
+ `Unsupported platform: ${platform}-${arch}. ` +
44
+ `Supported platforms: darwin-x64, darwin-arm64, linux-x64, linux-arm64`
45
+ );
46
+ }
47
+
48
+ // Try loading from various locations
49
+ const candidates = [
50
+ // Local development build (with .node extension renamed from .so)
51
+ join(__dirname, '..', '..', '..', 'target', 'release', 'remotemedia_native.node'),
52
+ join(__dirname, '..', '..', '..', 'target', 'debug', 'remotemedia_native.node'),
53
+ // Original .so file (Linux)
54
+ join(__dirname, '..', '..', '..', 'target', 'release', 'libremotemedia_ffi.so'),
55
+ join(__dirname, '..', '..', '..', 'target', 'debug', 'libremotemedia_ffi.so'),
56
+ // Platform-specific builds (for npm distribution)
57
+ join(__dirname, `remotemedia-native.${triple}.node`),
58
+ join(__dirname, 'remotemedia-native.node'),
59
+ // Fallback to generic name
60
+ join(__dirname, 'index.node'),
61
+ ];
62
+
63
+ for (const candidate of candidates) {
64
+ try {
65
+ if (existsSync(candidate)) {
66
+ return require(candidate);
67
+ }
68
+ } catch (e) {
69
+ // Continue to next candidate
70
+ }
71
+ }
72
+
73
+ // Try requiring without path (for globally installed)
74
+ try {
75
+ return require(`@remotemedia/native-${triple}`);
76
+ } catch (e) {
77
+ // Ignore
78
+ }
79
+
80
+ throw new Error(
81
+ `Failed to load native binding for ${platform}-${arch}. ` +
82
+ `Make sure you have built the native addon with 'npm run build'. ` +
83
+ `Tried: ${candidates.join(', ')}`
84
+ );
85
+ }
86
+
87
+ try {
88
+ nativeBinding = loadNativeBinding();
89
+ } catch (e) {
90
+ loadError = e;
91
+ }
92
+
93
+ // Export all bindings
94
+ if (nativeBinding) {
95
+ module.exports = nativeBinding;
96
+ } else {
97
+ // Create stub exports that throw helpful errors
98
+ const createStub = (name) => () => {
99
+ throw loadError || new Error(`Native binding not loaded: ${name}`);
100
+ };
101
+
102
+ module.exports = {
103
+ // Session management
104
+ createSession: createStub('createSession'),
105
+ getSession: createStub('getSession'),
106
+ listSessions: createStub('listSessions'),
107
+
108
+ // Node management
109
+ createIpcNode: createStub('createIpcNode'),
110
+
111
+ // Classes (will throw on instantiation)
112
+ NapiSession: class { constructor() { createStub('NapiSession')(); } },
113
+ NapiChannel: class { constructor() { createStub('NapiChannel')(); } },
114
+ NapiPublisher: class { constructor() { createStub('NapiPublisher')(); } },
115
+ NapiSubscriber: class { constructor() { createStub('NapiSubscriber')(); } },
116
+ ReceivedSample: class { constructor() { createStub('ReceivedSample')(); } },
117
+ LoanedSample: class { constructor() { createStub('LoanedSample')(); } },
118
+ IpcNode: class { constructor() { createStub('IpcNode')(); } },
119
+
120
+ // RuntimeData helpers
121
+ parseRuntimeDataHeader: createStub('parseRuntimeDataHeader'),
122
+
123
+ // Error for debugging
124
+ _loadError: loadError,
125
+ };
126
+ }
127
+
128
+ // Export type helpers
129
+ module.exports.isNativeLoaded = () => nativeBinding !== null;
130
+ module.exports.getLoadError = () => loadError;
131
+
132
+ // Export proto-utils (browser/Node.js compatible)
133
+ const protoUtils = require('./proto-utils');
134
+ module.exports.protoUtils = protoUtils;
135
+ module.exports.encodeTextData = protoUtils.encodeTextData;
136
+ module.exports.encodeJsonData = protoUtils.encodeJsonData;
137
+ module.exports.decodeDataBuffer = protoUtils.decodeDataBuffer;
138
+ module.exports.parseJsonFromDataBuffer = protoUtils.parseJsonFromDataBuffer;
@@ -0,0 +1,343 @@
1
+ [
2
+ {
3
+ "nodeType": "CalculatorNode",
4
+ "description": "Performs arithmetic operations on JSON input",
5
+ "category": "utility",
6
+ "accepts": [
7
+ "json"
8
+ ],
9
+ "produces": [
10
+ "json"
11
+ ],
12
+ "parameters": [
13
+ {
14
+ "name": "precision",
15
+ "paramType": "integer",
16
+ "description": "Decimal precision for results",
17
+ "defaultValue": "10",
18
+ "required": false
19
+ }
20
+ ],
21
+ "configSchema": "{\"properties\":{\"precision\":{\"default\":10,\"description\":\"Decimal precision for results\",\"type\":\"integer\"}},\"type\":\"object\"}",
22
+ "isPython": false,
23
+ "streaming": true,
24
+ "multiOutput": false
25
+ },
26
+ {
27
+ "nodeType": "WhisperNode",
28
+ "description": "Speech-to-text transcription using Whisper",
29
+ "category": "ml",
30
+ "accepts": [
31
+ "audio"
32
+ ],
33
+ "produces": [
34
+ "text",
35
+ "json"
36
+ ],
37
+ "parameters": [
38
+ {
39
+ "name": "language",
40
+ "paramType": "string",
41
+ "description": "Language code (null for auto-detect)",
42
+ "required": false
43
+ },
44
+ {
45
+ "name": "model",
46
+ "paramType": "string",
47
+ "description": "Whisper model size",
48
+ "defaultValue": "\"base\"",
49
+ "required": false,
50
+ "enumValues": "[\"tiny\",\"base\",\"small\",\"medium\",\"large\",\"large-v3\"]"
51
+ },
52
+ {
53
+ "name": "task",
54
+ "paramType": "string",
55
+ "description": "Task type",
56
+ "defaultValue": "\"transcribe\"",
57
+ "required": false,
58
+ "enumValues": "[\"transcribe\",\"translate\"]"
59
+ }
60
+ ],
61
+ "configSchema": "{\"properties\":{\"language\":{\"description\":\"Language code (null for auto-detect)\",\"type\":\"string\"},\"model\":{\"default\":\"base\",\"description\":\"Whisper model size\",\"enum\":[\"tiny\",\"base\",\"small\",\"medium\",\"large\",\"large-v3\"],\"type\":\"string\"},\"task\":{\"default\":\"transcribe\",\"description\":\"Task type\",\"enum\":[\"transcribe\",\"translate\"],\"type\":\"string\"}},\"type\":\"object\"}",
62
+ "isPython": true,
63
+ "streaming": true,
64
+ "multiOutput": false,
65
+ "capabilities": {
66
+ "parallelizable": false,
67
+ "batchAware": true,
68
+ "supportsControl": false,
69
+ "latencyClass": 4
70
+ }
71
+ },
72
+ {
73
+ "nodeType": "AudioChunker",
74
+ "description": "Splits audio into fixed-size chunks",
75
+ "category": "audio",
76
+ "accepts": [
77
+ "audio"
78
+ ],
79
+ "produces": [
80
+ "audio"
81
+ ],
82
+ "parameters": [
83
+ {
84
+ "name": "chunk_size_ms",
85
+ "paramType": "integer",
86
+ "description": "Chunk duration in milliseconds",
87
+ "defaultValue": "20",
88
+ "required": false
89
+ }
90
+ ],
91
+ "configSchema": "{\"properties\":{\"chunk_size_ms\":{\"default\":20,\"description\":\"Chunk duration in milliseconds\",\"type\":\"integer\"}},\"type\":\"object\"}",
92
+ "isPython": false,
93
+ "streaming": true,
94
+ "multiOutput": true
95
+ },
96
+ {
97
+ "nodeType": "PassThrough",
98
+ "description": "Passes input through unchanged",
99
+ "category": "utility",
100
+ "accepts": [
101
+ "audio",
102
+ "video",
103
+ "json",
104
+ "text",
105
+ "binary",
106
+ "tensor",
107
+ "numpy",
108
+ "controlmessage"
109
+ ],
110
+ "produces": [
111
+ "audio",
112
+ "video",
113
+ "json",
114
+ "text",
115
+ "binary",
116
+ "tensor",
117
+ "numpy",
118
+ "controlmessage"
119
+ ],
120
+ "parameters": [],
121
+ "isPython": false,
122
+ "streaming": true,
123
+ "multiOutput": false
124
+ },
125
+ {
126
+ "nodeType": "TextCollector",
127
+ "description": "Collects text chunks into complete utterances",
128
+ "category": "text",
129
+ "accepts": [
130
+ "text"
131
+ ],
132
+ "produces": [
133
+ "text"
134
+ ],
135
+ "parameters": [
136
+ {
137
+ "name": "delimiter",
138
+ "paramType": "string",
139
+ "description": "Delimiter to split on",
140
+ "defaultValue": "\"\"",
141
+ "required": false
142
+ },
143
+ {
144
+ "name": "flush_on_silence",
145
+ "paramType": "boolean",
146
+ "description": "Flush buffer when silence detected",
147
+ "defaultValue": "true",
148
+ "required": false
149
+ }
150
+ ],
151
+ "configSchema": "{\"properties\":{\"delimiter\":{\"default\":\"\",\"description\":\"Delimiter to split on\",\"type\":\"string\"},\"flush_on_silence\":{\"default\":true,\"description\":\"Flush buffer when silence detected\",\"type\":\"boolean\"}},\"type\":\"object\"}",
152
+ "isPython": false,
153
+ "streaming": true,
154
+ "multiOutput": false
155
+ },
156
+ {
157
+ "nodeType": "Echo",
158
+ "description": "Passes input through unchanged (for testing)",
159
+ "category": "utility",
160
+ "accepts": [
161
+ "audio",
162
+ "video",
163
+ "json",
164
+ "text",
165
+ "binary",
166
+ "tensor",
167
+ "numpy",
168
+ "controlmessage"
169
+ ],
170
+ "produces": [
171
+ "audio",
172
+ "video",
173
+ "json",
174
+ "text",
175
+ "binary",
176
+ "tensor",
177
+ "numpy",
178
+ "controlmessage"
179
+ ],
180
+ "parameters": [],
181
+ "isPython": false,
182
+ "streaming": true,
183
+ "multiOutput": false
184
+ },
185
+ {
186
+ "nodeType": "SileroVAD",
187
+ "description": "Voice Activity Detection using Silero VAD model",
188
+ "category": "audio",
189
+ "accepts": [
190
+ "audio"
191
+ ],
192
+ "produces": [
193
+ "audio",
194
+ "controlmessage"
195
+ ],
196
+ "parameters": [
197
+ {
198
+ "name": "min_silence_duration_ms",
199
+ "paramType": "integer",
200
+ "description": "Minimum silence duration in ms",
201
+ "defaultValue": "100",
202
+ "required": false
203
+ },
204
+ {
205
+ "name": "min_speech_duration_ms",
206
+ "paramType": "integer",
207
+ "description": "Minimum speech duration in ms",
208
+ "defaultValue": "250",
209
+ "required": false
210
+ },
211
+ {
212
+ "name": "threshold",
213
+ "paramType": "number",
214
+ "description": "Speech probability threshold",
215
+ "defaultValue": "0.5",
216
+ "required": false,
217
+ "minimum": 0,
218
+ "maximum": 1
219
+ }
220
+ ],
221
+ "configSchema": "{\"properties\":{\"min_silence_duration_ms\":{\"default\":100,\"description\":\"Minimum silence duration in ms\",\"type\":\"integer\"},\"min_speech_duration_ms\":{\"default\":250,\"description\":\"Minimum speech duration in ms\",\"type\":\"integer\"},\"threshold\":{\"default\":0.5,\"description\":\"Speech probability threshold\",\"maximum\":1.0,\"minimum\":0.0,\"type\":\"number\"}},\"type\":\"object\"}",
222
+ "isPython": false,
223
+ "streaming": true,
224
+ "multiOutput": false,
225
+ "capabilities": {
226
+ "parallelizable": true,
227
+ "batchAware": false,
228
+ "supportsControl": true,
229
+ "latencyClass": 1
230
+ }
231
+ },
232
+ {
233
+ "nodeType": "AudioResample",
234
+ "description": "Resamples audio to target sample rate",
235
+ "category": "audio",
236
+ "accepts": [
237
+ "audio"
238
+ ],
239
+ "produces": [
240
+ "audio"
241
+ ],
242
+ "parameters": [
243
+ {
244
+ "name": "target_sample_rate",
245
+ "paramType": "integer",
246
+ "description": "Target sample rate in Hz",
247
+ "defaultValue": "16000",
248
+ "required": false,
249
+ "minimum": 8000,
250
+ "maximum": 48000
251
+ }
252
+ ],
253
+ "configSchema": "{\"properties\":{\"target_sample_rate\":{\"default\":16000,\"description\":\"Target sample rate in Hz\",\"maximum\":48000,\"minimum\":8000,\"type\":\"integer\"}},\"type\":\"object\"}",
254
+ "isPython": false,
255
+ "streaming": true,
256
+ "multiOutput": false,
257
+ "capabilities": {
258
+ "parallelizable": true,
259
+ "batchAware": false,
260
+ "supportsControl": false,
261
+ "latencyClass": 0
262
+ }
263
+ },
264
+ {
265
+ "nodeType": "KokoroTTSNode",
266
+ "description": "Text-to-speech synthesis using Kokoro TTS",
267
+ "category": "ml",
268
+ "accepts": [
269
+ "text"
270
+ ],
271
+ "produces": [
272
+ "audio"
273
+ ],
274
+ "parameters": [
275
+ {
276
+ "name": "language",
277
+ "paramType": "string",
278
+ "description": "Language code",
279
+ "defaultValue": "\"en-us\"",
280
+ "required": false,
281
+ "enumValues": "[\"en-us\",\"en-gb\",\"es\",\"fr\",\"de\",\"it\",\"ja\",\"ko\",\"pt-br\",\"zh\"]"
282
+ },
283
+ {
284
+ "name": "speed",
285
+ "paramType": "number",
286
+ "description": "Speech speed multiplier",
287
+ "defaultValue": "1.0",
288
+ "required": false,
289
+ "minimum": 0.5,
290
+ "maximum": 2
291
+ },
292
+ {
293
+ "name": "voice",
294
+ "paramType": "string",
295
+ "description": "Voice ID to use",
296
+ "defaultValue": "\"af_bella\"",
297
+ "required": false,
298
+ "enumValues": "[\"af_bella\",\"af_nicole\",\"af_sarah\",\"af_sky\",\"am_adam\",\"am_michael\",\"bf_emma\",\"bf_isabella\",\"bm_george\",\"bm_lewis\"]"
299
+ }
300
+ ],
301
+ "configSchema": "{\"properties\":{\"language\":{\"default\":\"en-us\",\"description\":\"Language code\",\"enum\":[\"en-us\",\"en-gb\",\"es\",\"fr\",\"de\",\"it\",\"ja\",\"ko\",\"pt-br\",\"zh\"],\"type\":\"string\"},\"speed\":{\"default\":1.0,\"description\":\"Speech speed multiplier\",\"maximum\":2.0,\"minimum\":0.5,\"type\":\"number\"},\"voice\":{\"default\":\"af_bella\",\"description\":\"Voice ID to use\",\"enum\":[\"af_bella\",\"af_nicole\",\"af_sarah\",\"af_sky\",\"am_adam\",\"am_michael\",\"bf_emma\",\"bf_isabella\",\"bm_george\",\"bm_lewis\"],\"type\":\"string\"}},\"type\":\"object\"}",
302
+ "isPython": true,
303
+ "streaming": true,
304
+ "multiOutput": true,
305
+ "capabilities": {
306
+ "parallelizable": false,
307
+ "batchAware": true,
308
+ "supportsControl": false,
309
+ "latencyClass": 3
310
+ }
311
+ },
312
+ {
313
+ "nodeType": "VideoFlip",
314
+ "description": "Flips video frames horizontally or vertically",
315
+ "category": "video",
316
+ "accepts": [
317
+ "video"
318
+ ],
319
+ "produces": [
320
+ "video"
321
+ ],
322
+ "parameters": [
323
+ {
324
+ "name": "horizontal",
325
+ "paramType": "boolean",
326
+ "description": "Flip horizontally",
327
+ "defaultValue": "true",
328
+ "required": false
329
+ },
330
+ {
331
+ "name": "vertical",
332
+ "paramType": "boolean",
333
+ "description": "Flip vertically",
334
+ "defaultValue": "false",
335
+ "required": false
336
+ }
337
+ ],
338
+ "configSchema": "{\"properties\":{\"horizontal\":{\"default\":true,\"description\":\"Flip horizontally\",\"type\":\"boolean\"},\"vertical\":{\"default\":false,\"description\":\"Flip vertically\",\"type\":\"boolean\"}},\"type\":\"object\"}",
339
+ "isPython": false,
340
+ "streaming": true,
341
+ "multiOutput": false
342
+ }
343
+ ]