node-datachannel 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,20 @@
1
1
  #include "peer-connection-wrapper.h"
2
2
  #include "data-channel-wrapper.h"
3
+ #include "media-track-wrapper.h"
4
+ #include "media-video-wrapper.h"
5
+ #include "media-audio-wrapper.h"
6
+
3
7
  #include <sstream>
4
8
 
5
9
  Napi::FunctionReference PeerConnectionWrapper::constructor;
10
+ std::unordered_set<PeerConnectionWrapper *> PeerConnectionWrapper::instances;
11
+
12
+ void PeerConnectionWrapper::CloseAll()
13
+ {
14
+ auto copy(instances);
15
+ for (auto inst : copy)
16
+ inst->doClose();
17
+ }
6
18
 
7
19
  Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
8
20
  {
@@ -11,19 +23,26 @@ Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
11
23
  Napi::Function func = DefineClass(
12
24
  env,
13
25
  "PeerConnection",
14
- {InstanceMethod("close", &PeerConnectionWrapper::close),
15
- InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription),
16
- InstanceMethod("addRemoteCandidate", &PeerConnectionWrapper::addRemoteCandidate),
17
- InstanceMethod("createDataChannel", &PeerConnectionWrapper::createDataChannel),
18
- InstanceMethod("onLocalDescription", &PeerConnectionWrapper::onLocalDescription),
19
- InstanceMethod("onLocalCandidate", &PeerConnectionWrapper::onLocalCandidate),
20
- InstanceMethod("onStateChange", &PeerConnectionWrapper::onStateChange),
21
- InstanceMethod("onGatheringStateChange", &PeerConnectionWrapper::onGatheringStateChange),
22
- InstanceMethod("onDataChannel", &PeerConnectionWrapper::onDataChannel),
23
- InstanceMethod("bytesSent", &PeerConnectionWrapper::bytesSent),
24
- InstanceMethod("bytesReceived", &PeerConnectionWrapper::bytesReceived),
25
- InstanceMethod("rtt", &PeerConnectionWrapper::rtt),
26
- InstanceMethod("getSelectedCandidatePair", &PeerConnectionWrapper::getSelectedCandidatePair)});
26
+ {
27
+ InstanceMethod("close", &PeerConnectionWrapper::close),
28
+ InstanceMethod("setLocalDescription", &PeerConnectionWrapper::setLocalDescription),
29
+ InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription),
30
+ InstanceMethod("localDescription", &PeerConnectionWrapper::localDescription),
31
+ InstanceMethod("addRemoteCandidate", &PeerConnectionWrapper::addRemoteCandidate),
32
+ InstanceMethod("createDataChannel", &PeerConnectionWrapper::createDataChannel),
33
+ InstanceMethod("addTrack", &PeerConnectionWrapper::addTrack),
34
+ InstanceMethod("hasMedia", &PeerConnectionWrapper::hasMedia),
35
+ InstanceMethod("onLocalDescription", &PeerConnectionWrapper::onLocalDescription),
36
+ InstanceMethod("onLocalCandidate", &PeerConnectionWrapper::onLocalCandidate),
37
+ InstanceMethod("onStateChange", &PeerConnectionWrapper::onStateChange),
38
+ InstanceMethod("onGatheringStateChange", &PeerConnectionWrapper::onGatheringStateChange),
39
+ InstanceMethod("onDataChannel", &PeerConnectionWrapper::onDataChannel),
40
+ InstanceMethod("onTrack", &PeerConnectionWrapper::onTrack),
41
+ InstanceMethod("bytesSent", &PeerConnectionWrapper::bytesSent),
42
+ InstanceMethod("bytesReceived", &PeerConnectionWrapper::bytesReceived),
43
+ InstanceMethod("rtt", &PeerConnectionWrapper::rtt),
44
+ InstanceMethod("getSelectedCandidatePair", &PeerConnectionWrapper::getSelectedCandidatePair),
45
+ });
27
46
 
28
47
  constructor = Napi::Persistent(func);
29
48
  constructor.SuppressDestruct();
@@ -166,9 +185,9 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
166
185
  return;
167
186
  }
168
187
  std::string strPolicy = config.Get("iceTransportPolicy").As<Napi::String>().ToString();
169
- if(strPolicy == "all")
188
+ if (strPolicy == "all")
170
189
  rtcConfig.iceTransportPolicy = rtc::TransportPolicy::All;
171
- else if(strPolicy == "relay")
190
+ else if (strPolicy == "relay")
172
191
  rtcConfig.iceTransportPolicy = rtc::TransportPolicy::Relay;
173
192
  else
174
193
  {
@@ -180,21 +199,37 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
180
199
  // Create peer-connection
181
200
  try
182
201
  {
183
- mRtcPeerConnPtr = std::make_shared<rtc::PeerConnection>(rtcConfig);
202
+ mRtcPeerConnPtr = std::make_unique<rtc::PeerConnection>(rtcConfig);
184
203
  }
185
204
  catch (std::exception &ex)
186
205
  {
187
206
  Napi::Error::New(env, std::string("libdatachannel error while creating peerConnection# ") + ex.what()).ThrowAsJavaScriptException();
188
207
  return;
189
208
  }
209
+
210
+ instances.insert(this);
190
211
  }
191
212
 
192
213
  PeerConnectionWrapper::~PeerConnectionWrapper()
193
214
  {
215
+ doClose();
216
+ }
217
+
218
+ void PeerConnectionWrapper::doClose()
219
+ {
220
+ instances.erase(this);
221
+
194
222
  if (mRtcPeerConnPtr)
195
223
  {
196
224
  try
197
225
  {
226
+ mOnLocalDescriptionCallback.reset();
227
+ mOnLocalCandidateCallback.reset();
228
+ mOnStateChangeCallback.reset();
229
+ mOnGatheringStateChangeCallback.reset();
230
+ mOnDataChannelCallback.reset();
231
+ mOnTrackCallback.reset();
232
+
198
233
  mRtcPeerConnPtr->close();
199
234
  mRtcPeerConnPtr.reset();
200
235
  }
@@ -207,6 +242,11 @@ PeerConnectionWrapper::~PeerConnectionWrapper()
207
242
  }
208
243
 
209
244
  void PeerConnectionWrapper::close(const Napi::CallbackInfo &info)
245
+ {
246
+ doClose();
247
+ }
248
+
249
+ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
210
250
  {
211
251
  Napi::Env env = info.Env();
212
252
  if (!mRtcPeerConnPtr)
@@ -215,16 +255,31 @@ void PeerConnectionWrapper::close(const Napi::CallbackInfo &info)
215
255
  return;
216
256
  }
217
257
 
218
- try
219
- {
220
- mRtcPeerConnPtr->close();
221
- mRtcPeerConnPtr.reset();
222
- }
223
- catch (std::exception &ex)
258
+ int length = info.Length();
259
+
260
+ rtc::Description::Type type = rtc::Description::Type::Unspec;
261
+
262
+ // optional
263
+ if (length > 0)
224
264
  {
225
- Napi::Error::New(env, std::string("libdatachannel error while closing peerConnection# ") + ex.what()).ThrowAsJavaScriptException();
226
- return;
265
+ if (!info[0].IsString())
266
+ {
267
+ Napi::TypeError::New(env, "type (String) expected").ThrowAsJavaScriptException();
268
+ return;
269
+ }
270
+ std::string typeStr = info[0].As<Napi::String>().ToString();
271
+
272
+ if (typeStr == "Answer")
273
+ type = rtc::Description::Type::Answer;
274
+ if (typeStr == "Offer")
275
+ type = rtc::Description::Type::Offer;
276
+ if (typeStr == "Pranswer")
277
+ type = rtc::Description::Type::Pranswer;
278
+ if (typeStr == "Rollback")
279
+ type = rtc::Description::Type::Rollback;
227
280
  }
281
+
282
+ mRtcPeerConnPtr->setLocalDescription(type);
228
283
  }
229
284
 
230
285
  void PeerConnectionWrapper::setRemoteDescription(const Napi::CallbackInfo &info)
@@ -258,6 +313,17 @@ void PeerConnectionWrapper::setRemoteDescription(const Napi::CallbackInfo &info)
258
313
  }
259
314
  }
260
315
 
316
+ Napi::Value PeerConnectionWrapper::localDescription(const Napi::CallbackInfo &info)
317
+ {
318
+ Napi::Env env = info.Env();
319
+ std::optional<rtc::Description> desc = mRtcPeerConnPtr->localDescription();
320
+
321
+ Napi::Object obj = Napi::Object::New(env);
322
+ obj.Set("type", desc->typeString());
323
+ obj.Set("sdp", desc.value());
324
+ return obj;
325
+ }
326
+
261
327
  void PeerConnectionWrapper::addRemoteCandidate(const Napi::CallbackInfo &info)
262
328
  {
263
329
  Napi::Env env = info.Env();
@@ -420,7 +486,7 @@ Napi::Value PeerConnectionWrapper::createDataChannel(const Napi::CallbackInfo &i
420
486
  init.reliability.unordered = !initConfig.Get("ordered").As<Napi::Boolean>();
421
487
  }
422
488
 
423
- if(initConfig.Get("maxPacketLifeTime").IsNumber() && initConfig.Get("maxRetransmits").IsNumber())
489
+ if (initConfig.Get("maxPacketLifeTime").IsNumber() && initConfig.Get("maxRetransmits").IsNumber())
424
490
  {
425
491
  Napi::TypeError::New(env, "Wrong DataChannel Init Config, maxPacketLifeTime and maxRetransmits are exclusive").ThrowAsJavaScriptException();
426
492
  return info.Env().Null();
@@ -468,18 +534,22 @@ void PeerConnectionWrapper::onLocalDescription(const Napi::CallbackInfo &info)
468
534
  }
469
535
 
470
536
  // Callback
471
- mOnLocalDescriptionCallback = std::make_shared<ThreadSafeCallback>(info[0].As<Napi::Function>());
537
+ mOnLocalDescriptionCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
472
538
 
473
- mRtcPeerConnPtr->onLocalDescription([&](const rtc::Description &sdp) {
539
+ mRtcPeerConnPtr->onLocalDescription([&](const rtc::Description &sdp)
540
+ {
474
541
  if (mOnLocalDescriptionCallback)
475
- mOnLocalDescriptionCallback->call([sdp](Napi::Env env, std::vector<napi_value> &args) {
542
+ mOnLocalDescriptionCallback->call([this, sdp](Napi::Env env, std::vector<napi_value> &args) {
543
+ // Check the peer connection is not closed
544
+ if(instances.find(this) == instances.end())
545
+ throw ThreadSafeCallback::CancelException();
546
+
476
547
  // This will run in main thread and needs to construct the
477
548
  // arguments for the call
478
549
  args = {
479
550
  Napi::String::New(env, std::string(sdp)),
480
551
  Napi::String::New(env, sdp.typeString())};
481
- });
482
- });
552
+ }); });
483
553
  }
484
554
 
485
555
  void PeerConnectionWrapper::onLocalCandidate(const Napi::CallbackInfo &info)
@@ -494,18 +564,22 @@ void PeerConnectionWrapper::onLocalCandidate(const Napi::CallbackInfo &info)
494
564
  }
495
565
 
496
566
  // Callback
497
- mOnLocalCandidateCallback = std::make_shared<ThreadSafeCallback>(info[0].As<Napi::Function>());
567
+ mOnLocalCandidateCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
498
568
 
499
- mRtcPeerConnPtr->onLocalCandidate([&](const rtc::Candidate &candidate) {
569
+ mRtcPeerConnPtr->onLocalCandidate([&](const rtc::Candidate &candidate)
570
+ {
500
571
  if (mOnLocalCandidateCallback)
501
- mOnLocalCandidateCallback->call([candidate](Napi::Env env, std::vector<napi_value> &args) {
572
+ mOnLocalCandidateCallback->call([this, candidate](Napi::Env env, std::vector<napi_value> &args) {
573
+ // Check the peer connection is not closed
574
+ if(instances.find(this) == instances.end())
575
+ throw ThreadSafeCallback::CancelException();
576
+
502
577
  // This will run in main thread and needs to construct the
503
578
  // arguments for the call
504
579
  args = {
505
580
  Napi::String::New(env, std::string(candidate)),
506
581
  Napi::String::New(env, candidate.mid())};
507
- });
508
- });
582
+ }); });
509
583
  }
510
584
 
511
585
  void PeerConnectionWrapper::onStateChange(const Napi::CallbackInfo &info)
@@ -520,18 +594,22 @@ void PeerConnectionWrapper::onStateChange(const Napi::CallbackInfo &info)
520
594
  }
521
595
 
522
596
  // Callback
523
- mOnStateChangeCallback = std::make_shared<ThreadSafeCallback>(info[0].As<Napi::Function>());
597
+ mOnStateChangeCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
524
598
 
525
- mRtcPeerConnPtr->onStateChange([&](rtc::PeerConnection::State state) {
599
+ mRtcPeerConnPtr->onStateChange([&](rtc::PeerConnection::State state)
600
+ {
526
601
  if (mOnStateChangeCallback)
527
- mOnStateChangeCallback->call([state](Napi::Env env, std::vector<napi_value> &args) {
602
+ mOnStateChangeCallback->call([this, state](Napi::Env env, std::vector<napi_value> &args) {
603
+ // Check the peer connection is not closed
604
+ if(instances.find(this) == instances.end())
605
+ throw ThreadSafeCallback::CancelException();
606
+
528
607
  // This will run in main thread and needs to construct the
529
608
  // arguments for the call
530
609
  std::ostringstream stream;
531
610
  stream << state;
532
611
  args = {Napi::String::New(env, stream.str())};
533
- });
534
- });
612
+ }); });
535
613
  }
536
614
 
537
615
  void PeerConnectionWrapper::onGatheringStateChange(const Napi::CallbackInfo &info)
@@ -546,18 +624,22 @@ void PeerConnectionWrapper::onGatheringStateChange(const Napi::CallbackInfo &inf
546
624
  }
547
625
 
548
626
  // Callback
549
- mOnGatheringStateChangeCallback = std::make_shared<ThreadSafeCallback>(info[0].As<Napi::Function>());
627
+ mOnGatheringStateChangeCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
550
628
 
551
- mRtcPeerConnPtr->onGatheringStateChange([&](rtc::PeerConnection::GatheringState state) {
629
+ mRtcPeerConnPtr->onGatheringStateChange([&](rtc::PeerConnection::GatheringState state)
630
+ {
552
631
  if (mOnGatheringStateChangeCallback)
553
- mOnGatheringStateChangeCallback->call([state](Napi::Env env, std::vector<napi_value> &args) {
632
+ mOnGatheringStateChangeCallback->call([this, state](Napi::Env env, std::vector<napi_value> &args) {
633
+ // Check the peer connection is not closed
634
+ if(instances.find(this) == instances.end())
635
+ throw ThreadSafeCallback::CancelException();
636
+
554
637
  // This will run in main thread and needs to construct the
555
638
  // arguments for the call
556
639
  std::ostringstream stream;
557
640
  stream << state;
558
641
  args = {Napi::String::New(env, stream.str())};
559
- });
560
- });
642
+ }); });
561
643
  }
562
644
 
563
645
  void PeerConnectionWrapper::onDataChannel(const Napi::CallbackInfo &info)
@@ -572,18 +654,22 @@ void PeerConnectionWrapper::onDataChannel(const Napi::CallbackInfo &info)
572
654
  }
573
655
 
574
656
  // Callback
575
- mOnDataChannelCallback = std::make_shared<ThreadSafeCallback>(info[0].As<Napi::Function>());
657
+ mOnDataChannelCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
576
658
 
577
- mRtcPeerConnPtr->onDataChannel([&](std::shared_ptr<rtc::DataChannel> dc) {
659
+ mRtcPeerConnPtr->onDataChannel([&](std::shared_ptr<rtc::DataChannel> dc)
660
+ {
578
661
  if (mOnDataChannelCallback)
579
- mOnDataChannelCallback->call([dc](Napi::Env env, std::vector<napi_value> &args) {
662
+ mOnDataChannelCallback->call([this, dc](Napi::Env env, std::vector<napi_value> &args) {
663
+ // Check the peer connection is not closed
664
+ if(instances.find(this) == instances.end())
665
+ throw ThreadSafeCallback::CancelException();
666
+
580
667
  // This will run in main thread and needs to construct the
581
668
  // arguments for the call
582
669
  std::shared_ptr<rtc::DataChannel> dataChannel = dc;
583
670
  auto instance = DataChannelWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::DataChannel>>::New(env, &dataChannel)});
584
671
  args = {instance};
585
- });
586
- });
672
+ }); });
587
673
  }
588
674
 
589
675
  Napi::Value PeerConnectionWrapper::bytesSent(const Napi::CallbackInfo &info)
@@ -718,3 +804,85 @@ std::string PeerConnectionWrapper::candidateTransportTypeToString(const rtc::Can
718
804
  return "unknown";
719
805
  }
720
806
  }
807
+
808
+ Napi::Value PeerConnectionWrapper::addTrack(const Napi::CallbackInfo &info)
809
+ {
810
+ Napi::Env env = info.Env();
811
+ int length = info.Length();
812
+
813
+ if (!mRtcPeerConnPtr)
814
+ {
815
+ Napi::Error::New(info.Env(), "It seems peer-connection is closed").ThrowAsJavaScriptException();
816
+ return env.Null();
817
+ }
818
+
819
+ if (length < 1 || !info[0].IsObject())
820
+ {
821
+ Napi::TypeError::New(env, "Media class instance expected").ThrowAsJavaScriptException();
822
+ return env.Null();
823
+ }
824
+
825
+ try
826
+ {
827
+ Napi::Object obj = info[0].As<Napi::Object>();
828
+ if (obj.Get("media-type-video").IsBoolean())
829
+ {
830
+ VideoWrapper *videoPtr = Napi::ObjectWrap<VideoWrapper>::Unwrap(obj);
831
+ std::shared_ptr<rtc::Track> track = mRtcPeerConnPtr->addTrack(videoPtr->getVideoInstance());
832
+ auto instance = TrackWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::Track>>::New(info.Env(), &track)});
833
+ return instance;
834
+ }
835
+
836
+ if (obj.Get("media-type-audio").IsBoolean())
837
+ {
838
+ AudioWrapper *audioPtr = Napi::ObjectWrap<AudioWrapper>::Unwrap(obj);
839
+ std::shared_ptr<rtc::Track> track = mRtcPeerConnPtr->addTrack(audioPtr->getAudioInstance());
840
+ auto instance = TrackWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::Track>>::New(info.Env(), &track)});
841
+ return instance;
842
+ }
843
+
844
+ Napi::Error::New(env, std::string("Unknown media type")).ThrowAsJavaScriptException();
845
+ return env.Null();
846
+ }
847
+ catch (std::exception &ex)
848
+ {
849
+ Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
850
+ return env.Null();
851
+ }
852
+ }
853
+
854
+ void PeerConnectionWrapper::onTrack(const Napi::CallbackInfo &info)
855
+ {
856
+ Napi::Env env = info.Env();
857
+ int length = info.Length();
858
+
859
+ if (length < 1 || !info[0].IsFunction())
860
+ {
861
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
862
+ return;
863
+ }
864
+
865
+ // Callback
866
+ mOnTrackCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
867
+
868
+ mRtcPeerConnPtr->onTrack([&](std::shared_ptr<rtc::Track> track)
869
+ {
870
+ if (mOnTrackCallback)
871
+ mOnTrackCallback->call([this, track](Napi::Env env, std::vector<napi_value> &args) {
872
+ // Check the peer connection is not closed
873
+ if(instances.find(this) == instances.end())
874
+ throw ThreadSafeCallback::CancelException();
875
+
876
+ // This will run in main thread and needs to construct the
877
+ // arguments for the call
878
+ std::shared_ptr<rtc::Track> newTrack = track;
879
+ auto instance = TrackWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::Track>>::New(env, &newTrack)});
880
+ args = {instance};
881
+ }); });
882
+ }
883
+
884
+ Napi::Value PeerConnectionWrapper::hasMedia(const Napi::CallbackInfo &info)
885
+ {
886
+ Napi::Env env = info.Env();
887
+ return Napi::Boolean::New(env, mRtcPeerConnPtr->hasMedia());
888
+ }
@@ -5,9 +5,12 @@
5
5
  #include <string>
6
6
  #include <variant>
7
7
  #include <memory>
8
+ #include <unordered_set>
9
+
8
10
  #include <napi.h>
9
- #include <napi-thread-safe-callback.hpp>
10
- #include "rtc/rtc.hpp"
11
+ #include <rtc/rtc.hpp>
12
+
13
+ #include "thread-safe-callback.h"
11
14
 
12
15
  class PeerConnectionWrapper : public Napi::ObjectWrap<PeerConnectionWrapper>
13
16
  {
@@ -18,9 +21,13 @@ public:
18
21
 
19
22
  // Functions
20
23
  void close(const Napi::CallbackInfo &info);
24
+ void setLocalDescription(const Napi::CallbackInfo &info);
21
25
  void setRemoteDescription(const Napi::CallbackInfo &info);
26
+ Napi::Value localDescription(const Napi::CallbackInfo &info);
22
27
  void addRemoteCandidate(const Napi::CallbackInfo &info);
23
28
  Napi::Value createDataChannel(const Napi::CallbackInfo &info);
29
+ Napi::Value addTrack(const Napi::CallbackInfo &info);
30
+ Napi::Value hasMedia(const Napi::CallbackInfo &info);
24
31
 
25
32
  // Callbacks
26
33
  void onLocalDescription(const Napi::CallbackInfo &info);
@@ -28,6 +35,7 @@ public:
28
35
  void onStateChange(const Napi::CallbackInfo &info);
29
36
  void onGatheringStateChange(const Napi::CallbackInfo &info);
30
37
  void onDataChannel(const Napi::CallbackInfo &info);
38
+ void onTrack(const Napi::CallbackInfo &info);
31
39
 
32
40
  // Stats
33
41
  Napi::Value bytesSent(const Napi::CallbackInfo &info);
@@ -35,21 +43,29 @@ public:
35
43
  Napi::Value rtt(const Napi::CallbackInfo &info);
36
44
  Napi::Value getSelectedCandidatePair(const Napi::CallbackInfo &info);
37
45
 
46
+ // Close all existing DataChannels
47
+ static void CloseAll();
48
+
38
49
  private:
39
50
  static Napi::FunctionReference constructor;
51
+ static std::unordered_set<PeerConnectionWrapper *> instances;
52
+
53
+ void doClose();
54
+
40
55
  std::string mPeerName;
41
- std::shared_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
56
+ std::unique_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
42
57
 
43
58
  // Callback Ptrs
44
- std::shared_ptr<ThreadSafeCallback> mOnLocalDescriptionCallback = nullptr;
45
- std::shared_ptr<ThreadSafeCallback> mOnLocalCandidateCallback = nullptr;
46
- std::shared_ptr<ThreadSafeCallback> mOnStateChangeCallback = nullptr;
47
- std::shared_ptr<ThreadSafeCallback> mOnGatheringStateChangeCallback = nullptr;
48
- std::shared_ptr<ThreadSafeCallback> mOnDataChannelCallback = nullptr;
59
+ std::unique_ptr<ThreadSafeCallback> mOnLocalDescriptionCallback = nullptr;
60
+ std::unique_ptr<ThreadSafeCallback> mOnLocalCandidateCallback = nullptr;
61
+ std::unique_ptr<ThreadSafeCallback> mOnStateChangeCallback = nullptr;
62
+ std::unique_ptr<ThreadSafeCallback> mOnGatheringStateChangeCallback = nullptr;
63
+ std::unique_ptr<ThreadSafeCallback> mOnDataChannelCallback = nullptr;
64
+ std::unique_ptr<ThreadSafeCallback> mOnTrackCallback = nullptr;
49
65
 
50
66
  // Helpers
51
67
  std::string candidateTypeToString(const rtc::Candidate::Type &type);
52
68
  std::string candidateTransportTypeToString(const rtc::Candidate::TransportType &transportType);
53
69
  };
54
70
 
55
- #endif // PEER_CONNECTION_WRAPPER_H
71
+ #endif // PEER_CONNECTION_WRAPPER_H
@@ -1,4 +1,10 @@
1
1
  #include "rtc-wrapper.h"
2
+ #include "peer-connection-wrapper.h"
3
+ #include "data-channel-wrapper.h"
4
+ #include "media-track-wrapper.h"
5
+
6
+ #include <chrono>
7
+ #include <future>
2
8
 
3
9
  Napi::Object RtcWrapper::Init(Napi::Env env, Napi::Object exports)
4
10
  {
@@ -69,7 +75,13 @@ void RtcWrapper::cleanup(const Napi::CallbackInfo &info)
69
75
  Napi::Env env = info.Env();
70
76
  try
71
77
  {
72
- rtc::Cleanup();
78
+ PeerConnectionWrapper::CloseAll();
79
+ DataChannelWrapper::CloseAll();
80
+ TrackWrapper::CloseAll();
81
+
82
+ const auto timeout = std::chrono::seconds(10);
83
+ if(rtc::Cleanup().wait_for(std::chrono::seconds(timeout)) == std::future_status::timeout)
84
+ throw std::runtime_error("cleanup timeout (possible deadlock)");
73
85
  }
74
86
  catch (std::exception &ex)
75
87
  {
package/src/rtc-wrapper.h CHANGED
@@ -3,7 +3,9 @@
3
3
 
4
4
  #include <iostream>
5
5
  #include <string>
6
+
6
7
  #include <napi.h>
8
+
7
9
  #include "rtc/rtc.hpp"
8
10
 
9
11
  class RtcWrapper
@@ -16,4 +18,4 @@ public:
16
18
  static void setSctpSettings(const Napi::CallbackInfo &info);
17
19
  };
18
20
 
19
- #endif // RTC_WRAPPER_H
21
+ #endif // RTC_WRAPPER_H
@@ -0,0 +1,69 @@
1
+ #include "thread-safe-callback.h"
2
+
3
+ #ifdef LEGACY_NAPI_THREAD_SAFE_CALLBACK
4
+
5
+ // Nothing to do
6
+
7
+ #else
8
+
9
+ const char* ThreadSafeCallback::CancelException::what() const throw()
10
+ {
11
+ return "ThreadSafeCallback cancelled";
12
+ }
13
+
14
+ ThreadSafeCallback::ThreadSafeCallback(Napi::Function callback)
15
+ {
16
+ if (!callback.IsFunction())
17
+ throw Napi::Error::New(callback.Env(), "Callback must be a function");
18
+
19
+ Napi::Env env = callback.Env();
20
+
21
+ receiver = Napi::Persistent(static_cast<Napi::Value>(Napi::Object::New(env)));
22
+ tsfn = tsfn_t::New(env,
23
+ std::move(callback),
24
+ "ThreadSafeCallback callback",
25
+ 0, 1, &receiver);
26
+ }
27
+
28
+ ThreadSafeCallback::~ThreadSafeCallback()
29
+ {
30
+ tsfn.Abort();
31
+ }
32
+
33
+ void ThreadSafeCallback::call(arg_func_t argFunc)
34
+ {
35
+ CallbackData *data = new CallbackData{std::move(argFunc)};
36
+ if (tsfn.BlockingCall(data) != napi_ok)
37
+ {
38
+ delete data;
39
+ throw std::runtime_error("Failed to call JavaScript callback");
40
+ }
41
+ }
42
+
43
+ void ThreadSafeCallback::callbackFunc(Napi::Env env,
44
+ Napi::Function callback,
45
+ Napi::Reference<Napi::Value> *context,
46
+ CallbackData *data)
47
+ {
48
+ if (!data)
49
+ return;
50
+
51
+ arg_vector_t args;
52
+ arg_func_t argFunc(std::move(data->argFunc));
53
+ delete data;
54
+
55
+ try
56
+ {
57
+ argFunc(env, args);
58
+ }
59
+ catch (CancelException &)
60
+ {
61
+ return;
62
+ }
63
+
64
+ if (env && callback)
65
+ callback.Call(context->Value(), args);
66
+ }
67
+
68
+ #endif
69
+
@@ -0,0 +1,57 @@
1
+ #ifndef THREAD_SAFE_CALLBACK_H
2
+ #define THREAD_SAFE_CALLBACK_H
3
+
4
+ #ifdef LEGACY_NAPI_THREAD_SAFE_CALLBACK
5
+
6
+ #include "napi-thread-safe-callback.hpp"
7
+
8
+ #else
9
+
10
+ #include <napi.h>
11
+
12
+ #include <vector>
13
+ #include <functional>
14
+
15
+ class ThreadSafeCallback
16
+ {
17
+ public:
18
+ using arg_vector_t = std::vector<napi_value>;
19
+ using arg_func_t = std::function<void(napi_env, arg_vector_t &)>;
20
+
21
+ ThreadSafeCallback(Napi::Function callback);
22
+ ~ThreadSafeCallback();
23
+
24
+ ThreadSafeCallback(const ThreadSafeCallback &) = delete;
25
+ ThreadSafeCallback(ThreadSafeCallback &&) = delete;
26
+
27
+ ThreadSafeCallback& operator=(const ThreadSafeCallback &) = delete;
28
+ ThreadSafeCallback& operator=(ThreadSafeCallback &&) = delete;
29
+
30
+ void call(arg_func_t argFunc);
31
+
32
+ class CancelException : public std::exception
33
+ {
34
+ const char* what() const throw();
35
+ };
36
+
37
+ private:
38
+ struct CallbackData
39
+ {
40
+ arg_func_t argFunc;
41
+ };
42
+
43
+ static void callbackFunc(Napi::Env env,
44
+ Napi::Function callback,
45
+ Napi::Reference<Napi::Value> *context,
46
+ CallbackData *data);
47
+
48
+ using tsfn_t = Napi::TypedThreadSafeFunction<Napi::Reference<Napi::Value>, CallbackData, callbackFunc>;
49
+
50
+ Napi::Reference<Napi::Value> receiver;
51
+ tsfn_t tsfn;
52
+ };
53
+
54
+ #endif // LEGACY_NAPI_THREAD_SAFE_CALLBACK
55
+
56
+ #endif // THREAD_SAFE_CALLBACK_H
57
+
@@ -96,5 +96,6 @@ setTimeout(() => {
96
96
  peer1 = null;
97
97
  peer2 = null;
98
98
  nodeDataChannel.cleanup();
99
- process.exit();
99
+ // No need for this (>= V0.1.14)
100
+ // process.exit();
100
101
  }, 5 * 1000);