@4players/odin-nodejs 0.10.3 → 0.11.1
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 +72 -0
- package/LICENSE +21 -0
- package/README.md +603 -44
- package/binding.gyp +29 -13
- package/cppsrc/binding.cpp +3 -6
- package/cppsrc/odinbindings.cpp +9 -45
- package/cppsrc/odincipher.cpp +92 -0
- package/cppsrc/odincipher.h +32 -0
- package/cppsrc/odinclient.cpp +19 -158
- package/cppsrc/odinclient.h +2 -5
- package/cppsrc/odinmedia.cpp +144 -186
- package/cppsrc/odinmedia.h +51 -18
- package/cppsrc/odinroom.cpp +675 -635
- package/cppsrc/odinroom.h +76 -26
- package/cppsrc/utilities.cpp +11 -81
- package/cppsrc/utilities.h +25 -140
- package/index.cjs +829 -0
- package/index.d.ts +3 -4
- package/libs/bin/linux/arm64/libodin.so +0 -0
- package/libs/bin/linux/arm64/libodin_crypto.so +0 -0
- package/libs/bin/linux/ia32/libodin.so +0 -0
- package/libs/bin/linux/ia32/libodin_crypto.so +0 -0
- package/libs/bin/linux/x64/libodin.so +0 -0
- package/libs/bin/linux/x64/libodin_crypto.so +0 -0
- package/{prebuilds/darwin-x64/node.napi.node → libs/bin/macos/universal/libodin.dylib} +0 -0
- package/libs/bin/macos/universal/libodin_crypto.dylib +0 -0
- package/libs/bin/windows/arm64/odin.dll +0 -0
- package/libs/bin/windows/arm64/odin.lib +0 -0
- package/libs/bin/windows/arm64/odin_crypto.dll +0 -0
- package/libs/bin/windows/arm64/odin_crypto.lib +0 -0
- package/libs/bin/windows/ia32/odin.dll +0 -0
- package/libs/bin/windows/ia32/odin.lib +0 -0
- package/libs/bin/windows/ia32/odin_crypto.dll +0 -0
- package/libs/bin/windows/ia32/odin_crypto.lib +0 -0
- package/libs/bin/windows/x64/odin.dll +0 -0
- package/libs/bin/windows/x64/odin.lib +0 -0
- package/libs/bin/windows/x64/odin_crypto.dll +0 -0
- package/libs/bin/windows/x64/odin_crypto.lib +0 -0
- package/libs/include/odin.h +665 -567
- package/libs/include/odin_crypto.h +46 -0
- package/odin.cipher.d.ts +31 -0
- package/odin.media.d.ts +69 -19
- package/odin.room.d.ts +348 -7
- package/package.json +5 -4
- package/prebuilds/{darwin-arm64/node.napi.node → darwin-x64+arm64/libodin.dylib} +0 -0
- package/prebuilds/darwin-x64+arm64/libodin_crypto.dylib +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-x64/libodin.so +0 -0
- package/prebuilds/linux-x64/libodin_crypto.so +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/prebuilds/win32-x64/odin.dll +0 -0
- package/prebuilds/win32-x64/odin_crypto.dll +0 -0
- package/scripts/postbuild.cjs +133 -0
- package/tests/audio-recording/README.md +97 -12
- package/tests/audio-recording/index.js +238 -130
- package/tests/connection-test/README.md +97 -0
- package/tests/connection-test/index.js +273 -0
- package/tests/lifecycle/test-room-cycle.js +169 -0
- package/tests/sending-audio/README.md +178 -9
- package/tests/sending-audio/canBounce.mp3 +0 -0
- package/tests/sending-audio/index.js +250 -87
- package/tests/sending-audio/test-kiss-api.js +149 -0
- package/tests/sending-audio/test-loop-audio.js +142 -0
- package/CMakeLists.txt +0 -25
- package/libs/bin/linux/arm64/libodin_static.a +0 -0
- package/libs/bin/linux/ia32/libodin_static.a +0 -0
- package/libs/bin/linux/x64/libodin_static.a +0 -0
- package/libs/bin/macos/arm64/libodin_static.a +0 -0
- package/libs/bin/macos/x64/libodin_static.a +0 -0
- package/libs/bin/windows/arm64/odin_static.lib +0 -0
- package/libs/bin/windows/ia32/odin_static.lib +0 -0
- package/libs/bin/windows/x64/odin_static.lib +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ODIN Node.js SDK - Audio Sending Example (High-Level API)
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the HIGH-LEVEL API for sending audio to an ODIN room.
|
|
5
|
+
* The high-level API handles all the complexity automatically:
|
|
6
|
+
* - Media ID allocation from the Joined event
|
|
7
|
+
* - StartMedia RPC to notify the server
|
|
8
|
+
* - Proper 20ms chunk timing
|
|
9
|
+
* - Audio decoding for MP3/WAV files
|
|
10
|
+
*
|
|
11
|
+
* With the high-level API, sending audio is just:
|
|
12
|
+
* await media.sendMP3('./file.mp3');
|
|
13
|
+
*
|
|
14
|
+
* For full control, see index.js which uses the LOW-LEVEL API.
|
|
15
|
+
*
|
|
16
|
+
* Prerequisites:
|
|
17
|
+
* - Replace __YOUR_ACCESS_KEY__ with your ODIN access key
|
|
18
|
+
* - Get a free access key at https://www.4players.io/odin
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import odin from '../../index.cjs';
|
|
22
|
+
const { OdinClient, OdinCipher } = odin;
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import { fileURLToPath } from 'url';
|
|
25
|
+
|
|
26
|
+
// Get the directory of the current script
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = path.dirname(__filename);
|
|
29
|
+
|
|
30
|
+
// ============================================
|
|
31
|
+
// Configuration - Replace with your values
|
|
32
|
+
// ============================================
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Your ODIN access key. Get one for free at https://www.4players.io/odin
|
|
36
|
+
* @type {string}
|
|
37
|
+
*/
|
|
38
|
+
const accessKey = "__YOUR_ACCESS_KEY__";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The room ID to join. All users joining the same room can hear your audio.
|
|
42
|
+
* @type {string}
|
|
43
|
+
*/
|
|
44
|
+
const roomId = "__YOUR_ROOM_ID__";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Your unique user identifier.
|
|
48
|
+
* @type {string}
|
|
49
|
+
*/
|
|
50
|
+
const userId = "AudioBot-" + Math.floor(Math.random() * 10000);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Optional: Password for End-to-End Encryption.
|
|
54
|
+
* Set to null to disable, or set a string that matches other peers.
|
|
55
|
+
* @type {string|null}
|
|
56
|
+
*/
|
|
57
|
+
const cipherPassword = null;
|
|
58
|
+
|
|
59
|
+
// ============================================
|
|
60
|
+
// Main Function
|
|
61
|
+
// ============================================
|
|
62
|
+
|
|
63
|
+
async function main() {
|
|
64
|
+
console.log("=== ODIN NodeJS SDK - High-Level API Audio Test ===\n");
|
|
65
|
+
|
|
66
|
+
// Step 1: Create ODIN client
|
|
67
|
+
console.log("1. Creating ODIN client...");
|
|
68
|
+
const client = new OdinClient();
|
|
69
|
+
|
|
70
|
+
// Step 2: Generate token from access key
|
|
71
|
+
console.log("2. Generating token from access key...");
|
|
72
|
+
const token = client.generateToken(accessKey, roomId, userId);
|
|
73
|
+
console.log(" Token generated.\n");
|
|
74
|
+
|
|
75
|
+
// Step 3: Create room
|
|
76
|
+
console.log("3. Creating room...");
|
|
77
|
+
const room = client.createRoom(token);
|
|
78
|
+
|
|
79
|
+
// Step 4: Set up E2EE cipher (optional)
|
|
80
|
+
if (cipherPassword) {
|
|
81
|
+
console.log("4. Setting up E2EE cipher...");
|
|
82
|
+
const cipher = new OdinCipher();
|
|
83
|
+
cipher.setPassword(new TextEncoder().encode(cipherPassword));
|
|
84
|
+
room.setCipher(cipher);
|
|
85
|
+
console.log(" E2EE enabled.\n");
|
|
86
|
+
} else {
|
|
87
|
+
console.log("4. E2EE disabled.\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Wait for join to complete
|
|
91
|
+
const joinPromise = new Promise(resolve => room.onJoined(resolve));
|
|
92
|
+
|
|
93
|
+
// Prepare user data
|
|
94
|
+
const userData = new TextEncoder().encode(JSON.stringify({
|
|
95
|
+
name: "High-Level API Bot",
|
|
96
|
+
platform: "ODIN Node.js SDK"
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
// Step 5: Join the room
|
|
100
|
+
console.log("5. Joining room...");
|
|
101
|
+
room.join("https://gateway.odin.4players.io", userData);
|
|
102
|
+
|
|
103
|
+
// Wait for join confirmation
|
|
104
|
+
console.log(" Waiting for room join...");
|
|
105
|
+
const joinEvent = await joinPromise;
|
|
106
|
+
console.log(` Joined! Own Peer ID: ${joinEvent.ownPeerId}`);
|
|
107
|
+
console.log(` Available media IDs: ${JSON.stringify(room.availableMediaIds)}\n`);
|
|
108
|
+
|
|
109
|
+
// Step 6: Create audio stream
|
|
110
|
+
console.log("6. Creating audio stream (44.1kHz, stereo)...");
|
|
111
|
+
const media = room.createAudioStream(44100, 2);
|
|
112
|
+
console.log(" Audio stream created.\n");
|
|
113
|
+
|
|
114
|
+
// Step 7: Send MP3 with one line! (high-level API magic)
|
|
115
|
+
// The sendMP3 method automatically:
|
|
116
|
+
// - Claims a media ID from the available pool
|
|
117
|
+
// - Sends the StartMedia RPC
|
|
118
|
+
// - Decodes the MP3 file
|
|
119
|
+
// - Streams audio in 20ms chunks with correct timing
|
|
120
|
+
console.log("7. Sending MP3 file (auto handles setup & timing)...");
|
|
121
|
+
const audioFile = path.join(__dirname, 'canBounce.mp3');
|
|
122
|
+
console.log(` File: ${audioFile}`);
|
|
123
|
+
|
|
124
|
+
await media.sendMP3(audioFile);
|
|
125
|
+
|
|
126
|
+
console.log(" Audio streaming complete!\n");
|
|
127
|
+
|
|
128
|
+
// Wait a bit for final audio to transmit
|
|
129
|
+
console.log("8. Waiting for transmission to complete...");
|
|
130
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
131
|
+
|
|
132
|
+
// Step 9: Clean up
|
|
133
|
+
console.log("9. Cleaning up...");
|
|
134
|
+
media.close();
|
|
135
|
+
room.close();
|
|
136
|
+
console.log(" Resources released.\n");
|
|
137
|
+
|
|
138
|
+
console.log("=== Test Complete ===");
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================
|
|
143
|
+
// Entry Point
|
|
144
|
+
// ============================================
|
|
145
|
+
|
|
146
|
+
main().catch(err => {
|
|
147
|
+
console.error("Error:", err);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ODIN Node.js SDK - Multiple Audio Sending Test
|
|
3
|
+
*
|
|
4
|
+
* This test verifies that multiple audio files can be sent in sequence
|
|
5
|
+
* within a single room connection. It creates a media stream, sends an MP3,
|
|
6
|
+
* closes the media, waits 5 seconds, and repeats.
|
|
7
|
+
*
|
|
8
|
+
* This is a regression test for issues where sending multiple MP3s in
|
|
9
|
+
* one connection instance would fail.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import odin from '../../index.cjs';
|
|
13
|
+
const { OdinClient, OdinCipher } = odin;
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
// Get the directory of the current script
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// Configuration - Replace with your values
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
const accessKey = "__YOUR_ACCESS_KEY__";
|
|
26
|
+
const roomId = "__YOUR_ROOM_ID__";
|
|
27
|
+
const userId = "LoopBot-" + Math.floor(Math.random() * 10000);
|
|
28
|
+
const cipherPassword = "__YOUR_CIPHER_PASSWORD_OR_NULL__";
|
|
29
|
+
|
|
30
|
+
// Number of times to send the audio file
|
|
31
|
+
const LOOP_COUNT = 3;
|
|
32
|
+
|
|
33
|
+
// Delay between sends (in milliseconds)
|
|
34
|
+
const DELAY_BETWEEN_SENDS = 5000;
|
|
35
|
+
|
|
36
|
+
// ============================================
|
|
37
|
+
// Main Function
|
|
38
|
+
// ============================================
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
console.log("=== ODIN NodeJS SDK - Multiple Audio Sending Test ===\n");
|
|
42
|
+
console.log(`Will send audio ${LOOP_COUNT} times with ${DELAY_BETWEEN_SENDS}ms delay between sends.\n`);
|
|
43
|
+
|
|
44
|
+
// Step 1: Create ODIN client
|
|
45
|
+
console.log("1. Creating ODIN client...");
|
|
46
|
+
const client = new OdinClient();
|
|
47
|
+
|
|
48
|
+
// Step 2: Generate token from access key
|
|
49
|
+
console.log("2. Generating token from access key...");
|
|
50
|
+
const token = client.generateToken(accessKey, roomId, userId);
|
|
51
|
+
console.log(" Token generated.\n");
|
|
52
|
+
|
|
53
|
+
// Step 3: Create room
|
|
54
|
+
console.log("3. Creating room...");
|
|
55
|
+
const room = client.createRoom(token);
|
|
56
|
+
|
|
57
|
+
// Step 4: Set up E2EE cipher (optional)
|
|
58
|
+
if (cipherPassword) {
|
|
59
|
+
console.log("4. Setting up E2EE cipher...");
|
|
60
|
+
const cipher = new OdinCipher();
|
|
61
|
+
cipher.setPassword(new TextEncoder().encode(cipherPassword));
|
|
62
|
+
room.setCipher(cipher);
|
|
63
|
+
console.log(" E2EE enabled.\n");
|
|
64
|
+
} else {
|
|
65
|
+
console.log("4. E2EE disabled.\n");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Wait for join to complete
|
|
69
|
+
const joinPromise = new Promise(resolve => room.onJoined(resolve));
|
|
70
|
+
|
|
71
|
+
// Prepare user data
|
|
72
|
+
const userData = new TextEncoder().encode(JSON.stringify({
|
|
73
|
+
name: "Loop Audio Bot",
|
|
74
|
+
platform: "ODIN Node.js SDK"
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
// Step 5: Join the room
|
|
78
|
+
console.log("5. Joining room...");
|
|
79
|
+
room.join("https://gateway.odin.4players.io", userData);
|
|
80
|
+
|
|
81
|
+
// Wait for join confirmation
|
|
82
|
+
console.log(" Waiting for room join...");
|
|
83
|
+
const joinEvent = await joinPromise;
|
|
84
|
+
console.log(` Joined! Own Peer ID: ${joinEvent.ownPeerId}`);
|
|
85
|
+
console.log(` Available media IDs: ${JSON.stringify(room.availableMediaIds)}\n`);
|
|
86
|
+
|
|
87
|
+
const audioFile = path.join(__dirname, 'canBounce.mp3');
|
|
88
|
+
|
|
89
|
+
// Loop: Send audio multiple times
|
|
90
|
+
for (let i = 1; i <= LOOP_COUNT; i++) {
|
|
91
|
+
console.log(`\n========== ITERATION ${i}/${LOOP_COUNT} ==========\n`);
|
|
92
|
+
|
|
93
|
+
// Step 6: Create audio stream
|
|
94
|
+
console.log(`${i}.1 Creating audio stream (44.1kHz, stereo)...`);
|
|
95
|
+
const media = room.createAudioStream(44100, 2);
|
|
96
|
+
console.log(" Audio stream created.");
|
|
97
|
+
console.log(` Available media IDs after create: ${JSON.stringify(room.availableMediaIds)}`);
|
|
98
|
+
|
|
99
|
+
// Step 7: Send MP3 with high-level API
|
|
100
|
+
console.log(`${i}.2 Sending MP3 file...`);
|
|
101
|
+
console.log(` File: ${audioFile}`);
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await media.sendMP3(audioFile);
|
|
105
|
+
console.log(" Audio streaming complete!");
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(` ERROR during sendMP3: ${error.message}`);
|
|
108
|
+
console.error(error.stack);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Step 8: Close media
|
|
112
|
+
console.log(`${i}.3 Closing media stream...`);
|
|
113
|
+
media.close();
|
|
114
|
+
console.log(" Media stream closed.");
|
|
115
|
+
console.log(` Available media IDs after close: ${JSON.stringify(room.availableMediaIds)}`);
|
|
116
|
+
|
|
117
|
+
// Wait before next iteration (except on last iteration)
|
|
118
|
+
if (i < LOOP_COUNT) {
|
|
119
|
+
console.log(`\n${i}.4 Waiting ${DELAY_BETWEEN_SENDS}ms before next send...`);
|
|
120
|
+
await new Promise(r => setTimeout(r, DELAY_BETWEEN_SENDS));
|
|
121
|
+
console.log(" Wait complete.");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Final cleanup
|
|
126
|
+
console.log("\n========== CLEANUP ==========\n");
|
|
127
|
+
console.log("Closing room...");
|
|
128
|
+
room.close();
|
|
129
|
+
console.log("Room closed.\n");
|
|
130
|
+
|
|
131
|
+
console.log("=== Test Complete ===");
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================
|
|
136
|
+
// Entry Point
|
|
137
|
+
// ============================================
|
|
138
|
+
|
|
139
|
+
main().catch(err => {
|
|
140
|
+
console.error("Error:", err);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
package/CMakeLists.txt
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.24)
|
|
2
|
-
project(odin_nodejs)
|
|
3
|
-
|
|
4
|
-
set(CMAKE_CXX_STANDARD 14)
|
|
5
|
-
|
|
6
|
-
add_definitions(-DNAPI_VERSION=4)
|
|
7
|
-
|
|
8
|
-
include_directories(cppsrc/samples)
|
|
9
|
-
include_directories(libs)
|
|
10
|
-
include_directories(libs/include)
|
|
11
|
-
include_directories(node_modules/node-addon-api)
|
|
12
|
-
|
|
13
|
-
add_executable(odin_nodejs
|
|
14
|
-
cppsrc/odinbindings.cpp
|
|
15
|
-
cppsrc/odinbindings.h
|
|
16
|
-
cppsrc/odinroom.cpp
|
|
17
|
-
cppsrc/odinroom.h
|
|
18
|
-
cppsrc/binding.cpp
|
|
19
|
-
libs/include/odin.h
|
|
20
|
-
node_modules/node-addon-api/napi-inl.deprecated.h
|
|
21
|
-
node_modules/node-addon-api/napi-inl.h
|
|
22
|
-
node_modules/node-addon-api/napi.h
|
|
23
|
-
node_modules/node-addon-api/nothing.c
|
|
24
|
-
node_modules/node-gyp/gyp/data/win/large-pdb-shim.cc
|
|
25
|
-
node_modules/node-gyp/src/win_delay_load_hook.cc cppsrc/odinbindings.h cppsrc/odinbindings.cpp cppsrc/odinroom.h cppsrc/odinroom.cpp cppsrc/utilities.cpp cppsrc/utilities.h cppsrc/odinclient.h cppsrc/odinclient.cpp cppsrc/odinmedia.h cppsrc/odinmedia.cpp)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|