node-datachannel 0.3.0 → 0.3.3

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 (54) hide show
  1. package/.github/workflows/build-linux.yml +1 -1
  2. package/.github/workflows/build-mac.yml +1 -1
  3. package/.github/workflows/build-win.yml +1 -1
  4. package/.github/workflows/lint.yml +19 -0
  5. package/CMakeLists.txt +3 -3
  6. package/README.md +2 -0
  7. package/examples/client-server/client-benchmark.js +43 -32
  8. package/examples/client-server/client-periodic.js +30 -14
  9. package/examples/client-server/client.js +6 -5
  10. package/examples/client-server/package-lock.json +183 -44
  11. package/examples/client-server/signaling-server.js +3 -5
  12. package/examples/media/media.js +9 -9
  13. package/lib/index.d.ts +102 -101
  14. package/node_modules/lru-cache/package.json +2 -4
  15. package/node_modules/minimist/package.json +0 -1
  16. package/node_modules/node-abi/node_modules/semver/README.md +3 -1
  17. package/node_modules/node-abi/node_modules/semver/bin/semver.js +19 -9
  18. package/node_modules/node-abi/node_modules/semver/classes/comparator.js +3 -2
  19. package/node_modules/node-abi/node_modules/semver/classes/index.js +1 -1
  20. package/node_modules/node-abi/node_modules/semver/classes/range.js +31 -22
  21. package/node_modules/node-abi/node_modules/semver/classes/semver.js +1 -1
  22. package/node_modules/node-abi/node_modules/semver/functions/cmp.js +8 -4
  23. package/node_modules/node-abi/node_modules/semver/functions/coerce.js +3 -2
  24. package/node_modules/node-abi/node_modules/semver/functions/inc.js +4 -1
  25. package/node_modules/node-abi/node_modules/semver/functions/parse.js +1 -1
  26. package/node_modules/node-abi/node_modules/semver/internal/constants.js +2 -2
  27. package/node_modules/node-abi/node_modules/semver/internal/identifiers.js +1 -1
  28. package/node_modules/node-abi/node_modules/semver/internal/parse-options.js +3 -3
  29. package/node_modules/node-abi/node_modules/semver/internal/re.js +3 -3
  30. package/node_modules/node-abi/node_modules/semver/package.json +51 -18
  31. package/node_modules/node-abi/node_modules/semver/ranges/min-version.js +2 -1
  32. package/node_modules/node-abi/node_modules/semver/ranges/outside.js +1 -1
  33. package/node_modules/node-abi/node_modules/semver/ranges/simplify.js +15 -12
  34. package/node_modules/node-abi/node_modules/semver/ranges/subset.js +53 -31
  35. package/node_modules/npmlog/node_modules/are-we-there-yet/package.json +10 -10
  36. package/node_modules/npmlog/package.json +1 -1
  37. package/node_modules/tar-stream/node_modules/bl/.travis.yml +4 -3
  38. package/node_modules/tar-stream/node_modules/bl/BufferList.js +1 -1
  39. package/node_modules/tar-stream/node_modules/bl/package.json +10 -10
  40. package/node_modules/tar-stream/node_modules/bl/test/test.js +20 -2
  41. package/node_modules/wide-align/LICENSE +0 -0
  42. package/node_modules/wide-align/README.md +0 -0
  43. package/node_modules/wide-align/align.js +0 -0
  44. package/node_modules/wide-align/package.json +14 -14
  45. package/package.json +2 -2
  46. package/src/data-channel-wrapper.cpp +69 -53
  47. package/src/data-channel-wrapper.h +0 -1
  48. package/src/media-track-wrapper.cpp +91 -52
  49. package/src/media-track-wrapper.h +0 -1
  50. package/src/peer-connection-wrapper.cpp +111 -71
  51. package/src/peer-connection-wrapper.h +0 -2
  52. package/test/connectivity.js +18 -17
  53. package/test/test.js +22 -30
  54. package/node_modules/node-abi/node_modules/semver/CHANGELOG.md +0 -111
@@ -44,26 +44,8 @@ Napi::Object DataChannelWrapper::Init(Napi::Env env, Napi::Object exports)
44
44
 
45
45
  DataChannelWrapper::DataChannelWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<DataChannelWrapper>(info)
46
46
  {
47
- mDataChannelPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::DataChannel>>>().Data());
47
+ mDataChannelPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::DataChannel>>>().Data());
48
48
 
49
- mDataChannelPtr->onClosed([&]()
50
- {
51
- if (mOnClosedCallback)
52
- mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
53
- // Check the peer connection is not closed
54
- if(instances.find(this) == instances.end())
55
- throw ThreadSafeCallback::CancelException();
56
-
57
- cleanCbsAndEraseInstance();
58
-
59
- // This will run in main thread and needs to construct the
60
- // arguments for the call
61
- args = {};
62
- });
63
- else {
64
- cleanCbsAndEraseInstance();
65
- } });
66
-
67
49
  instances.insert(this);
68
50
  }
69
51
 
@@ -79,17 +61,15 @@ void DataChannelWrapper::doClose()
79
61
  try
80
62
  {
81
63
  mDataChannelPtr->close();
64
+ mDataChannelPtr.reset();
82
65
  }
83
66
  catch (std::exception &ex)
84
67
  {
85
- std::cout << std::string("libdatachannel error while closing dataChannel# ") + ex.what() << std::endl;
68
+ std::cerr << std::string("libdatachannel error while closing data channel: ") + ex.what() << std::endl;
86
69
  return;
87
70
  }
88
71
  }
89
- }
90
72
 
91
- void DataChannelWrapper::cleanCbsAndEraseInstance()
92
- {
93
73
  mOnOpenCallback.reset();
94
74
  mOnClosedCallback.reset();
95
75
  mOnErrorCallback.reset();
@@ -97,7 +77,6 @@ void DataChannelWrapper::cleanCbsAndEraseInstance()
97
77
  mOnMessageCallback.reset();
98
78
 
99
79
  instances.erase(this);
100
- mDataChannelPtr.reset();
101
80
  }
102
81
 
103
82
  void DataChannelWrapper::close(const Napi::CallbackInfo &info)
@@ -109,7 +88,7 @@ Napi::Value DataChannelWrapper::getLabel(const Napi::CallbackInfo &info)
109
88
  {
110
89
  if (!mDataChannelPtr)
111
90
  {
112
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
91
+ Napi::Error::New(info.Env(), "getLabel() called on destroyed channel").ThrowAsJavaScriptException();
113
92
  return info.Env().Null();
114
93
  }
115
94
 
@@ -120,7 +99,7 @@ Napi::Value DataChannelWrapper::getId(const Napi::CallbackInfo &info)
120
99
  {
121
100
  if (!mDataChannelPtr)
122
101
  {
123
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
102
+ Napi::Error::New(info.Env(), "getId() called on destroyed channel").ThrowAsJavaScriptException();
124
103
  return info.Env().Null();
125
104
  }
126
105
 
@@ -131,7 +110,7 @@ Napi::Value DataChannelWrapper::getProtocol(const Napi::CallbackInfo &info)
131
110
  {
132
111
  if (!mDataChannelPtr)
133
112
  {
134
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
113
+ Napi::Error::New(info.Env(), "getProtocol() called on destroyed channel").ThrowAsJavaScriptException();
135
114
  return info.Env().Null();
136
115
  }
137
116
 
@@ -142,7 +121,7 @@ Napi::Value DataChannelWrapper::sendMessage(const Napi::CallbackInfo &info)
142
121
  {
143
122
  if (!mDataChannelPtr)
144
123
  {
145
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
124
+ Napi::Error::New(info.Env(), "sendMessage() called on destroyed channel").ThrowAsJavaScriptException();
146
125
  return info.Env().Null();
147
126
  }
148
127
 
@@ -162,7 +141,7 @@ Napi::Value DataChannelWrapper::sendMessage(const Napi::CallbackInfo &info)
162
141
  }
163
142
  catch (std::exception &ex)
164
143
  {
165
- Napi::Error::New(env, std::string("libdatachannel error while sending dataChannel msg# ") + ex.what()).ThrowAsJavaScriptException();
144
+ Napi::Error::New(env, std::string("libdatachannel error while sending data channel message: ") + ex.what()).ThrowAsJavaScriptException();
166
145
  return Napi::Boolean::New(info.Env(), false);
167
146
  }
168
147
  }
@@ -171,7 +150,7 @@ Napi::Value DataChannelWrapper::sendMessageBinary(const Napi::CallbackInfo &info
171
150
  {
172
151
  if (!mDataChannelPtr)
173
152
  {
174
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
153
+ Napi::Error::New(info.Env(), "sendMessagBinary() called on destroyed channel").ThrowAsJavaScriptException();
175
154
  return info.Env().Null();
176
155
  }
177
156
 
@@ -191,7 +170,7 @@ Napi::Value DataChannelWrapper::sendMessageBinary(const Napi::CallbackInfo &info
191
170
  }
192
171
  catch (std::exception &ex)
193
172
  {
194
- Napi::Error::New(env, std::string("libdatachannel error while sending dataChannel msg# ") + ex.what()).ThrowAsJavaScriptException();
173
+ Napi::Error::New(env, std::string("libdatachannel error while sending data channel message: ") + ex.what()).ThrowAsJavaScriptException();
195
174
  return Napi::Boolean::New(info.Env(), false);
196
175
  }
197
176
  }
@@ -199,6 +178,7 @@ Napi::Value DataChannelWrapper::sendMessageBinary(const Napi::CallbackInfo &info
199
178
  Napi::Value DataChannelWrapper::isOpen(const Napi::CallbackInfo &info)
200
179
  {
201
180
  Napi::Env env = info.Env();
181
+
202
182
  if (!mDataChannelPtr)
203
183
  {
204
184
  return Napi::Boolean::New(info.Env(), false);
@@ -210,7 +190,7 @@ Napi::Value DataChannelWrapper::isOpen(const Napi::CallbackInfo &info)
210
190
  }
211
191
  catch (std::exception &ex)
212
192
  {
213
- Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
193
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
214
194
  return Napi::Boolean::New(info.Env(), false);
215
195
  }
216
196
  }
@@ -218,6 +198,7 @@ Napi::Value DataChannelWrapper::isOpen(const Napi::CallbackInfo &info)
218
198
  Napi::Value DataChannelWrapper::bufferedAmount(const Napi::CallbackInfo &info)
219
199
  {
220
200
  Napi::Env env = info.Env();
201
+
221
202
  if (!mDataChannelPtr)
222
203
  {
223
204
  return Napi::Number::New(info.Env(), 0);
@@ -229,7 +210,7 @@ Napi::Value DataChannelWrapper::bufferedAmount(const Napi::CallbackInfo &info)
229
210
  }
230
211
  catch (std::exception &ex)
231
212
  {
232
- Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
213
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
233
214
  return Napi::Number::New(info.Env(), 0);
234
215
  }
235
216
  }
@@ -237,6 +218,7 @@ Napi::Value DataChannelWrapper::bufferedAmount(const Napi::CallbackInfo &info)
237
218
  Napi::Value DataChannelWrapper::maxMessageSize(const Napi::CallbackInfo &info)
238
219
  {
239
220
  Napi::Env env = info.Env();
221
+
240
222
  if (!mDataChannelPtr)
241
223
  {
242
224
  return Napi::Number::New(info.Env(), 0);
@@ -248,7 +230,7 @@ Napi::Value DataChannelWrapper::maxMessageSize(const Napi::CallbackInfo &info)
248
230
  }
249
231
  catch (std::exception &ex)
250
232
  {
251
- Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
233
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
252
234
  return Napi::Number::New(info.Env(), 0);
253
235
  }
254
236
  }
@@ -257,7 +239,7 @@ void DataChannelWrapper::setBufferedAmountLowThreshold(const Napi::CallbackInfo
257
239
  {
258
240
  if (!mDataChannelPtr)
259
241
  {
260
- Napi::Error::New(info.Env(), "It seems data-channel is destroyed!").ThrowAsJavaScriptException();
242
+ Napi::Error::New(info.Env(), "setBufferedAmountLowThreshold() called on destroyed channel").ThrowAsJavaScriptException();
261
243
  return;
262
244
  }
263
245
 
@@ -276,13 +258,19 @@ void DataChannelWrapper::setBufferedAmountLowThreshold(const Napi::CallbackInfo
276
258
  }
277
259
  catch (std::exception &ex)
278
260
  {
279
- Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
261
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
280
262
  return;
281
263
  }
282
264
  }
283
265
 
284
266
  void DataChannelWrapper::onOpen(const Napi::CallbackInfo &info)
285
267
  {
268
+ if (!mDataChannelPtr)
269
+ {
270
+ Napi::Error::New(info.Env(), "onOpen() called on destroyed channel").ThrowAsJavaScriptException();
271
+ return;
272
+ }
273
+
286
274
  Napi::Env env = info.Env();
287
275
  int length = info.Length();
288
276
 
@@ -295,11 +283,10 @@ void DataChannelWrapper::onOpen(const Napi::CallbackInfo &info)
295
283
  // Callback
296
284
  mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
297
285
 
298
- mDataChannelPtr->onOpen([&]()
299
- {
286
+ mDataChannelPtr->onOpen([&]() {
300
287
  if (mOnOpenCallback)
301
288
  mOnOpenCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
302
- // Check the peer connection is not closed
289
+ // Check the data channel is not closed
303
290
  if(instances.find(this) == instances.end())
304
291
  throw ThreadSafeCallback::CancelException();
305
292
 
@@ -311,6 +298,12 @@ void DataChannelWrapper::onOpen(const Napi::CallbackInfo &info)
311
298
 
312
299
  void DataChannelWrapper::onClosed(const Napi::CallbackInfo &info)
313
300
  {
301
+ if (!mDataChannelPtr)
302
+ {
303
+ Napi::Error::New(info.Env(), "onClosed() called on destroyed channel").ThrowAsJavaScriptException();
304
+ return;
305
+ }
306
+
314
307
  Napi::Env env = info.Env();
315
308
  int length = info.Length();
316
309
 
@@ -323,11 +316,25 @@ void DataChannelWrapper::onClosed(const Napi::CallbackInfo &info)
323
316
  // Callback
324
317
  mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
325
318
 
326
- // Object cb call moved to the constructor
319
+ mDataChannelPtr->onClosed([&]() {
320
+ if (mOnClosedCallback)
321
+ mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
322
+ // Do not check if the data channel has been closed here
323
+
324
+ // This will run in main thread and needs to construct the
325
+ // arguments for the call
326
+ args = {};
327
+ }); });
327
328
  }
328
329
 
329
330
  void DataChannelWrapper::onError(const Napi::CallbackInfo &info)
330
331
  {
332
+ if (!mDataChannelPtr)
333
+ {
334
+ Napi::Error::New(info.Env(), "onError() called on destroyed channel").ThrowAsJavaScriptException();
335
+ return;
336
+ }
337
+
331
338
  Napi::Env env = info.Env();
332
339
  int length = info.Length();
333
340
 
@@ -340,11 +347,10 @@ void DataChannelWrapper::onError(const Napi::CallbackInfo &info)
340
347
  // Callback
341
348
  mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
342
349
 
343
- mDataChannelPtr->onError([&](const std::string &error)
344
- {
350
+ mDataChannelPtr->onError([&](std::string error) {
345
351
  if (mOnErrorCallback)
346
- mOnErrorCallback->call([this, error](Napi::Env env, std::vector<napi_value> &args) {
347
- // Check the peer connection is not closed
352
+ mOnErrorCallback->call([this, error = std::move(error)](Napi::Env env, std::vector<napi_value> &args) {
353
+ // Check the data channel is not closed
348
354
  if(instances.find(this) == instances.end())
349
355
  throw ThreadSafeCallback::CancelException();
350
356
 
@@ -356,6 +362,12 @@ void DataChannelWrapper::onError(const Napi::CallbackInfo &info)
356
362
 
357
363
  void DataChannelWrapper::onBufferedAmountLow(const Napi::CallbackInfo &info)
358
364
  {
365
+ if (!mDataChannelPtr)
366
+ {
367
+ Napi::Error::New(info.Env(), "onBufferedAmountLow() called on destroyed channel").ThrowAsJavaScriptException();
368
+ return;
369
+ }
370
+
359
371
  Napi::Env env = info.Env();
360
372
  int length = info.Length();
361
373
 
@@ -368,11 +380,10 @@ void DataChannelWrapper::onBufferedAmountLow(const Napi::CallbackInfo &info)
368
380
  // Callback
369
381
  mOnBufferedAmountLowCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
370
382
 
371
- mDataChannelPtr->onBufferedAmountLow([&]()
372
- {
383
+ mDataChannelPtr->onBufferedAmountLow([&]() {
373
384
  if (mOnBufferedAmountLowCallback)
374
385
  mOnBufferedAmountLowCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
375
- // Check the peer connection is not closed
386
+ // Check the data channel is not closed
376
387
  if(instances.find(this) == instances.end())
377
388
  throw ThreadSafeCallback::CancelException();
378
389
 
@@ -384,6 +395,12 @@ void DataChannelWrapper::onBufferedAmountLow(const Napi::CallbackInfo &info)
384
395
 
385
396
  void DataChannelWrapper::onMessage(const Napi::CallbackInfo &info)
386
397
  {
398
+ if (!mDataChannelPtr)
399
+ {
400
+ Napi::Error::New(info.Env(), "onMessage() called on destroyed channel").ThrowAsJavaScriptException();
401
+ return;
402
+ }
403
+
387
404
  Napi::Env env = info.Env();
388
405
  int length = info.Length();
389
406
 
@@ -396,24 +413,23 @@ void DataChannelWrapper::onMessage(const Napi::CallbackInfo &info)
396
413
  // Callback
397
414
  mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
398
415
 
399
- mDataChannelPtr->onMessage([&](const std::variant<rtc::binary, std::string> &message)
400
- {
416
+ mDataChannelPtr->onMessage([&](std::variant<rtc::binary, std::string> message) {
401
417
  if (mOnMessageCallback)
402
- mOnMessageCallback->call([this, message](Napi::Env env, std::vector<napi_value> &args) {
403
- // Check the peer connection is not closed
418
+ mOnMessageCallback->call([this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args) {
419
+ // Check the data channel is not closed
404
420
  if(instances.find(this) == instances.end())
405
421
  throw ThreadSafeCallback::CancelException();
406
422
 
407
423
  // This will run in main thread and needs to construct the
408
424
  // arguments for the call
409
- Napi::Object payload = Napi::Object::New(env);
410
425
  if (std::holds_alternative<std::string>(message))
411
426
  {
412
427
  args = {Napi::String::New(env, std::get<std::string>(message))};
413
428
  }
414
429
  else
415
430
  {
416
- args = {Napi::Buffer<std::byte>::Copy(env, std::get<rtc::binary>(message).data(), std::get<rtc::binary>(message).size())};
431
+ auto bin = std::get<rtc::binary>(std::move(message));
432
+ args = {Napi::Buffer<std::byte>::Copy(env, bin.data(), bin.size())};
417
433
  }
418
434
  }); });
419
435
  }
@@ -46,7 +46,6 @@ private:
46
46
  static std::unordered_set<DataChannelWrapper*> instances;
47
47
 
48
48
  void doClose();
49
- void cleanCbsAndEraseInstance();
50
49
 
51
50
  std::string mLabel;
52
51
  std::shared_ptr<rtc::DataChannel> mDataChannelPtr = nullptr;
@@ -46,26 +46,8 @@ Napi::Object TrackWrapper::Init(Napi::Env env, Napi::Object exports)
46
46
 
47
47
  TrackWrapper::TrackWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<TrackWrapper>(info)
48
48
  {
49
- mTrackPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::Track>>>().Data());
49
+ mTrackPtr = *(info[0].As<Napi::External<std::shared_ptr<rtc::Track>>>().Data());
50
50
 
51
- mTrackPtr->onClosed([&]()
52
- {
53
- if (mOnClosedCallback)
54
- mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
55
- // Check the peer connection is not closed
56
- if(instances.find(this) == instances.end())
57
- throw ThreadSafeCallback::CancelException();
58
-
59
- cleanCbsAndEraseInstance();
60
-
61
- // This will run in main thread and needs to construct the
62
- // arguments for the call
63
- args = {};
64
- });
65
- else {
66
- cleanCbsAndEraseInstance();
67
- } });
68
-
69
51
  instances.insert(this);
70
52
  }
71
53
 
@@ -81,24 +63,21 @@ void TrackWrapper::doClose()
81
63
  try
82
64
  {
83
65
  mTrackPtr->close();
66
+ mTrackPtr.reset();
84
67
  }
85
68
  catch (std::exception &ex)
86
69
  {
87
- std::cout << std::string("libdatachannel error while closing track# ") + ex.what() << std::endl;
70
+ std::cerr << std::string("libdatachannel error while closing track: ") + ex.what() << std::endl;
88
71
  return;
89
72
  }
90
73
  }
91
- }
92
74
 
93
- void TrackWrapper::cleanCbsAndEraseInstance()
94
- {
95
75
  mOnOpenCallback.reset();
96
76
  mOnClosedCallback.reset();
97
77
  mOnErrorCallback.reset();
98
78
  mOnMessageCallback.reset();
99
79
 
100
80
  instances.erase(this);
101
- mTrackPtr.reset();
102
81
  }
103
82
 
104
83
  void TrackWrapper::close(const Napi::CallbackInfo &info)
@@ -108,18 +87,36 @@ void TrackWrapper::close(const Napi::CallbackInfo &info)
108
87
 
109
88
  Napi::Value TrackWrapper::direction(const Napi::CallbackInfo &info)
110
89
  {
90
+ if (!mTrackPtr)
91
+ {
92
+ Napi::Error::New(info.Env(), "direction() called on destroyed track").ThrowAsJavaScriptException();
93
+ return info.Env().Null();
94
+ }
95
+
111
96
  Napi::Env env = info.Env();
112
97
  return Napi::String::New(env, directionToStr(mTrackPtr->direction()));
113
98
  }
114
99
 
115
100
  Napi::Value TrackWrapper::mid(const Napi::CallbackInfo &info)
116
101
  {
102
+ if (!mTrackPtr)
103
+ {
104
+ Napi::Error::New(info.Env(), "mid() called on destroyed track").ThrowAsJavaScriptException();
105
+ return info.Env().Null();
106
+ }
107
+
117
108
  Napi::Env env = info.Env();
118
109
  return Napi::String::New(env, mTrackPtr->mid());
119
110
  }
120
111
 
121
112
  Napi::Value TrackWrapper::type(const Napi::CallbackInfo &info)
122
113
  {
114
+ if (!mTrackPtr)
115
+ {
116
+ Napi::Error::New(info.Env(), "type() called on destroyed track").ThrowAsJavaScriptException();
117
+ return info.Env().Null();
118
+ }
119
+
123
120
  Napi::Env env = info.Env();
124
121
  return Napi::String::New(env, mTrackPtr->description().type());
125
122
  }
@@ -128,7 +125,7 @@ Napi::Value TrackWrapper::sendMessage(const Napi::CallbackInfo &info)
128
125
  {
129
126
  if (!mTrackPtr)
130
127
  {
131
- Napi::Error::New(info.Env(), "It seems track is destroyed!").ThrowAsJavaScriptException();
128
+ Napi::Error::New(info.Env(), "sendMessage() called on destroyed track").ThrowAsJavaScriptException();
132
129
  return info.Env().Null();
133
130
  }
134
131
 
@@ -148,7 +145,7 @@ Napi::Value TrackWrapper::sendMessage(const Napi::CallbackInfo &info)
148
145
  }
149
146
  catch (std::exception &ex)
150
147
  {
151
- Napi::Error::New(env, std::string("libdatachannel error while sending track msg# ") + ex.what()).ThrowAsJavaScriptException();
148
+ Napi::Error::New(env, std::string("libdatachannel error while sending track message: ") + ex.what()).ThrowAsJavaScriptException();
152
149
  return Napi::Boolean::New(info.Env(), false);
153
150
  }
154
151
  }
@@ -157,7 +154,7 @@ Napi::Value TrackWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
157
154
  {
158
155
  if (!mTrackPtr)
159
156
  {
160
- Napi::Error::New(info.Env(), "It seems track is destroyed!").ThrowAsJavaScriptException();
157
+ Napi::Error::New(info.Env(), "sendMessageBinary() called on destroyed track").ThrowAsJavaScriptException();
161
158
  return info.Env().Null();
162
159
  }
163
160
 
@@ -177,7 +174,7 @@ Napi::Value TrackWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
177
174
  }
178
175
  catch (std::exception &ex)
179
176
  {
180
- Napi::Error::New(env, std::string("libdatachannel error while sending track msg# ") + ex.what()).ThrowAsJavaScriptException();
177
+ Napi::Error::New(env, std::string("libdatachannel error while sending track message: ") + ex.what()).ThrowAsJavaScriptException();
181
178
  return Napi::Boolean::New(info.Env(), false);
182
179
  }
183
180
  }
@@ -185,18 +182,31 @@ Napi::Value TrackWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
185
182
  Napi::Value TrackWrapper::isOpen(const Napi::CallbackInfo &info)
186
183
  {
187
184
  Napi::Env env = info.Env();
185
+
186
+ if (!mTrackPtr)
187
+ {
188
+ return Napi::Boolean::New(env, false);
189
+ }
190
+
188
191
  return Napi::Boolean::New(env, mTrackPtr->isOpen());
189
192
  }
190
193
 
191
194
  Napi::Value TrackWrapper::isClosed(const Napi::CallbackInfo &info)
192
195
  {
193
196
  Napi::Env env = info.Env();
197
+
198
+ if (!mTrackPtr)
199
+ {
200
+ return Napi::Boolean::New(env, true);
201
+ }
202
+
194
203
  return Napi::Boolean::New(env, mTrackPtr->isClosed());
195
204
  }
196
205
 
197
206
  Napi::Value TrackWrapper::maxMessageSize(const Napi::CallbackInfo &info)
198
207
  {
199
208
  Napi::Env env = info.Env();
209
+
200
210
  if (!mTrackPtr)
201
211
  {
202
212
  return Napi::Number::New(info.Env(), 0);
@@ -208,13 +218,19 @@ Napi::Value TrackWrapper::maxMessageSize(const Napi::CallbackInfo &info)
208
218
  }
209
219
  catch (std::exception &ex)
210
220
  {
211
- Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
221
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
212
222
  return Napi::Number::New(info.Env(), 0);
213
223
  }
214
224
  }
215
225
 
216
226
  Napi::Value TrackWrapper::requestKeyframe(const Napi::CallbackInfo &info)
217
227
  {
228
+ if (!mTrackPtr)
229
+ {
230
+ Napi::Error::New(info.Env(), "requestKeyframe() called on destroyed track").ThrowAsJavaScriptException();
231
+ return info.Env().Null();
232
+ }
233
+
218
234
  Napi::Env env = info.Env();
219
235
  return Napi::Boolean::New(env, mTrackPtr->requestKeyframe());
220
236
  }
@@ -223,7 +239,7 @@ void TrackWrapper::setMediaHandler(const Napi::CallbackInfo &info)
223
239
  {
224
240
  if (!mTrackPtr)
225
241
  {
226
- Napi::Error::New(info.Env(), "It seems track is destroyed!").ThrowAsJavaScriptException();
242
+ Napi::Error::New(info.Env(), "setMediaHandler() called on destroyed track").ThrowAsJavaScriptException();
227
243
  return;
228
244
  }
229
245
 
@@ -232,7 +248,7 @@ void TrackWrapper::setMediaHandler(const Napi::CallbackInfo &info)
232
248
 
233
249
  if (length < 1 || !info[0].IsObject())
234
250
  {
235
- Napi::TypeError::New(env, "Mediahandler class instance expected").ThrowAsJavaScriptException();
251
+ Napi::TypeError::New(env, "MediaHandler class instance expected").ThrowAsJavaScriptException();
236
252
  return;
237
253
  }
238
254
 
@@ -242,6 +258,12 @@ void TrackWrapper::setMediaHandler(const Napi::CallbackInfo &info)
242
258
 
243
259
  void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
244
260
  {
261
+ if (!mTrackPtr)
262
+ {
263
+ Napi::Error::New(info.Env(), "onOpen() called on destroyed track").ThrowAsJavaScriptException();
264
+ return;
265
+ }
266
+
245
267
  Napi::Env env = info.Env();
246
268
  int length = info.Length();
247
269
 
@@ -254,11 +276,10 @@ void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
254
276
  // Callback
255
277
  mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
256
278
 
257
- mTrackPtr->onOpen([&]()
258
- {
279
+ mTrackPtr->onOpen([&]() {
259
280
  if (mOnOpenCallback)
260
281
  mOnOpenCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
261
- // Check the peer connection is not closed
282
+ // Check the track is not closed
262
283
  if(instances.find(this) == instances.end())
263
284
  throw ThreadSafeCallback::CancelException();
264
285
 
@@ -270,6 +291,12 @@ void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
270
291
 
271
292
  void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
272
293
  {
294
+ if (!mTrackPtr)
295
+ {
296
+ Napi::Error::New(info.Env(), "onClosed() called on destroyed track").ThrowAsJavaScriptException();
297
+ return;
298
+ }
299
+
273
300
  Napi::Env env = info.Env();
274
301
  int length = info.Length();
275
302
 
@@ -282,11 +309,25 @@ void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
282
309
  // Callback
283
310
  mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
284
311
 
285
- // Object cb call moved to the constructor
312
+ mTrackPtr->onClosed([&]() {
313
+ if (mOnClosedCallback)
314
+ mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
315
+ // Do not check if the data channel has been closed here
316
+
317
+ // This will run in main thread and needs to construct the
318
+ // arguments for the call
319
+ args = {};
320
+ }); });
286
321
  }
287
322
 
288
323
  void TrackWrapper::onError(const Napi::CallbackInfo &info)
289
324
  {
325
+ if (!mTrackPtr)
326
+ {
327
+ Napi::Error::New(info.Env(), "onError() called on destroyed track").ThrowAsJavaScriptException();
328
+ return;
329
+ }
330
+
290
331
  Napi::Env env = info.Env();
291
332
  int length = info.Length();
292
333
 
@@ -299,11 +340,10 @@ void TrackWrapper::onError(const Napi::CallbackInfo &info)
299
340
  // Callback
300
341
  mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
301
342
 
302
- mTrackPtr->onError([&](const std::string &error)
303
- {
343
+ mTrackPtr->onError([&](const std::string &error) {
304
344
  if (mOnErrorCallback)
305
345
  mOnErrorCallback->call([this, error](Napi::Env env, std::vector<napi_value> &args) {
306
- // Check the peer connection is not closed
346
+ // Check the track is not closed
307
347
  if(instances.find(this) == instances.end())
308
348
  throw ThreadSafeCallback::CancelException();
309
349
 
@@ -315,6 +355,12 @@ void TrackWrapper::onError(const Napi::CallbackInfo &info)
315
355
 
316
356
  void TrackWrapper::onMessage(const Napi::CallbackInfo &info)
317
357
  {
358
+ if (!mTrackPtr)
359
+ {
360
+ Napi::Error::New(info.Env(), "onMessage() called on destroyed track").ThrowAsJavaScriptException();
361
+ return;
362
+ }
363
+
318
364
  Napi::Env env = info.Env();
319
365
  int length = info.Length();
320
366
 
@@ -327,26 +373,19 @@ void TrackWrapper::onMessage(const Napi::CallbackInfo &info)
327
373
  // Callback
328
374
  mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
329
375
 
330
- mTrackPtr->onMessage([&](const std::variant<rtc::binary, std::string> &message)
331
- {
376
+ mTrackPtr->onMessage([&](std::variant<rtc::binary, std::string> message) {
332
377
  if (mOnMessageCallback)
333
- mOnMessageCallback->call([this, message](Napi::Env env, std::vector<napi_value> &args) {
334
- // Check the peer connection is not closed
378
+ mOnMessageCallback->call([this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args) {
379
+ // Check the track is not closed
335
380
  if(instances.find(this) == instances.end())
336
381
  throw ThreadSafeCallback::CancelException();
337
382
 
338
383
  // This will run in main thread and needs to construct the
339
384
  // arguments for the call
340
- Napi::Object payload = Napi::Object::New(env);
341
- if (std::holds_alternative<std::string>(message))
342
- {
343
- // Track will always send message as binary
344
- // So this code is not needed actually
345
- args = {Napi::String::New(env, std::get<std::string>(message))};
346
- }
347
- else
385
+ if (std::holds_alternative<rtc::binary>(message)) // Track will always receive messages as binary
348
386
  {
349
- args = {Napi::Buffer<std::byte>::Copy(env, std::get<rtc::binary>(message).data(), std::get<rtc::binary>(message).size())};
387
+ auto bin = std::get<rtc::binary>(std::move(message));
388
+ args = {Napi::Buffer<std::byte>::Copy(env, bin.data(), bin.size())};
350
389
  }
351
390
  }); });
352
- }
391
+ }
@@ -46,7 +46,6 @@ private:
46
46
  static std::unordered_set<TrackWrapper *> instances;
47
47
 
48
48
  void doClose();
49
- void cleanCbsAndEraseInstance();
50
49
 
51
50
  std::shared_ptr<rtc::Track> mTrackPtr = nullptr;
52
51