@lng2004/node-datachannel 0.31.0-20251228

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 (171) hide show
  1. package/.clang-format +17 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +8 -0
  4. package/.eslintrc.json +27 -0
  5. package/.gitmodules +3 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +13 -0
  8. package/API.md +247 -0
  9. package/BULDING.md +33 -0
  10. package/CMakeLists.txt +134 -0
  11. package/LICENSE +373 -0
  12. package/README.md +130 -0
  13. package/dist/cjs/index.cjs +11 -0
  14. package/dist/cjs/index.cjs.map +1 -0
  15. package/dist/cjs/lib/datachannel-stream.cjs +101 -0
  16. package/dist/cjs/lib/datachannel-stream.cjs.map +1 -0
  17. package/dist/cjs/lib/index.cjs +88 -0
  18. package/dist/cjs/lib/index.cjs.map +1 -0
  19. package/dist/cjs/lib/node-datachannel.cjs +8 -0
  20. package/dist/cjs/lib/node-datachannel.cjs.map +1 -0
  21. package/dist/cjs/lib/websocket-server.cjs +45 -0
  22. package/dist/cjs/lib/websocket-server.cjs.map +1 -0
  23. package/dist/cjs/lib/websocket.cjs +8 -0
  24. package/dist/cjs/lib/websocket.cjs.map +1 -0
  25. package/dist/cjs/polyfill/Events.cjs +86 -0
  26. package/dist/cjs/polyfill/Events.cjs.map +1 -0
  27. package/dist/cjs/polyfill/Exception.cjs +34 -0
  28. package/dist/cjs/polyfill/Exception.cjs.map +1 -0
  29. package/dist/cjs/polyfill/RTCCertificate.cjs +34 -0
  30. package/dist/cjs/polyfill/RTCCertificate.cjs.map +1 -0
  31. package/dist/cjs/polyfill/RTCDataChannel.cjs +214 -0
  32. package/dist/cjs/polyfill/RTCDataChannel.cjs.map +1 -0
  33. package/dist/cjs/polyfill/RTCDtlsTransport.cjs +53 -0
  34. package/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +1 -0
  35. package/dist/cjs/polyfill/RTCError.cjs +83 -0
  36. package/dist/cjs/polyfill/RTCError.cjs.map +1 -0
  37. package/dist/cjs/polyfill/RTCIceCandidate.cjs +132 -0
  38. package/dist/cjs/polyfill/RTCIceCandidate.cjs.map +1 -0
  39. package/dist/cjs/polyfill/RTCIceTransport.cjs +94 -0
  40. package/dist/cjs/polyfill/RTCIceTransport.cjs.map +1 -0
  41. package/dist/cjs/polyfill/RTCPeerConnection.cjs +434 -0
  42. package/dist/cjs/polyfill/RTCPeerConnection.cjs.map +1 -0
  43. package/dist/cjs/polyfill/RTCSctpTransport.cjs +59 -0
  44. package/dist/cjs/polyfill/RTCSctpTransport.cjs.map +1 -0
  45. package/dist/cjs/polyfill/RTCSessionDescription.cjs +45 -0
  46. package/dist/cjs/polyfill/RTCSessionDescription.cjs.map +1 -0
  47. package/dist/cjs/polyfill/index.cjs +42 -0
  48. package/dist/cjs/polyfill/index.cjs.map +1 -0
  49. package/dist/esm/index.mjs +8 -0
  50. package/dist/esm/index.mjs.map +1 -0
  51. package/dist/esm/lib/datachannel-stream.mjs +78 -0
  52. package/dist/esm/lib/datachannel-stream.mjs.map +1 -0
  53. package/dist/esm/lib/index.mjs +62 -0
  54. package/dist/esm/lib/index.mjs.map +1 -0
  55. package/dist/esm/lib/node-datachannel.mjs +12 -0
  56. package/dist/esm/lib/node-datachannel.mjs.map +1 -0
  57. package/dist/esm/lib/websocket-server.mjs +43 -0
  58. package/dist/esm/lib/websocket-server.mjs.map +1 -0
  59. package/dist/esm/lib/websocket.mjs +6 -0
  60. package/dist/esm/lib/websocket.mjs.map +1 -0
  61. package/dist/esm/polyfill/Events.mjs +82 -0
  62. package/dist/esm/polyfill/Events.mjs.map +1 -0
  63. package/dist/esm/polyfill/Exception.mjs +28 -0
  64. package/dist/esm/polyfill/Exception.mjs.map +1 -0
  65. package/dist/esm/polyfill/RTCCertificate.mjs +30 -0
  66. package/dist/esm/polyfill/RTCCertificate.mjs.map +1 -0
  67. package/dist/esm/polyfill/RTCDataChannel.mjs +210 -0
  68. package/dist/esm/polyfill/RTCDataChannel.mjs.map +1 -0
  69. package/dist/esm/polyfill/RTCDtlsTransport.mjs +49 -0
  70. package/dist/esm/polyfill/RTCDtlsTransport.mjs.map +1 -0
  71. package/dist/esm/polyfill/RTCError.mjs +79 -0
  72. package/dist/esm/polyfill/RTCError.mjs.map +1 -0
  73. package/dist/esm/polyfill/RTCIceCandidate.mjs +128 -0
  74. package/dist/esm/polyfill/RTCIceCandidate.mjs.map +1 -0
  75. package/dist/esm/polyfill/RTCIceTransport.mjs +89 -0
  76. package/dist/esm/polyfill/RTCIceTransport.mjs.map +1 -0
  77. package/dist/esm/polyfill/RTCPeerConnection.mjs +430 -0
  78. package/dist/esm/polyfill/RTCPeerConnection.mjs.map +1 -0
  79. package/dist/esm/polyfill/RTCSctpTransport.mjs +55 -0
  80. package/dist/esm/polyfill/RTCSctpTransport.mjs.map +1 -0
  81. package/dist/esm/polyfill/RTCSessionDescription.mjs +41 -0
  82. package/dist/esm/polyfill/RTCSessionDescription.mjs.map +1 -0
  83. package/dist/esm/polyfill/index.mjs +27 -0
  84. package/dist/esm/polyfill/index.mjs.map +1 -0
  85. package/dist/types/lib/datachannel-stream.d.ts +24 -0
  86. package/dist/types/lib/index.d.ts +235 -0
  87. package/dist/types/lib/types.d.ts +118 -0
  88. package/dist/types/lib/websocket-server.d.ts +13 -0
  89. package/dist/types/lib/websocket.d.ts +25 -0
  90. package/dist/types/polyfill/Events.d.ts +15 -0
  91. package/dist/types/polyfill/RTCCertificate.d.ts +9 -0
  92. package/dist/types/polyfill/RTCDataChannel.d.ts +29 -0
  93. package/dist/types/polyfill/RTCDtlsTransport.d.ts +15 -0
  94. package/dist/types/polyfill/RTCError.d.ts +17 -0
  95. package/dist/types/polyfill/RTCIceCandidate.d.ts +21 -0
  96. package/dist/types/polyfill/RTCIceTransport.d.ts +30 -0
  97. package/dist/types/polyfill/RTCPeerConnection.d.ts +64 -0
  98. package/dist/types/polyfill/RTCSctpTransport.d.ts +15 -0
  99. package/dist/types/polyfill/RTCSessionDescription.d.ts +10 -0
  100. package/dist/types/polyfill/index.d.ts +26 -0
  101. package/jest.config.ts +14 -0
  102. package/package.json +121 -0
  103. package/rollup.config.mjs +72 -0
  104. package/src/cpp/data-channel-wrapper.cpp +530 -0
  105. package/src/cpp/data-channel-wrapper.h +63 -0
  106. package/src/cpp/ice-udp-mux-listener-wrapper.cpp +157 -0
  107. package/src/cpp/ice-udp-mux-listener-wrapper.h +43 -0
  108. package/src/cpp/main.cpp +58 -0
  109. package/src/cpp/media-audio-wrapper.cpp +457 -0
  110. package/src/cpp/media-audio-wrapper.h +52 -0
  111. package/src/cpp/media-av1packetization.cpp +24 -0
  112. package/src/cpp/media-av1packetization.h +11 -0
  113. package/src/cpp/media-av1rtppacketizer-wrapper.cpp +126 -0
  114. package/src/cpp/media-av1rtppacketizer-wrapper.h +29 -0
  115. package/src/cpp/media-direction.cpp +43 -0
  116. package/src/cpp/media-direction.h +10 -0
  117. package/src/cpp/media-h264rtppacketizer-wrapper.cpp +126 -0
  118. package/src/cpp/media-h264rtppacketizer-wrapper.h +30 -0
  119. package/src/cpp/media-h265rtppacketizer-wrapper.cpp +126 -0
  120. package/src/cpp/media-h265rtppacketizer-wrapper.h +30 -0
  121. package/src/cpp/media-h26xseparator.cpp +32 -0
  122. package/src/cpp/media-h26xseparator.h +11 -0
  123. package/src/cpp/media-mediahandler-helper.cpp +31 -0
  124. package/src/cpp/media-mediahandler-helper.h +11 -0
  125. package/src/cpp/media-pacinghandler-wrapper.cpp +79 -0
  126. package/src/cpp/media-pacinghandler-wrapper.h +28 -0
  127. package/src/cpp/media-rtcpnackresponder-wrapper.cpp +68 -0
  128. package/src/cpp/media-rtcpnackresponder-wrapper.h +28 -0
  129. package/src/cpp/media-rtcpreceivingsession-wrapper.cpp +57 -0
  130. package/src/cpp/media-rtcpreceivingsession-wrapper.h +28 -0
  131. package/src/cpp/media-rtcpsrreporter-wrapper.cpp +93 -0
  132. package/src/cpp/media-rtcpsrreporter-wrapper.h +30 -0
  133. package/src/cpp/media-rtppacketizationconfig-wrapper.cpp +167 -0
  134. package/src/cpp/media-rtppacketizationconfig-wrapper.h +35 -0
  135. package/src/cpp/media-rtppacketizer-wrapper.cpp +95 -0
  136. package/src/cpp/media-rtppacketizer-wrapper.h +30 -0
  137. package/src/cpp/media-track-wrapper.cpp +458 -0
  138. package/src/cpp/media-track-wrapper.h +61 -0
  139. package/src/cpp/media-video-wrapper.cpp +526 -0
  140. package/src/cpp/media-video-wrapper.h +56 -0
  141. package/src/cpp/peer-connection-wrapper.cpp +1298 -0
  142. package/src/cpp/peer-connection-wrapper.h +89 -0
  143. package/src/cpp/rtc-wrapper.cpp +205 -0
  144. package/src/cpp/rtc-wrapper.h +24 -0
  145. package/src/cpp/thread-safe-callback.cpp +57 -0
  146. package/src/cpp/thread-safe-callback.h +47 -0
  147. package/src/cpp/web-socket-server-wrapper.cpp +275 -0
  148. package/src/cpp/web-socket-server-wrapper.h +41 -0
  149. package/src/cpp/web-socket-wrapper.cpp +796 -0
  150. package/src/cpp/web-socket-wrapper.h +63 -0
  151. package/src/index.ts +9 -0
  152. package/src/lib/datachannel-stream.ts +100 -0
  153. package/src/lib/index.ts +283 -0
  154. package/src/lib/node-datachannel.ts +3 -0
  155. package/src/lib/types.ts +168 -0
  156. package/src/lib/websocket-server.ts +37 -0
  157. package/src/lib/websocket.ts +26 -0
  158. package/src/polyfill/Events.ts +82 -0
  159. package/src/polyfill/Exception.ts +37 -0
  160. package/src/polyfill/README.md +41 -0
  161. package/src/polyfill/RTCCertificate.ts +21 -0
  162. package/src/polyfill/RTCDataChannel.ts +225 -0
  163. package/src/polyfill/RTCDtlsTransport.ts +46 -0
  164. package/src/polyfill/RTCError.ts +78 -0
  165. package/src/polyfill/RTCIceCandidate.ts +128 -0
  166. package/src/polyfill/RTCIceTransport.ts +90 -0
  167. package/src/polyfill/RTCPeerConnection.ts +527 -0
  168. package/src/polyfill/RTCSctpTransport.ts +51 -0
  169. package/src/polyfill/RTCSessionDescription.ts +41 -0
  170. package/src/polyfill/index.ts +38 -0
  171. package/tsconfig.json +21 -0
@@ -0,0 +1,95 @@
1
+ #include "media-rtppacketizer-wrapper.h"
2
+ #include "media-rtppacketizationconfig-wrapper.h"
3
+ #include "media-mediahandler-helper.h"
4
+
5
+ Napi::FunctionReference RtpPacketizerWrapper::constructor = Napi::FunctionReference();
6
+
7
+ Napi::Object RtpPacketizerWrapper::Init(Napi::Env env, Napi::Object exports)
8
+ {
9
+ Napi::HandleScope scope(env);
10
+
11
+ Napi::Function func = Napi::ObjectWrap<RtpPacketizerWrapper>::DefineClass(env, "RtpPacketizer",
12
+ {
13
+ // Instance Methods
14
+ InstanceMethod("addToChain", &RtpPacketizerWrapper::addToChain),
15
+ // Accessors
16
+ InstanceAccessor("rtpConfig", &RtpPacketizerWrapper::getRtpPacketizationConfig, nullptr),
17
+ });
18
+
19
+ // If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
20
+ if (constructor.IsEmpty())
21
+ {
22
+ constructor = Napi::Persistent(func);
23
+ constructor.SuppressDestruct();
24
+ }
25
+
26
+ exports.Set("RtpPacketizer", func);
27
+ return exports;
28
+ }
29
+
30
+ RtpPacketizerWrapper::RtpPacketizerWrapper(const Napi::CallbackInfo &info)
31
+ : Napi::ObjectWrap<RtpPacketizerWrapper>(info)
32
+ {
33
+ Napi::Env env = info.Env();
34
+
35
+ if (!info[0].IsObject())
36
+ {
37
+ Napi::TypeError::New(env, "rtpConfig must be a RtpPacketizationConfig instance").ThrowAsJavaScriptException();
38
+ return;
39
+ }
40
+ auto obj = info[0].As<Napi::Object>();
41
+ if (!obj.InstanceOf(RtpPacketizationConfigWrapper::constructor.Value()))
42
+ {
43
+ Napi::TypeError::New(env, "rtpConfig must be a RtpPacketizationConfig instance").ThrowAsJavaScriptException();
44
+ return;
45
+ }
46
+ // store original JS object so we can return it later
47
+ mRtpConfigObject = Napi::Persistent(obj);
48
+ mRtpConfigObject.SuppressDestruct();
49
+ auto rtpConfig = RtpPacketizationConfigWrapper::Unwrap(obj)->getConfigInstance();
50
+
51
+ mPacketizerPtr = std::make_unique<rtc::RtpPacketizer>(rtpConfig);
52
+
53
+ auto asMediaHandler = new std::shared_ptr<rtc::MediaHandler>(mPacketizerPtr);
54
+ }
55
+
56
+ RtpPacketizerWrapper::~RtpPacketizerWrapper()
57
+ {
58
+ mPacketizerPtr.reset();
59
+ mRtpConfigObject.Reset();
60
+ }
61
+
62
+ std::shared_ptr<rtc::RtpPacketizer> RtpPacketizerWrapper::getPacketizerInstance() { return mPacketizerPtr; }
63
+
64
+ void RtpPacketizerWrapper::addToChain(const Napi::CallbackInfo &info)
65
+ {
66
+ auto env = info.Env();
67
+ if (info.Length() < 1 || !info[0].IsObject())
68
+ {
69
+ Napi::TypeError::New(env, "Expected a MediaHandler instance").ThrowAsJavaScriptException();
70
+ return;
71
+ }
72
+ auto mediaHandler = asMediaHandler(info[0].As<Napi::Object>());
73
+ if (!mediaHandler)
74
+ {
75
+ Napi::TypeError::New(env, "Expected a MediaHandler instance. If this error is unexpected, please report a bug!")
76
+ .ThrowAsJavaScriptException();
77
+ return;
78
+ }
79
+ mPacketizerPtr->addToChain(mediaHandler);
80
+ }
81
+
82
+ Napi::Value RtpPacketizerWrapper::getRtpPacketizationConfig(const Napi::CallbackInfo &info)
83
+ {
84
+ Napi::Env env = info.Env();
85
+ if (!mPacketizerPtr)
86
+ {
87
+ Napi::Error::New(env, "getRtpPacketizationConfig() called on destroyed packetizer").ThrowAsJavaScriptException();
88
+ return env.Null();
89
+ }
90
+
91
+ if (mRtpConfigObject.IsEmpty())
92
+ return env.Null();
93
+
94
+ return mRtpConfigObject.Value();
95
+ }
@@ -0,0 +1,30 @@
1
+ #ifndef MEDIA_RTPPACKETIZER_WRAPPER_H
2
+ #define MEDIA_RTPPACKETIZER_WRAPPER_H
3
+
4
+ #include <memory>
5
+ #include <unordered_set>
6
+
7
+ #include <napi.h>
8
+ #include <rtc/rtc.hpp>
9
+
10
+ class RtpPacketizerWrapper : public Napi::ObjectWrap<RtpPacketizerWrapper>
11
+ {
12
+ public:
13
+ static Napi::FunctionReference constructor;
14
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
15
+ RtpPacketizerWrapper(const Napi::CallbackInfo &info);
16
+ ~RtpPacketizerWrapper();
17
+ std::shared_ptr<rtc::RtpPacketizer> getPacketizerInstance();
18
+
19
+ // Functions
20
+ void addToChain(const Napi::CallbackInfo &info);
21
+ Napi::Value getRtpPacketizationConfig(const Napi::CallbackInfo &info);
22
+
23
+ // Callbacks
24
+
25
+ private:
26
+ std::shared_ptr<rtc::RtpPacketizer> mPacketizerPtr = nullptr;
27
+ Napi::ObjectReference mRtpConfigObject;
28
+ };
29
+
30
+ #endif // MEDIA_RTPPACKETIZER_WRAPPER_H
@@ -0,0 +1,458 @@
1
+ #include "media-track-wrapper.h"
2
+ #include "media-direction.h"
3
+ #include "media-mediahandler-helper.h"
4
+
5
+ #include "plog/Log.h"
6
+
7
+ Napi::FunctionReference TrackWrapper::constructor = Napi::FunctionReference();
8
+ std::unordered_set<TrackWrapper *> TrackWrapper::instances;
9
+
10
+ void TrackWrapper::CloseAll()
11
+ {
12
+ auto copy(instances);
13
+ for (auto inst : copy)
14
+ inst->doClose();
15
+ }
16
+
17
+ void TrackWrapper::CleanupAll()
18
+ {
19
+ PLOG_DEBUG << "CleanupAll() called";
20
+ auto copy(instances);
21
+ for (auto inst : copy)
22
+ inst->doCleanup();
23
+ }
24
+
25
+ Napi::Object TrackWrapper::Init(Napi::Env env, Napi::Object exports)
26
+ {
27
+ Napi::HandleScope scope(env);
28
+
29
+ Napi::Function func = DefineClass(env, "Track",
30
+ {
31
+ InstanceMethod("direction", &TrackWrapper::direction),
32
+ InstanceMethod("mid", &TrackWrapper::mid),
33
+ InstanceMethod("type", &TrackWrapper::type),
34
+ InstanceMethod("close", &TrackWrapper::close),
35
+ InstanceMethod("sendMessage", &TrackWrapper::sendMessage),
36
+ InstanceMethod("sendMessageBinary", &TrackWrapper::sendMessageBinary),
37
+ InstanceMethod("isOpen", &TrackWrapper::isOpen),
38
+ InstanceMethod("isClosed", &TrackWrapper::isClosed),
39
+ InstanceMethod("maxMessageSize", &TrackWrapper::maxMessageSize),
40
+ InstanceMethod("requestBitrate", &TrackWrapper::requestBitrate),
41
+ InstanceMethod("requestKeyframe", &TrackWrapper::requestKeyframe),
42
+ InstanceMethod("setMediaHandler", &TrackWrapper::setMediaHandler),
43
+ InstanceMethod("onOpen", &TrackWrapper::onOpen),
44
+ InstanceMethod("onClosed", &TrackWrapper::onClosed),
45
+ InstanceMethod("onError", &TrackWrapper::onError),
46
+ InstanceMethod("onMessage", &TrackWrapper::onMessage),
47
+ });
48
+
49
+ // If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
50
+ if (constructor.IsEmpty())
51
+ {
52
+ constructor = Napi::Persistent(func);
53
+ constructor.SuppressDestruct();
54
+ }
55
+
56
+ exports.Set("Track", func);
57
+ return exports;
58
+ }
59
+
60
+ TrackWrapper::TrackWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<TrackWrapper>(info)
61
+ {
62
+ PLOG_DEBUG << "Constructor called";
63
+ mTrackPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::Track>>>().Data());
64
+ PLOG_DEBUG << "Track created";
65
+
66
+ // Closed callback must be set to trigger cleanup
67
+ mOnClosedCallback =
68
+ std::make_unique<ThreadSafeCallback>(Napi::Function::New(info.Env(), [](const Napi::CallbackInfo &) {}));
69
+
70
+ instances.insert(this);
71
+ }
72
+
73
+ TrackWrapper::~TrackWrapper() { doClose(); }
74
+
75
+ void TrackWrapper::doClose()
76
+ {
77
+ if (mTrackPtr)
78
+ {
79
+ try
80
+ {
81
+ mTrackPtr->close();
82
+ mTrackPtr.reset();
83
+ }
84
+ catch (std::exception &ex)
85
+ {
86
+ std::cerr << std::string("libdatachannel error while closing track: ") + ex.what() << std::endl;
87
+ return;
88
+ }
89
+ }
90
+
91
+ mOnOpenCallback.reset();
92
+ mOnErrorCallback.reset();
93
+ mOnMessageCallback.reset();
94
+ }
95
+
96
+ void TrackWrapper::doCleanup()
97
+ {
98
+ PLOG_DEBUG << "doCleanup() called";
99
+ mOnClosedCallback.reset();
100
+ instances.erase(this);
101
+ }
102
+
103
+ void TrackWrapper::close(const Napi::CallbackInfo &info) { doClose(); }
104
+
105
+ Napi::Value TrackWrapper::direction(const Napi::CallbackInfo &info)
106
+ {
107
+ if (!mTrackPtr)
108
+ {
109
+ Napi::Error::New(info.Env(), "direction() called on destroyed track").ThrowAsJavaScriptException();
110
+ return info.Env().Null();
111
+ }
112
+
113
+ Napi::Env env = info.Env();
114
+ return Napi::String::New(env, directionToStr(mTrackPtr->direction()));
115
+ }
116
+
117
+ Napi::Value TrackWrapper::mid(const Napi::CallbackInfo &info)
118
+ {
119
+ if (!mTrackPtr)
120
+ {
121
+ Napi::Error::New(info.Env(), "mid() called on destroyed track").ThrowAsJavaScriptException();
122
+ return info.Env().Null();
123
+ }
124
+
125
+ Napi::Env env = info.Env();
126
+ return Napi::String::New(env, mTrackPtr->mid());
127
+ }
128
+
129
+ Napi::Value TrackWrapper::type(const Napi::CallbackInfo &info)
130
+ {
131
+ if (!mTrackPtr)
132
+ {
133
+ Napi::Error::New(info.Env(), "type() called on destroyed track").ThrowAsJavaScriptException();
134
+ return info.Env().Null();
135
+ }
136
+
137
+ Napi::Env env = info.Env();
138
+ return Napi::String::New(env, mTrackPtr->description().type());
139
+ }
140
+
141
+ Napi::Value TrackWrapper::sendMessage(const Napi::CallbackInfo &info)
142
+ {
143
+ if (!mTrackPtr)
144
+ {
145
+ Napi::Error::New(info.Env(), "sendMessage() called on destroyed track").ThrowAsJavaScriptException();
146
+ return info.Env().Null();
147
+ }
148
+
149
+ Napi::Env env = info.Env();
150
+ int length = info.Length();
151
+
152
+ // Allow call with NULL
153
+ if (length < 1 || (!info[0].IsString() && !info[0].IsNull()))
154
+ {
155
+ Napi::TypeError::New(env, "String or Null expected").ThrowAsJavaScriptException();
156
+ return info.Env().Null();
157
+ }
158
+
159
+ try
160
+ {
161
+ return Napi::Boolean::New(info.Env(), mTrackPtr->send(info[0].As<Napi::String>().ToString()));
162
+ }
163
+ catch (std::exception &ex)
164
+ {
165
+ Napi::Error::New(env, std::string("libdatachannel error while sending track message: ") + ex.what())
166
+ .ThrowAsJavaScriptException();
167
+ return Napi::Boolean::New(info.Env(), false);
168
+ }
169
+ }
170
+
171
+ Napi::Value TrackWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
172
+ {
173
+ if (!mTrackPtr)
174
+ {
175
+ Napi::Error::New(info.Env(), "sendMessageBinary() called on destroyed track").ThrowAsJavaScriptException();
176
+ return info.Env().Null();
177
+ }
178
+
179
+ Napi::Env env = info.Env();
180
+ int length = info.Length();
181
+
182
+ if (length < 1 || !info[0].IsBuffer())
183
+ {
184
+ Napi::TypeError::New(env, "Buffer expected").ThrowAsJavaScriptException();
185
+ return info.Env().Null();
186
+ }
187
+
188
+ try
189
+ {
190
+ Napi::Uint8Array buffer = info[0].As<Napi::Uint8Array>();
191
+ return Napi::Boolean::New(info.Env(), mTrackPtr->send((std::byte *)buffer.Data(), buffer.ByteLength()));
192
+ }
193
+ catch (std::exception &ex)
194
+ {
195
+ Napi::Error::New(env, std::string("libdatachannel error while sending track message: ") + ex.what())
196
+ .ThrowAsJavaScriptException();
197
+ return Napi::Boolean::New(info.Env(), false);
198
+ }
199
+ }
200
+
201
+ Napi::Value TrackWrapper::isOpen(const Napi::CallbackInfo &info)
202
+ {
203
+ Napi::Env env = info.Env();
204
+
205
+ if (!mTrackPtr)
206
+ {
207
+ return Napi::Boolean::New(env, false);
208
+ }
209
+
210
+ return Napi::Boolean::New(env, mTrackPtr->isOpen());
211
+ }
212
+
213
+ Napi::Value TrackWrapper::isClosed(const Napi::CallbackInfo &info)
214
+ {
215
+ Napi::Env env = info.Env();
216
+
217
+ if (!mTrackPtr)
218
+ {
219
+ return Napi::Boolean::New(env, true);
220
+ }
221
+
222
+ return Napi::Boolean::New(env, mTrackPtr->isClosed());
223
+ }
224
+
225
+ Napi::Value TrackWrapper::maxMessageSize(const Napi::CallbackInfo &info)
226
+ {
227
+ Napi::Env env = info.Env();
228
+
229
+ if (!mTrackPtr)
230
+ {
231
+ return Napi::Number::New(info.Env(), 0);
232
+ }
233
+
234
+ try
235
+ {
236
+ return Napi::Number::New(info.Env(), mTrackPtr->maxMessageSize());
237
+ }
238
+ catch (std::exception &ex)
239
+ {
240
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
241
+ return Napi::Number::New(info.Env(), 0);
242
+ }
243
+ }
244
+
245
+ Napi::Value TrackWrapper::requestBitrate(const Napi::CallbackInfo &info)
246
+ {
247
+ if (!mTrackPtr)
248
+ {
249
+ Napi::Error::New(info.Env(), "requestBitrate() called on destroyed track").ThrowAsJavaScriptException();
250
+ return info.Env().Null();
251
+ }
252
+
253
+ Napi::Env env = info.Env();
254
+ int length = info.Length();
255
+
256
+ if (length < 1 || !info[0].IsNumber())
257
+ {
258
+ Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
259
+ return info.Env().Null();
260
+ }
261
+
262
+ unsigned int bitrate = static_cast<uint32_t>(info[0].As<Napi::Number>());
263
+ return Napi::Boolean::New(env, mTrackPtr->requestBitrate(bitrate));
264
+ }
265
+
266
+ Napi::Value TrackWrapper::requestKeyframe(const Napi::CallbackInfo &info)
267
+ {
268
+ if (!mTrackPtr)
269
+ {
270
+ Napi::Error::New(info.Env(), "requestKeyframe() called on destroyed track").ThrowAsJavaScriptException();
271
+ return info.Env().Null();
272
+ }
273
+
274
+ Napi::Env env = info.Env();
275
+ return Napi::Boolean::New(env, mTrackPtr->requestKeyframe());
276
+ }
277
+
278
+ void TrackWrapper::setMediaHandler(const Napi::CallbackInfo &info)
279
+ {
280
+ if (!mTrackPtr)
281
+ {
282
+ Napi::Error::New(info.Env(), "setMediaHandler() called on destroyed track").ThrowAsJavaScriptException();
283
+ return;
284
+ }
285
+
286
+ Napi::Env env = info.Env();
287
+ int length = info.Length();
288
+
289
+ if (length < 1 || !info[0].IsObject())
290
+ {
291
+ Napi::TypeError::New(env, "MediaHandler class instance expected").ThrowAsJavaScriptException();
292
+ return;
293
+ }
294
+
295
+ std::shared_ptr<rtc::MediaHandler> handler = asMediaHandler(info[0].As<Napi::Object>());
296
+ if (!handler)
297
+ {
298
+ Napi::TypeError::New(env, "MediaHandler class instance expected. If this error is unexpected, please report a bug!")
299
+ .ThrowAsJavaScriptException();
300
+ return;
301
+ }
302
+ mTrackPtr->setMediaHandler(handler);
303
+ }
304
+
305
+ void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
306
+ {
307
+ if (!mTrackPtr)
308
+ {
309
+ Napi::Error::New(info.Env(), "onOpen() called on destroyed track").ThrowAsJavaScriptException();
310
+ return;
311
+ }
312
+
313
+ Napi::Env env = info.Env();
314
+ int length = info.Length();
315
+
316
+ if (length < 1 || !info[0].IsFunction())
317
+ {
318
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
319
+ return;
320
+ }
321
+
322
+ // Callback
323
+ mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
324
+
325
+ mTrackPtr->onOpen(
326
+ [&]()
327
+ {
328
+ if (mOnOpenCallback)
329
+ mOnOpenCallback->call(
330
+ [this](Napi::Env env, std::vector<napi_value> &args)
331
+ {
332
+ // Check the track is not closed
333
+ if (instances.find(this) == instances.end())
334
+ throw ThreadSafeCallback::CancelException();
335
+
336
+ // This will run in main thread and needs to construct the
337
+ // arguments for the call
338
+ args = {};
339
+ });
340
+ });
341
+ }
342
+
343
+ void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
344
+ {
345
+ if (!mTrackPtr)
346
+ {
347
+ Napi::Error::New(info.Env(), "onClosed() called on destroyed track").ThrowAsJavaScriptException();
348
+ return;
349
+ }
350
+
351
+ Napi::Env env = info.Env();
352
+ int length = info.Length();
353
+
354
+ if (length < 1 || !info[0].IsFunction())
355
+ {
356
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
357
+ return;
358
+ }
359
+
360
+ // Callback
361
+ mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
362
+
363
+ mTrackPtr->onClosed(
364
+ [&]()
365
+ {
366
+ if (mOnClosedCallback)
367
+ mOnClosedCallback->call(
368
+ [this](Napi::Env env, std::vector<napi_value> &args)
369
+ {
370
+ // Do not check if the data channel has been closed here
371
+
372
+ // This will run in main thread and needs to construct the
373
+ // arguments for the call
374
+ args = {};
375
+ },
376
+ [this] { doCleanup(); });
377
+ });
378
+ }
379
+
380
+ void TrackWrapper::onError(const Napi::CallbackInfo &info)
381
+ {
382
+ if (!mTrackPtr)
383
+ {
384
+ Napi::Error::New(info.Env(), "onError() called on destroyed track").ThrowAsJavaScriptException();
385
+ return;
386
+ }
387
+
388
+ Napi::Env env = info.Env();
389
+ int length = info.Length();
390
+
391
+ if (length < 1 || !info[0].IsFunction())
392
+ {
393
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
394
+ return;
395
+ }
396
+
397
+ // Callback
398
+ mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
399
+
400
+ mTrackPtr->onError(
401
+ [&](const std::string &error)
402
+ {
403
+ if (mOnErrorCallback)
404
+ mOnErrorCallback->call(
405
+ [this, error](Napi::Env env, std::vector<napi_value> &args)
406
+ {
407
+ // Check the track is not closed
408
+ if (instances.find(this) == instances.end())
409
+ throw ThreadSafeCallback::CancelException();
410
+
411
+ // This will run in main thread and needs to construct the
412
+ // arguments for the call
413
+ args = {Napi::String::New(env, error)};
414
+ });
415
+ });
416
+ }
417
+
418
+ void TrackWrapper::onMessage(const Napi::CallbackInfo &info)
419
+ {
420
+ if (!mTrackPtr)
421
+ {
422
+ Napi::Error::New(info.Env(), "onMessage() called on destroyed track").ThrowAsJavaScriptException();
423
+ return;
424
+ }
425
+
426
+ Napi::Env env = info.Env();
427
+ int length = info.Length();
428
+
429
+ if (length < 1 || !info[0].IsFunction())
430
+ {
431
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
432
+ return;
433
+ }
434
+
435
+ // Callback
436
+ mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
437
+
438
+ mTrackPtr->onMessage(
439
+ [&](std::variant<rtc::binary, std::string> message)
440
+ {
441
+ if (mOnMessageCallback)
442
+ mOnMessageCallback->call(
443
+ [this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args)
444
+ {
445
+ // Check the track is not closed
446
+ if (instances.find(this) == instances.end())
447
+ throw ThreadSafeCallback::CancelException();
448
+
449
+ // This will run in main thread and needs to construct the
450
+ // arguments for the call
451
+ if (std::holds_alternative<rtc::binary>(message)) // Track will always receive messages as binary
452
+ {
453
+ auto bin = std::get<rtc::binary>(std::move(message));
454
+ args = {Napi::Buffer<std::byte>::Copy(env, bin.data(), bin.size())};
455
+ }
456
+ });
457
+ });
458
+ }
@@ -0,0 +1,61 @@
1
+ #ifndef MEDIA_TRACK_WRAPPER_H
2
+ #define MEDIA_TRACK_WRAPPER_H
3
+
4
+ #include <memory>
5
+ #include <unordered_set>
6
+
7
+ #include <napi.h>
8
+ #include <rtc/rtc.hpp>
9
+
10
+ #include "thread-safe-callback.h"
11
+
12
+ class TrackWrapper : public Napi::ObjectWrap<TrackWrapper>
13
+ {
14
+ public:
15
+ static Napi::FunctionReference constructor;
16
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
17
+ TrackWrapper(const Napi::CallbackInfo &info);
18
+ ~TrackWrapper();
19
+
20
+ // Functions
21
+ Napi::Value direction(const Napi::CallbackInfo &info);
22
+ Napi::Value mid(const Napi::CallbackInfo &info);
23
+ Napi::Value type(const Napi::CallbackInfo &info);
24
+ void close(const Napi::CallbackInfo &info);
25
+ Napi::Value sendMessage(const Napi::CallbackInfo &info);
26
+ Napi::Value sendMessageBinary(const Napi::CallbackInfo &info);
27
+ Napi::Value isOpen(const Napi::CallbackInfo &info);
28
+ Napi::Value isClosed(const Napi::CallbackInfo &info);
29
+ Napi::Value maxMessageSize(const Napi::CallbackInfo &info);
30
+ Napi::Value requestBitrate(const Napi::CallbackInfo &info);
31
+ Napi::Value requestKeyframe(const Napi::CallbackInfo &info);
32
+ void setMediaHandler(const Napi::CallbackInfo &info);
33
+
34
+ // Callbacks
35
+ void onOpen(const Napi::CallbackInfo &info);
36
+ void onClosed(const Napi::CallbackInfo &info);
37
+ void onError(const Napi::CallbackInfo &info);
38
+ void onMessage(const Napi::CallbackInfo &info);
39
+
40
+ // Close all existing tracks
41
+ static void CloseAll();
42
+
43
+ // Reset all Callbacks for existing tracks
44
+ static void CleanupAll();
45
+
46
+ private:
47
+ static std::unordered_set<TrackWrapper *> instances;
48
+
49
+ void doClose();
50
+ void doCleanup();
51
+
52
+ std::shared_ptr<rtc::Track> mTrackPtr = nullptr;
53
+
54
+ // Callback Ptrs
55
+ std::unique_ptr<ThreadSafeCallback> mOnOpenCallback = nullptr;
56
+ std::unique_ptr<ThreadSafeCallback> mOnClosedCallback = nullptr;
57
+ std::unique_ptr<ThreadSafeCallback> mOnErrorCallback = nullptr;
58
+ std::unique_ptr<ThreadSafeCallback> mOnMessageCallback = nullptr;
59
+ };
60
+
61
+ #endif // MEDIA_TRACK_WRAPPER_H