@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/README.md +229 -0
- package/index.d.ts +717 -0
- package/index.js +138 -0
- package/node-schemas.json +343 -0
- package/node-schemas.ts +689 -0
- package/package.json +106 -0
- package/proto-utils.d.ts +97 -0
- package/proto-utils.js +376 -0
- package/remotemedia-native.x86_64-unknown-linux-gnu.node +0 -0
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@matbee/remotemedia-native",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Native Node.js bindings for RemoteMedia zero-copy IPC with iceoryx2 shared memory",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/remotemedia/remotemedia-sdk.git",
|
|
10
|
+
"directory": "transports/ffi/nodejs"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/remotemedia/remotemedia-sdk#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/remotemedia/remotemedia-sdk/issues"
|
|
15
|
+
},
|
|
16
|
+
"author": "RemoteMedia SDK Contributors",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"remotemedia",
|
|
20
|
+
"ipc",
|
|
21
|
+
"zero-copy",
|
|
22
|
+
"shared-memory",
|
|
23
|
+
"iceoryx2",
|
|
24
|
+
"napi-rs",
|
|
25
|
+
"native",
|
|
26
|
+
"ffi",
|
|
27
|
+
"pipeline",
|
|
28
|
+
"streaming",
|
|
29
|
+
"audio",
|
|
30
|
+
"video",
|
|
31
|
+
"machine-learning"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">= 18"
|
|
35
|
+
},
|
|
36
|
+
"os": [
|
|
37
|
+
"darwin",
|
|
38
|
+
"linux"
|
|
39
|
+
],
|
|
40
|
+
"cpu": [
|
|
41
|
+
"x64",
|
|
42
|
+
"arm64"
|
|
43
|
+
],
|
|
44
|
+
"napi": {
|
|
45
|
+
"name": "remotemedia-native",
|
|
46
|
+
"triples": {
|
|
47
|
+
"additional": [
|
|
48
|
+
"aarch64-apple-darwin",
|
|
49
|
+
"aarch64-unknown-linux-gnu",
|
|
50
|
+
"x86_64-apple-darwin",
|
|
51
|
+
"x86_64-unknown-linux-gnu"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public",
|
|
57
|
+
"registry": "https://registry.npmjs.org/"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "../scripts/build-npm.sh --release",
|
|
61
|
+
"build:debug": "../scripts/build-npm.sh --debug",
|
|
62
|
+
"build:cargo": "cd ../../.. && cargo build --release --features napi --no-default-features -p remotemedia-ffi && cp target/release/libremotemedia_ffi.so target/release/remotemedia_native.node || cp target/release/libremotemedia_ffi.dylib target/release/remotemedia_native.node 2>/dev/null || true",
|
|
63
|
+
"build:cargo:debug": "cd ../../.. && rm -f target/release/remotemedia_native.node && cargo build --features napi --no-default-features -p remotemedia-ffi && cp target/debug/libremotemedia_ffi.so target/debug/remotemedia_native.node || cp target/debug/libremotemedia_ffi.dylib target/debug/remotemedia_native.node 2>/dev/null || true",
|
|
64
|
+
"build:napi": "napi build --platform --release",
|
|
65
|
+
"build:napi:debug": "napi build --platform",
|
|
66
|
+
"build:all": "../scripts/build-npm.sh --release --all",
|
|
67
|
+
"build:webrtc": "../scripts/build-npm.sh --release --webrtc",
|
|
68
|
+
"generate-types": "node scripts/generate-types.js",
|
|
69
|
+
"postbuild": "npm run generate-types || true",
|
|
70
|
+
"prepublishOnly": "test -n \"$(ls *.node 2>/dev/null)\" || (echo 'Error: No .node binaries found. Run npm run build first.' && exit 1)",
|
|
71
|
+
"publish:npm": "../scripts/publish-npm.sh",
|
|
72
|
+
"publish:npm:dry": "../scripts/publish-npm.sh --dry-run",
|
|
73
|
+
"test": "jest",
|
|
74
|
+
"test:subscriber": "jest subscriber.test.ts",
|
|
75
|
+
"test:publisher": "jest publisher.test.ts",
|
|
76
|
+
"test:session": "jest session.test.ts",
|
|
77
|
+
"test:webrtc": "jest webrtc.test.ts",
|
|
78
|
+
"test:webrtc-pipeline": "jest webrtc-pipeline.test.ts",
|
|
79
|
+
"benchmark": "node benchmark-zero-copy.js",
|
|
80
|
+
"clean": "rm -rf *.node node_modules"
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"@napi-rs/cli": "^2.18.4",
|
|
84
|
+
"@types/jest": "^29.5.0",
|
|
85
|
+
"@types/node": "^20.0.0",
|
|
86
|
+
"@types/ws": "^8.5.0",
|
|
87
|
+
"jest": "^29.5.0",
|
|
88
|
+
"ts-jest": "^29.1.0",
|
|
89
|
+
"typescript": "^5.0.0",
|
|
90
|
+
"werift": "^0.22.2"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"ws": "^8.18.0"
|
|
94
|
+
},
|
|
95
|
+
"files": [
|
|
96
|
+
"index.js",
|
|
97
|
+
"index.d.ts",
|
|
98
|
+
"proto-utils.js",
|
|
99
|
+
"proto-utils.d.ts",
|
|
100
|
+
"node-schemas.ts",
|
|
101
|
+
"node-schemas.json",
|
|
102
|
+
"*.node",
|
|
103
|
+
"LICENSE",
|
|
104
|
+
"README.md"
|
|
105
|
+
]
|
|
106
|
+
}
|
package/proto-utils.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protobuf DataBuffer Encoder/Decoder Utilities
|
|
3
|
+
*
|
|
4
|
+
* Browser and Node.js compatible utilities for encoding/decoding
|
|
5
|
+
* DataBuffer protobuf messages for WebRTC data channel communication.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Decoded Types
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export interface DecodedTextBuffer {
|
|
13
|
+
type: 'text';
|
|
14
|
+
textData: string;
|
|
15
|
+
encoding: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DecodedJsonBuffer {
|
|
19
|
+
type: 'json';
|
|
20
|
+
jsonPayload: string;
|
|
21
|
+
schemaType?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DecodedAudioBuffer {
|
|
25
|
+
type: 'audio';
|
|
26
|
+
samples: Float32Array;
|
|
27
|
+
sampleRate: number;
|
|
28
|
+
channels: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DecodedUnknown {
|
|
32
|
+
type: 'unknown';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type DecodedDataBuffer =
|
|
36
|
+
| DecodedTextBuffer
|
|
37
|
+
| DecodedJsonBuffer
|
|
38
|
+
| DecodedAudioBuffer
|
|
39
|
+
| DecodedUnknown;
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Encoder Functions
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Encode a text string as a Protobuf DataBuffer with TextBuffer variant
|
|
47
|
+
* @param text - The text string to encode
|
|
48
|
+
* @returns Protobuf-encoded DataBuffer
|
|
49
|
+
*/
|
|
50
|
+
export function encodeTextData(text: string): ArrayBuffer;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Encode a JSON object as a Protobuf DataBuffer with JsonData variant
|
|
54
|
+
* @param data - The object to encode as JSON
|
|
55
|
+
* @param schemaType - Optional schema type identifier
|
|
56
|
+
* @returns Protobuf-encoded DataBuffer
|
|
57
|
+
*/
|
|
58
|
+
export function encodeJsonData(data: unknown, schemaType?: string): ArrayBuffer;
|
|
59
|
+
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Decoder Functions
|
|
62
|
+
// =============================================================================
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Decode a DataBuffer message from binary data
|
|
66
|
+
* @param data - The binary data to decode
|
|
67
|
+
* @returns Decoded DataBuffer
|
|
68
|
+
*/
|
|
69
|
+
export function decodeDataBuffer(data: ArrayBuffer | Uint8Array): DecodedDataBuffer;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Decode a TextBuffer message
|
|
73
|
+
* @param data - The binary data to decode
|
|
74
|
+
* @returns Decoded TextBuffer
|
|
75
|
+
*/
|
|
76
|
+
export function decodeTextBuffer(data: Uint8Array): DecodedTextBuffer;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Decode a JsonData message
|
|
80
|
+
* @param data - The binary data to decode
|
|
81
|
+
* @returns Decoded JsonBuffer
|
|
82
|
+
*/
|
|
83
|
+
export function decodeJsonData(data: Uint8Array): DecodedJsonBuffer;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Decode an AudioBuffer message
|
|
87
|
+
* @param data - The binary data to decode
|
|
88
|
+
* @returns Decoded AudioBuffer
|
|
89
|
+
*/
|
|
90
|
+
export function decodeAudioBuffer(data: Uint8Array): DecodedAudioBuffer;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Parse JSON from a decoded DataBuffer
|
|
94
|
+
* @param decoded - The decoded DataBuffer
|
|
95
|
+
* @returns Parsed JSON object or null
|
|
96
|
+
*/
|
|
97
|
+
export function parseJsonFromDataBuffer<T = unknown>(decoded: DecodedDataBuffer): T | null;
|
package/proto-utils.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protobuf DataBuffer Encoder/Decoder Utilities
|
|
3
|
+
*
|
|
4
|
+
* Browser and Node.js compatible utilities for encoding/decoding
|
|
5
|
+
* DataBuffer protobuf messages for WebRTC data channel communication.
|
|
6
|
+
*
|
|
7
|
+
* Based on transports/webrtc/protos/common.proto
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Encoder Functions
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Calculate the length of a varint encoding
|
|
16
|
+
* @param {number} value
|
|
17
|
+
* @returns {number}
|
|
18
|
+
*/
|
|
19
|
+
function varintLength(value) {
|
|
20
|
+
if (value < 0x80) return 1;
|
|
21
|
+
if (value < 0x4000) return 2;
|
|
22
|
+
if (value < 0x200000) return 3;
|
|
23
|
+
if (value < 0x10000000) return 4;
|
|
24
|
+
return 5;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Write a varint to a buffer
|
|
29
|
+
* @param {Uint8Array} buffer
|
|
30
|
+
* @param {number} offset
|
|
31
|
+
* @param {number} value
|
|
32
|
+
* @returns {number} New offset
|
|
33
|
+
*/
|
|
34
|
+
function writeVarint(buffer, offset, value) {
|
|
35
|
+
while (value >= 0x80) {
|
|
36
|
+
buffer[offset++] = (value & 0x7f) | 0x80;
|
|
37
|
+
value >>>= 7;
|
|
38
|
+
}
|
|
39
|
+
buffer[offset++] = value;
|
|
40
|
+
return offset;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Encode a text string as a Protobuf DataBuffer with TextBuffer variant
|
|
45
|
+
*
|
|
46
|
+
* Wire format:
|
|
47
|
+
* - DataBuffer.text (field 5): tag=0x2a
|
|
48
|
+
* - TextBuffer.text_data (field 1): tag=0x0a
|
|
49
|
+
* - TextBuffer.encoding (field 2): tag=0x12
|
|
50
|
+
*
|
|
51
|
+
* @param {string} text - The text string to encode
|
|
52
|
+
* @returns {ArrayBuffer} Protobuf-encoded DataBuffer
|
|
53
|
+
*/
|
|
54
|
+
function encodeTextData(text) {
|
|
55
|
+
const encoder = new TextEncoder();
|
|
56
|
+
const textBytes = encoder.encode(text);
|
|
57
|
+
const encoding = 'utf-8';
|
|
58
|
+
const encodingBytes = encoder.encode(encoding);
|
|
59
|
+
|
|
60
|
+
// Calculate sizes
|
|
61
|
+
const textDataSize = 1 + varintLength(textBytes.length) + textBytes.length;
|
|
62
|
+
const encodingSize = 1 + varintLength(encodingBytes.length) + encodingBytes.length;
|
|
63
|
+
const textBufferSize = textDataSize + encodingSize;
|
|
64
|
+
const dataBufferSize = 1 + varintLength(textBufferSize) + textBufferSize;
|
|
65
|
+
|
|
66
|
+
const buffer = new Uint8Array(dataBufferSize);
|
|
67
|
+
let offset = 0;
|
|
68
|
+
|
|
69
|
+
// DataBuffer.text (field 5, wire type 2)
|
|
70
|
+
buffer[offset++] = (5 << 3) | 2;
|
|
71
|
+
offset = writeVarint(buffer, offset, textBufferSize);
|
|
72
|
+
|
|
73
|
+
// TextBuffer.text_data (field 1, wire type 2)
|
|
74
|
+
buffer[offset++] = (1 << 3) | 2;
|
|
75
|
+
offset = writeVarint(buffer, offset, textBytes.length);
|
|
76
|
+
buffer.set(textBytes, offset);
|
|
77
|
+
offset += textBytes.length;
|
|
78
|
+
|
|
79
|
+
// TextBuffer.encoding (field 2, wire type 2)
|
|
80
|
+
buffer[offset++] = (2 << 3) | 2;
|
|
81
|
+
offset = writeVarint(buffer, offset, encodingBytes.length);
|
|
82
|
+
buffer.set(encodingBytes, offset);
|
|
83
|
+
|
|
84
|
+
return buffer.buffer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Encode a JSON object as a Protobuf DataBuffer with JsonData variant
|
|
89
|
+
*
|
|
90
|
+
* Wire format:
|
|
91
|
+
* - DataBuffer.json (field 6): tag=0x32
|
|
92
|
+
* - JsonData.json_payload (field 1): tag=0x0a
|
|
93
|
+
* - JsonData.schema_type (field 2): tag=0x12 [optional]
|
|
94
|
+
*
|
|
95
|
+
* @param {unknown} data - The object to encode as JSON
|
|
96
|
+
* @param {string} [schemaType] - Optional schema type identifier
|
|
97
|
+
* @returns {ArrayBuffer} Protobuf-encoded DataBuffer
|
|
98
|
+
*/
|
|
99
|
+
function encodeJsonData(data, schemaType) {
|
|
100
|
+
const encoder = new TextEncoder();
|
|
101
|
+
const jsonString = JSON.stringify(data);
|
|
102
|
+
const jsonBytes = encoder.encode(jsonString);
|
|
103
|
+
|
|
104
|
+
// Calculate JsonData message size
|
|
105
|
+
const jsonPayloadSize = 1 + varintLength(jsonBytes.length) + jsonBytes.length;
|
|
106
|
+
let schemaTypeSize = 0;
|
|
107
|
+
let schemaBytes = null;
|
|
108
|
+
|
|
109
|
+
if (schemaType) {
|
|
110
|
+
schemaBytes = encoder.encode(schemaType);
|
|
111
|
+
schemaTypeSize = 1 + varintLength(schemaBytes.length) + schemaBytes.length;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const jsonDataSize = jsonPayloadSize + schemaTypeSize;
|
|
115
|
+
const dataBufferSize = 1 + varintLength(jsonDataSize) + jsonDataSize;
|
|
116
|
+
|
|
117
|
+
const buffer = new Uint8Array(dataBufferSize);
|
|
118
|
+
let offset = 0;
|
|
119
|
+
|
|
120
|
+
// DataBuffer.json (field 6, wire type 2)
|
|
121
|
+
buffer[offset++] = (6 << 3) | 2;
|
|
122
|
+
offset = writeVarint(buffer, offset, jsonDataSize);
|
|
123
|
+
|
|
124
|
+
// JsonData.json_payload (field 1, wire type 2)
|
|
125
|
+
buffer[offset++] = (1 << 3) | 2;
|
|
126
|
+
offset = writeVarint(buffer, offset, jsonBytes.length);
|
|
127
|
+
buffer.set(jsonBytes, offset);
|
|
128
|
+
offset += jsonBytes.length;
|
|
129
|
+
|
|
130
|
+
// JsonData.schema_type (field 2, wire type 2) if provided
|
|
131
|
+
if (schemaBytes) {
|
|
132
|
+
buffer[offset++] = (2 << 3) | 2;
|
|
133
|
+
offset = writeVarint(buffer, offset, schemaBytes.length);
|
|
134
|
+
buffer.set(schemaBytes, offset);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return buffer.buffer;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// =============================================================================
|
|
141
|
+
// Decoder Functions
|
|
142
|
+
// =============================================================================
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Read a varint from a Uint8Array
|
|
146
|
+
* @param {Uint8Array} buffer
|
|
147
|
+
* @param {number} offset
|
|
148
|
+
* @returns {[number, number]} [value, bytesRead]
|
|
149
|
+
*/
|
|
150
|
+
function readVarint(buffer, offset) {
|
|
151
|
+
let result = 0;
|
|
152
|
+
let shift = 0;
|
|
153
|
+
let bytesRead = 0;
|
|
154
|
+
|
|
155
|
+
while (offset + bytesRead < buffer.length) {
|
|
156
|
+
const byte = buffer[offset + bytesRead];
|
|
157
|
+
result |= (byte & 0x7f) << shift;
|
|
158
|
+
bytesRead++;
|
|
159
|
+
if ((byte & 0x80) === 0) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
shift += 7;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return [result, bytesRead];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Decode a DataBuffer message from a Uint8Array
|
|
170
|
+
*
|
|
171
|
+
* DataBuffer oneof fields:
|
|
172
|
+
* - field 1: audio (AudioBuffer)
|
|
173
|
+
* - field 2: video (VideoBuffer)
|
|
174
|
+
* - field 3: image (ImageBuffer)
|
|
175
|
+
* - field 4: control (ControlBuffer)
|
|
176
|
+
* - field 5: text (TextBuffer)
|
|
177
|
+
* - field 6: json (JsonData)
|
|
178
|
+
* - field 7: bytes (BytesBuffer)
|
|
179
|
+
*
|
|
180
|
+
* @param {ArrayBuffer|Uint8Array} data
|
|
181
|
+
* @returns {{type: string, [key: string]: unknown}}
|
|
182
|
+
*/
|
|
183
|
+
function decodeDataBuffer(data) {
|
|
184
|
+
const buffer = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
185
|
+
let offset = 0;
|
|
186
|
+
|
|
187
|
+
while (offset < buffer.length) {
|
|
188
|
+
const [tag, tagBytes] = readVarint(buffer, offset);
|
|
189
|
+
offset += tagBytes;
|
|
190
|
+
|
|
191
|
+
const fieldNumber = tag >>> 3;
|
|
192
|
+
const wireType = tag & 0x07;
|
|
193
|
+
|
|
194
|
+
if (wireType === 2) {
|
|
195
|
+
// Length-delimited
|
|
196
|
+
const [length, lengthBytes] = readVarint(buffer, offset);
|
|
197
|
+
offset += lengthBytes;
|
|
198
|
+
|
|
199
|
+
const fieldData = buffer.slice(offset, offset + length);
|
|
200
|
+
offset += length;
|
|
201
|
+
|
|
202
|
+
switch (fieldNumber) {
|
|
203
|
+
case 5: // text (TextBuffer)
|
|
204
|
+
return decodeTextBuffer(fieldData);
|
|
205
|
+
case 6: // json (JsonData)
|
|
206
|
+
return decodeJsonData(fieldData);
|
|
207
|
+
case 1: // audio (AudioBuffer)
|
|
208
|
+
return decodeAudioBuffer(fieldData);
|
|
209
|
+
}
|
|
210
|
+
} else if (wireType === 0) {
|
|
211
|
+
// Varint - skip
|
|
212
|
+
const [, varBytes] = readVarint(buffer, offset);
|
|
213
|
+
offset += varBytes;
|
|
214
|
+
} else if (wireType === 5) {
|
|
215
|
+
// 32-bit fixed
|
|
216
|
+
offset += 4;
|
|
217
|
+
} else if (wireType === 1) {
|
|
218
|
+
// 64-bit fixed
|
|
219
|
+
offset += 8;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { type: 'unknown' };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Decode a TextBuffer message
|
|
228
|
+
* @param {Uint8Array} data
|
|
229
|
+
* @returns {{type: 'text', textData: string, encoding: string}}
|
|
230
|
+
*/
|
|
231
|
+
function decodeTextBuffer(data) {
|
|
232
|
+
const decoder = new TextDecoder();
|
|
233
|
+
let textData = '';
|
|
234
|
+
let encoding = 'utf-8';
|
|
235
|
+
let offset = 0;
|
|
236
|
+
|
|
237
|
+
while (offset < data.length) {
|
|
238
|
+
const [tag, tagBytes] = readVarint(data, offset);
|
|
239
|
+
offset += tagBytes;
|
|
240
|
+
|
|
241
|
+
const fieldNumber = tag >>> 3;
|
|
242
|
+
const wireType = tag & 0x07;
|
|
243
|
+
|
|
244
|
+
if (wireType === 2) {
|
|
245
|
+
const [length, lengthBytes] = readVarint(data, offset);
|
|
246
|
+
offset += lengthBytes;
|
|
247
|
+
|
|
248
|
+
const fieldData = data.slice(offset, offset + length);
|
|
249
|
+
offset += length;
|
|
250
|
+
|
|
251
|
+
if (fieldNumber === 1) {
|
|
252
|
+
textData = decoder.decode(fieldData);
|
|
253
|
+
} else if (fieldNumber === 2) {
|
|
254
|
+
encoding = decoder.decode(fieldData);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { type: 'text', textData, encoding };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Decode a JsonData message
|
|
264
|
+
* @param {Uint8Array} data
|
|
265
|
+
* @returns {{type: 'json', jsonPayload: string, schemaType?: string}}
|
|
266
|
+
*/
|
|
267
|
+
function decodeJsonData(data) {
|
|
268
|
+
const decoder = new TextDecoder();
|
|
269
|
+
let jsonPayload = '';
|
|
270
|
+
let schemaType;
|
|
271
|
+
let offset = 0;
|
|
272
|
+
|
|
273
|
+
while (offset < data.length) {
|
|
274
|
+
const [tag, tagBytes] = readVarint(data, offset);
|
|
275
|
+
offset += tagBytes;
|
|
276
|
+
|
|
277
|
+
const fieldNumber = tag >>> 3;
|
|
278
|
+
const wireType = tag & 0x07;
|
|
279
|
+
|
|
280
|
+
if (wireType === 2) {
|
|
281
|
+
const [length, lengthBytes] = readVarint(data, offset);
|
|
282
|
+
offset += lengthBytes;
|
|
283
|
+
|
|
284
|
+
const fieldData = data.slice(offset, offset + length);
|
|
285
|
+
offset += length;
|
|
286
|
+
|
|
287
|
+
if (fieldNumber === 1) {
|
|
288
|
+
jsonPayload = decoder.decode(fieldData);
|
|
289
|
+
} else if (fieldNumber === 2) {
|
|
290
|
+
schemaType = decoder.decode(fieldData);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return { type: 'json', jsonPayload, schemaType };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Decode an AudioBuffer message
|
|
300
|
+
* @param {Uint8Array} data
|
|
301
|
+
* @returns {{type: 'audio', samples: Float32Array, sampleRate: number, channels: number}}
|
|
302
|
+
*/
|
|
303
|
+
function decodeAudioBuffer(data) {
|
|
304
|
+
let samples = new Float32Array(0);
|
|
305
|
+
let sampleRate = 48000;
|
|
306
|
+
let channels = 1;
|
|
307
|
+
let offset = 0;
|
|
308
|
+
|
|
309
|
+
while (offset < data.length) {
|
|
310
|
+
const [tag, tagBytes] = readVarint(data, offset);
|
|
311
|
+
offset += tagBytes;
|
|
312
|
+
|
|
313
|
+
const fieldNumber = tag >>> 3;
|
|
314
|
+
const wireType = tag & 0x07;
|
|
315
|
+
|
|
316
|
+
if (wireType === 2 && fieldNumber === 1) {
|
|
317
|
+
// samples (packed repeated float)
|
|
318
|
+
const [length, lengthBytes] = readVarint(data, offset);
|
|
319
|
+
offset += lengthBytes;
|
|
320
|
+
|
|
321
|
+
const floatCount = length / 4;
|
|
322
|
+
samples = new Float32Array(floatCount);
|
|
323
|
+
const view = new DataView(data.buffer, data.byteOffset + offset, length);
|
|
324
|
+
for (let i = 0; i < floatCount; i++) {
|
|
325
|
+
samples[i] = view.getFloat32(i * 4, true);
|
|
326
|
+
}
|
|
327
|
+
offset += length;
|
|
328
|
+
} else if (wireType === 0) {
|
|
329
|
+
const [value, varBytes] = readVarint(data, offset);
|
|
330
|
+
offset += varBytes;
|
|
331
|
+
|
|
332
|
+
if (fieldNumber === 2) {
|
|
333
|
+
sampleRate = value;
|
|
334
|
+
} else if (fieldNumber === 3) {
|
|
335
|
+
channels = value;
|
|
336
|
+
}
|
|
337
|
+
} else if (wireType === 5) {
|
|
338
|
+
offset += 4;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return { type: 'audio', samples, sampleRate, channels };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Parse JSON from a decoded DataBuffer
|
|
347
|
+
* @template T
|
|
348
|
+
* @param {{type: string, jsonPayload?: string}} decoded
|
|
349
|
+
* @returns {T|null}
|
|
350
|
+
*/
|
|
351
|
+
function parseJsonFromDataBuffer(decoded) {
|
|
352
|
+
if (decoded.type === 'json' && decoded.jsonPayload) {
|
|
353
|
+
try {
|
|
354
|
+
return JSON.parse(decoded.jsonPayload);
|
|
355
|
+
} catch {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// =============================================================================
|
|
363
|
+
// Exports
|
|
364
|
+
// =============================================================================
|
|
365
|
+
|
|
366
|
+
module.exports = {
|
|
367
|
+
// Encoders
|
|
368
|
+
encodeTextData,
|
|
369
|
+
encodeJsonData,
|
|
370
|
+
// Decoders
|
|
371
|
+
decodeDataBuffer,
|
|
372
|
+
decodeTextBuffer,
|
|
373
|
+
decodeJsonData,
|
|
374
|
+
decodeAudioBuffer,
|
|
375
|
+
parseJsonFromDataBuffer,
|
|
376
|
+
};
|
|
Binary file
|