@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.
Files changed (73) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/LICENSE +21 -0
  3. package/README.md +603 -44
  4. package/binding.gyp +29 -13
  5. package/cppsrc/binding.cpp +3 -6
  6. package/cppsrc/odinbindings.cpp +9 -45
  7. package/cppsrc/odincipher.cpp +92 -0
  8. package/cppsrc/odincipher.h +32 -0
  9. package/cppsrc/odinclient.cpp +19 -158
  10. package/cppsrc/odinclient.h +2 -5
  11. package/cppsrc/odinmedia.cpp +144 -186
  12. package/cppsrc/odinmedia.h +51 -18
  13. package/cppsrc/odinroom.cpp +675 -635
  14. package/cppsrc/odinroom.h +76 -26
  15. package/cppsrc/utilities.cpp +11 -81
  16. package/cppsrc/utilities.h +25 -140
  17. package/index.cjs +829 -0
  18. package/index.d.ts +3 -4
  19. package/libs/bin/linux/arm64/libodin.so +0 -0
  20. package/libs/bin/linux/arm64/libodin_crypto.so +0 -0
  21. package/libs/bin/linux/ia32/libodin.so +0 -0
  22. package/libs/bin/linux/ia32/libodin_crypto.so +0 -0
  23. package/libs/bin/linux/x64/libodin.so +0 -0
  24. package/libs/bin/linux/x64/libodin_crypto.so +0 -0
  25. package/{prebuilds/darwin-x64/node.napi.node → libs/bin/macos/universal/libodin.dylib} +0 -0
  26. package/libs/bin/macos/universal/libodin_crypto.dylib +0 -0
  27. package/libs/bin/windows/arm64/odin.dll +0 -0
  28. package/libs/bin/windows/arm64/odin.lib +0 -0
  29. package/libs/bin/windows/arm64/odin_crypto.dll +0 -0
  30. package/libs/bin/windows/arm64/odin_crypto.lib +0 -0
  31. package/libs/bin/windows/ia32/odin.dll +0 -0
  32. package/libs/bin/windows/ia32/odin.lib +0 -0
  33. package/libs/bin/windows/ia32/odin_crypto.dll +0 -0
  34. package/libs/bin/windows/ia32/odin_crypto.lib +0 -0
  35. package/libs/bin/windows/x64/odin.dll +0 -0
  36. package/libs/bin/windows/x64/odin.lib +0 -0
  37. package/libs/bin/windows/x64/odin_crypto.dll +0 -0
  38. package/libs/bin/windows/x64/odin_crypto.lib +0 -0
  39. package/libs/include/odin.h +665 -567
  40. package/libs/include/odin_crypto.h +46 -0
  41. package/odin.cipher.d.ts +31 -0
  42. package/odin.media.d.ts +69 -19
  43. package/odin.room.d.ts +348 -7
  44. package/package.json +5 -4
  45. package/prebuilds/{darwin-arm64/node.napi.node → darwin-x64+arm64/libodin.dylib} +0 -0
  46. package/prebuilds/darwin-x64+arm64/libodin_crypto.dylib +0 -0
  47. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  48. package/prebuilds/linux-x64/libodin.so +0 -0
  49. package/prebuilds/linux-x64/libodin_crypto.so +0 -0
  50. package/prebuilds/linux-x64/node.napi.node +0 -0
  51. package/prebuilds/win32-x64/node.napi.node +0 -0
  52. package/prebuilds/win32-x64/odin.dll +0 -0
  53. package/prebuilds/win32-x64/odin_crypto.dll +0 -0
  54. package/scripts/postbuild.cjs +133 -0
  55. package/tests/audio-recording/README.md +97 -12
  56. package/tests/audio-recording/index.js +238 -130
  57. package/tests/connection-test/README.md +97 -0
  58. package/tests/connection-test/index.js +273 -0
  59. package/tests/lifecycle/test-room-cycle.js +169 -0
  60. package/tests/sending-audio/README.md +178 -9
  61. package/tests/sending-audio/canBounce.mp3 +0 -0
  62. package/tests/sending-audio/index.js +250 -87
  63. package/tests/sending-audio/test-kiss-api.js +149 -0
  64. package/tests/sending-audio/test-loop-audio.js +142 -0
  65. package/CMakeLists.txt +0 -25
  66. package/libs/bin/linux/arm64/libodin_static.a +0 -0
  67. package/libs/bin/linux/ia32/libodin_static.a +0 -0
  68. package/libs/bin/linux/x64/libodin_static.a +0 -0
  69. package/libs/bin/macos/arm64/libodin_static.a +0 -0
  70. package/libs/bin/macos/x64/libodin_static.a +0 -0
  71. package/libs/bin/windows/arm64/odin_static.lib +0 -0
  72. package/libs/bin/windows/ia32/odin_static.lib +0 -0
  73. package/libs/bin/windows/x64/odin_static.lib +0 -0
package/cppsrc/odinroom.h CHANGED
@@ -1,66 +1,116 @@
1
- //
2
- // Created by Phillip Schuster on 11.02.23.
3
- //
4
-
5
1
  #ifndef ODIN_NODEJS_ODINROOM_H
6
2
  #define ODIN_NODEJS_ODINROOM_H
7
3
 
8
4
  #include <napi.h>
9
5
  #include <odin.h>
6
+ // Forward declaration instead of including odin_crypto.h
7
+ struct OdinCipher;
10
8
  #include "utilities.h"
11
9
  #include <map>
12
10
  #include <thread>
11
+ #include <mutex>
12
+ #include <atomic>
13
13
 
14
- class OdinRoom : public Napi::ObjectWrap<OdinRoom> {
15
- struct Media {
16
- OdinMediaStreamHandle Handle;
17
- uint16_t Id;
18
- uint64_t PeerId;
19
- };
14
+ class OdinRoomWrapper : public Napi::ObjectWrap<OdinRoomWrapper> {
20
15
 
21
16
  public:
22
- OdinRoom(const Napi::CallbackInfo& info); //Constructor to initialise
17
+ OdinRoomWrapper(const Napi::CallbackInfo& info);
23
18
  static Napi::FunctionReference *New;
24
19
  static Napi::Object Init(Napi::Env env, Napi::Object exports);
25
20
  static Napi::Value CreateNewItem(const Napi::CallbackInfo& info);
26
21
  void Finalize(Napi::Env env) override;
27
- static void HandleOdinEvent(OdinRoomHandle room, const OdinEvent *event, void *data);
28
- static Napi::Object PrepareEventObject(Napi::Env env, const EventData *event);
29
- static Napi::Object PrepareEventObjectTest(Napi::Env env, const OdinEvent *event);
30
- OdinRoomHandle GetRoomHandle() const;
22
+
23
+ // Callbacks for Connection Pool
24
+ static void OnDatagram(uint64_t room_ref, uint16_t media_id, const uint8_t *bytes, uint32_t bytes_length, void *user_data);
25
+ static void OnRPC(uint64_t room_ref, const uint8_t *bytes, uint32_t bytes_length, void *user_data);
26
+
27
+ struct OdinRoom* GetRoom() const { return _room; }
31
28
  static Napi::FunctionReference *constructor;
32
29
  static Napi::Object NewInstance(Napi::Value arg);
30
+
31
+ // SDK lifecycle management - reference counting for proper initialization/shutdown
32
+ static void IncrementRoomCount();
33
+ static void DecrementRoomCount();
34
+
35
+ // Internal handler called by static callbacks
36
+ void HandleDatagramInternal(uint16_t media_id, const uint8_t *bytes, uint32_t bytes_length);
37
+ void HandleRPCInternal(const uint8_t *bytes, uint32_t bytes_length);
38
+
39
+ struct OdinRoom* _room;
40
+
33
41
  private:
34
- OdinRoomHandle _roomHandle;
42
+ struct OdinCipher* _cipher;
43
+
44
+ // Static connection pool for all rooms
45
+ static struct OdinConnectionPool* _pool;
46
+ static std::map<uint64_t, OdinRoomWrapper*> _roomsMap;
47
+ static std::mutex _roomsMapMutex;
48
+
49
+ static struct OdinConnectionPool* GetConnectionPool();
50
+
35
51
  std::string _token;
36
- Napi::ThreadSafeFunction _eventListener;
37
- std::map<uint16_t, Media> _mediaStreams;
52
+
53
+ std::map<uint16_t, struct OdinDecoder*> _decoders;
54
+ std::mutex _decodersMutex;
55
+
56
+ // Mapping from media_id to peer_id (populated from JavaScript via MediaStarted events)
57
+ std::map<uint16_t, uint64_t> _mediaToPeer;
58
+ std::mutex _mediaToPeerMutex;
59
+
60
+ // Own peer ID (set from JavaScript after joining)
61
+ uint64_t _ownPeerId;
62
+
63
+ // Position coordinates (x, y, z) for spatial audio and server-side culling
64
+ // These are stored and used during odin_room_create_ex call in Join()
65
+ float _position[3];
66
+
67
+ // Position scale factor - divides position coordinates to normalize to unit circle
68
+ float _positionScale;
38
69
 
39
70
  std::thread _nativeThread;
40
- bool _started;
71
+ std::atomic<bool> _started;
72
+
73
+ // Counter for pending NonBlockingCall callbacks
74
+ // Incremented before NonBlockingCall, decremented when callback completes
75
+ // Used to ensure all callbacks finish before releasing ThreadSafeFunctions
76
+ std::atomic<int> _pendingCallbacks;
77
+
41
78
  bool _joined;
42
- float _audioSamplesBuffer[960];
79
+
80
+ float _audioSamplesBuffer[1920];
43
81
 
44
82
  std::map<std::string, Napi::ThreadSafeFunction> _eventListeners;
83
+ Napi::ThreadSafeFunction _eventListener; // Restored
45
84
  Napi::ThreadSafeFunction _audioDataReceivedEventListener;
46
85
 
47
86
  void Close(const Napi::CallbackInfo& info);
48
87
  void Join(const Napi::CallbackInfo& info);
49
88
  void UpdatePeerUserData(const Napi::CallbackInfo &info);
50
- void SendMessage(const Napi::CallbackInfo &info);
89
+ void SendMessage(const Napi::CallbackInfo &info);
90
+ void SendRpc(const Napi::CallbackInfo &info);
91
+
51
92
  void SetEventListener(const Napi::CallbackInfo &info);
52
93
  void AddEventListener(const Napi::CallbackInfo &info);
53
94
  void RemoveEventListener(const Napi::CallbackInfo &info);
54
95
  Napi::Value GetOwnPeerId(const Napi::CallbackInfo &info);
55
96
  Napi::Value RoomId(const Napi::CallbackInfo &info);
56
- EventData* PrepareEventData(OdinEvent* event);
57
- static uint16_t GetMediaIdFromHandle(OdinMediaStreamHandle handle);
58
- void HandleAudioData();
97
+ void SetCipher(const Napi::CallbackInfo &info);
98
+
99
+ // Methods for JavaScript to register media-to-peer mapping
100
+ void RegisterMediaPeer(const Napi::CallbackInfo &info);
101
+ void UnregisterMedia(const Napi::CallbackInfo &info);
102
+ void SetOwnPeerId(const Napi::CallbackInfo &info);
103
+
104
+ void HandleAudioData();
59
105
  void UpdatePosition(const Napi::CallbackInfo &info);
60
- void SetPositionScale(const Napi::CallbackInfo &info);
106
+ void SetPositionScale(const Napi::CallbackInfo &info);
61
107
 
62
108
  Napi::Value CreateAudioStream(const Napi::CallbackInfo &info);
109
+
110
+ // Stats and diagnostics methods
111
+ Napi::Value GetConnectionStats(const Napi::CallbackInfo &info);
112
+ Napi::Value GetConnectionId(const Napi::CallbackInfo &info);
113
+ Napi::Value GetJitterStats(const Napi::CallbackInfo &info);
63
114
  };
64
115
 
65
-
66
116
  #endif //ODIN_NODEJS_ODINROOM_H
@@ -1,102 +1,32 @@
1
1
  #include "utilities.h"
2
+ #include <cstdio>
3
+ #include <cstdarg>
2
4
 
3
5
  std::string string_format(const std::string fmt, ...) {
4
- int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
6
+ int size = ((int)fmt.size()) * 2 + 50;
5
7
  std::string str;
6
8
  va_list ap;
7
- while (1) { // Maximum two passes on a POSIX system...
9
+ while (1) {
8
10
  str.resize(size);
9
11
  va_start(ap, fmt);
10
12
  int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
11
13
  va_end(ap);
12
- if (n > -1 && n < size) { // Everything worked
14
+ if (n > -1 && n < size) {
13
15
  str.resize(n);
14
16
  return str;
15
17
  }
16
- if (n > -1) // Needed size returned
17
- size = n + 1; // For null char
18
+ if (n > -1)
19
+ size = n + 1;
18
20
  else
19
- size *= 2; // Guess at a larger size (OS specific)
21
+ size *= 2;
20
22
  }
21
23
  return str;
22
24
  }
23
25
 
24
- void OdinUtilities::ThrowNapiException(Napi::Env env, OdinReturnCode error, const char* message)
26
+ void OdinUtilities::ThrowNapiException(Napi::Env env, OdinError error, const char* message)
25
27
  {
26
- char buffer[512];
27
- odin_error_format(error, buffer, sizeof(buffer));
28
-
29
- std::string text = string_format("%s: %s", message, buffer);
28
+ const char* lastError = odin_error_get_last_error();
29
+ std::string text = string_format("%s: ErrorCode=%d Details=%s", message, (int)error, lastError ? lastError : "");
30
30
 
31
31
  Napi::TypeError::New(env, text.c_str()).ThrowAsJavaScriptException();
32
- }
33
-
34
- /**
35
- * @brief Returns a human-readable string representation for a specified connection state
36
- *
37
- * @param state The room connection state to translate
38
- */
39
- const char* OdinUtilities::GetNameFromConnectionState(OdinRoomConnectionState state)
40
- {
41
- switch (state)
42
- {
43
- case OdinRoomConnectionState_Connecting:
44
- return "connecting";
45
- case OdinRoomConnectionState_Connected:
46
- return "connected";
47
- case OdinRoomConnectionState_Disconnecting:
48
- return "disconnecting";
49
- case OdinRoomConnectionState_Disconnected:
50
- return "disconnected";
51
- default:
52
- return "unknown";
53
- }
54
- }
55
-
56
- /**
57
- * @brief Returns a human-readable string representation for a specified connection state change reason
58
- *
59
- * @param state The room connection state change reason to translate
60
- */
61
- const char* OdinUtilities::GetNameFromConnectionStateChangeReason(OdinRoomConnectionStateChangeReason reason)
62
- {
63
- switch (reason)
64
- {
65
- case OdinRoomConnectionStateChangeReason_ClientRequested:
66
- return "client_request";
67
- case OdinRoomConnectionStateChangeReason_ServerRequested:
68
- return "server_request";
69
- case OdinRoomConnectionStateChangeReason_ConnectionLost:
70
- return "timeout";
71
- default:
72
- return "unknown";
73
- }
74
- }
75
-
76
- /**
77
- * @brief Basic helper function to get the internal media ID from a specified handle
78
- *
79
- * @param handle The media stream handle to get the ID from
80
- */
81
- uint16_t OdinUtilities::GetMediaIdFromHandle(OdinMediaStreamHandle handle)
82
- {
83
- uint16_t media_id;
84
- int error = odin_media_stream_media_id(handle, &media_id);
85
- return odin_is_error(error) ? 0 : media_id;
86
- }
87
-
88
-
89
- void convert_float32_to_float16(const float *in_buffer, size_t in_buffer_len, short *out_buffer) {
90
- for (size_t i = 0; i < in_buffer_len; i++) {
91
- float f = in_buffer[i];
92
- // Clamp to [-1, 1] range
93
- if (f > 1.0f) {
94
- f = 1.0f;
95
- } else if (f < -1.0f) {
96
- f = -1.0f;
97
- }
98
- // Convert to 16-bit float
99
- int16_t f16 = (int16_t) (f * 32767.0f);
100
- out_buffer[i] = f16;
101
- }
102
32
  }
@@ -1,175 +1,60 @@
1
- #include <odin.h>
2
- #include <napi.h>
3
-
4
1
  #ifndef ODIN_NODEJS_UTILITIES_H
5
2
  #define ODIN_NODEJS_UTILITIES_H
6
3
 
4
+ #include <odin.h>
5
+ #include <napi.h>
6
+ #include <string>
7
+ #include <atomic>
8
+
7
9
  namespace OdinUtilities
8
10
  {
9
- void ThrowNapiException(Napi::Env env, OdinReturnCode error, const char* message);
10
- const char* GetNameFromConnectionState(OdinRoomConnectionState state);
11
- const char* GetNameFromConnectionStateChangeReason(OdinRoomConnectionStateChangeReason reason);
12
- uint16_t GetMediaIdFromHandle(OdinMediaStreamHandle handle);
11
+ void ThrowNapiException(Napi::Env env, OdinError error, const char* message);
13
12
  }
14
13
 
15
- /**
16
- * Event data base structure used to copy OdinEvent data as a base for sending events to JS side
17
- */
18
- struct EventData {
19
- OdinEventTag Tag;
20
- std::string Event;
21
- };
22
-
23
- /**
24
- * Event data structure used to copy OdinEvent data as a base for sending events to JS side including data
25
- */
26
- struct EventDataWithData: EventData
27
- {
28
- const uint8_t *Data;
29
- size_t DataLen;
30
-
31
- void SetData(const uint8_t *data, size_t len)
32
- {
33
- Data = new uint8_t[len];
34
- memcpy((void*)Data, data, len);
35
- DataLen = len;
36
- }
37
-
38
- ~EventDataWithData()
39
- {
40
- delete[] Data;
41
- }
42
- };
43
-
44
- /**
45
- * Event data structure used to copy The Joined event data
46
- */
47
- struct JoinedEventData: EventDataWithData
48
- {
49
- std::string RoomId;
50
- std::string UserId;
51
- uint64_t PeerId;
52
- };
53
-
54
- /**
55
- * Event data structure used to copy The Left event data
56
- */
57
- struct PeerJoinedEventData: EventDataWithData
58
- {
59
- uint64_t PeerId;
60
- std::string UserId;
61
- };
62
-
63
- /**
64
- * Event data structure used to copy The PeerLeft event data
65
- */
66
- struct PeerLeftEventData: EventData
14
+ inline bool odin_is_error(OdinError code)
67
15
  {
68
- uint64_t PeerId;
69
- };
70
-
71
- /**
72
- * Event data structure used to copy The PeerUserDataChanged event data
73
- */
74
- struct PeerUserDataChangedEventData: EventDataWithData
75
- {
76
- uint64_t PeerId;
77
- };
78
-
79
- /**
80
- * Event data structure used to copy The RoomUserDataChanged event data
81
- */
82
- struct RoomUserDataChangedEventData: EventDataWithData
83
- {
84
-
85
- };
86
-
87
- /**
88
- * Event data structure used to copy The MessageReceived event data
89
- */
90
- struct MessageReceivedEventData: EventDataWithData
91
- {
92
- uint64_t PeerId;
93
- };
94
-
95
- /**
96
- * Event data structure used to copy The RoomConnectionStateChanged event data
97
- */
98
- struct RoomConnectionStateChangedEventData: EventData
99
- {
100
- int State;
101
- int Reason;
102
- std::string StateName;
103
- std::string ReasonName;
104
- };
105
-
106
- /**
107
- * Event data structure used to copy The MediaAdded event data
108
- */
109
- struct MediaAddedEventData: EventData
110
- {
111
- uint64_t PeerId;
112
- uint16_t MediaId;
113
- OdinMediaStreamHandle MediaStreamHandle;
114
- };
115
-
116
- /**
117
- * Event data structure used to copy The MediaRemoved event data
118
- */
119
- struct MediaRemovedEventData: EventData
120
- {
121
- uint64_t PeerId;
122
- uint16_t MediaId;
123
- };
16
+ return code != ODIN_ERROR_SUCCESS && code != ODIN_ERROR_NO_DATA;
17
+ }
124
18
 
125
19
  /**
126
- * Event data structure used to copy The MediaActiveStateChanged event data
20
+ * Event data base structure used to copy OdinEvent data as a base for sending events to JS side
127
21
  */
128
- struct MediaActiveStateChangedEventData: EventData
129
- {
130
- uint64_t PeerId;
131
- uint16_t MediaId;
132
- bool Active;
22
+ struct EventData {
23
+ std::string Event;
24
+ std::string Json;
133
25
  };
134
26
 
135
27
  /**
136
- * Data structure to store audio samples used to copy The AudioDataReceived event data
28
+ * Data structure to store audio samples used to copy The AudioDataReceived event data.
29
+ * Also carries a pointer to the pending callback counter for ref-counting.
137
30
  */
138
31
  struct AudioSamples {
139
- short Data[960];
140
- float OriginalData[960];
32
+ short Data[1920];
33
+ float OriginalData[1920];
141
34
  size_t Len;
142
35
  uint64_t PeerId;
143
36
  uint16_t MediaId;
37
+
38
+ // Pointer to atomic counter for tracking pending callbacks
39
+ // Set before NonBlockingCall, decremented in callback after execution
40
+ std::atomic<int>* PendingCounterPtr = nullptr;
144
41
 
145
42
  void ConvertFloat32ToFloat16(const float *in_buffer, size_t in_buffer_len, short *out_buffer) {
146
43
  for (size_t i = 0; i < in_buffer_len; i++) {
147
44
  float f = in_buffer[i];
148
- // Clamp to [-1, 1] range
149
- if (f > 1.0f) {
150
- f = 1.0f;
151
- } else if (f < -1.0f) {
152
- f = -1.0f;
153
- }
154
- // Convert to 16-bit float
155
- int16_t f16 = (int16_t) (f * 32767.0f);
156
- out_buffer[i] = f16;
45
+ if (f > 1.0f) f = 1.0f;
46
+ else if (f < -1.0f) f = -1.0f;
47
+ out_buffer[i] = (int16_t) (f * 32767.0f);
157
48
  }
158
49
  }
159
50
 
160
51
  void SetSamples(const float *samples, size_t len)
161
52
  {
53
+ if (len > 1920) len = 1920;
162
54
  ConvertFloat32ToFloat16(samples, len, Data);
163
55
  memcpy(OriginalData, samples, len * sizeof(float));
164
56
  Len = len;
165
57
  }
166
-
167
- ~AudioSamples()
168
- {
169
- }
170
58
  };
171
59
 
172
-
173
- void convert_float32_to_float16(const float *in_buffer, size_t in_buffer_len, short *out_buffer);
174
-
175
60
  #endif //ODIN_NODEJS_UTILITIES_H