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.
- package/CMakeLists.txt +18 -3
- package/README.md +224 -71
- package/examples/README.md +24 -2
- package/examples/media/main.html +45 -0
- package/examples/media/media.js +54 -0
- package/lib/index.d.ts +96 -1
- package/package.json +20 -6
- package/src/data-channel-wrapper.cpp +85 -101
- package/src/data-channel-wrapper.h +18 -11
- package/src/main.cpp +8 -0
- package/src/media-audio-wrapper.cpp +457 -0
- package/src/media-audio-wrapper.h +54 -0
- package/src/media-direction.cpp +43 -0
- package/src/media-direction.h +10 -0
- package/src/media-rtcpreceivingsession-wrapper.cpp +63 -0
- package/src/media-rtcpreceivingsession-wrapper.h +36 -0
- package/src/media-track-wrapper.cpp +333 -0
- package/src/media-track-wrapper.h +58 -0
- package/src/media-video-wrapper.cpp +490 -0
- package/src/media-video-wrapper.h +56 -0
- package/src/peer-connection-wrapper.cpp +218 -50
- package/src/peer-connection-wrapper.h +25 -9
- package/src/rtc-wrapper.cpp +13 -1
- package/src/rtc-wrapper.h +3 -1
- package/src/thread-safe-callback.cpp +69 -0
- package/src/thread-safe-callback.h +57 -0
- package/test/connectivity.js +2 -1
- package/test/test.js +111 -112
|
@@ -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
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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::
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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 <
|
|
10
|
-
|
|
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::
|
|
56
|
+
std::unique_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
|
|
42
57
|
|
|
43
58
|
// Callback Ptrs
|
|
44
|
-
std::
|
|
45
|
-
std::
|
|
46
|
-
std::
|
|
47
|
-
std::
|
|
48
|
-
std::
|
|
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
|
package/src/rtc-wrapper.cpp
CHANGED
|
@@ -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
|
-
|
|
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
|
+
|