diff --git a/binding.gyp b/binding.gyp index d7fbca62c6..66852b77e1 100644 --- a/binding.gyp +++ b/binding.gyp @@ -22,6 +22,8 @@ 'deps/exokit-bindings/windowsystem/src/*.cc', 'deps/exokit-bindings/glfw/src/*.cc', 'deps/exokit-bindings/webrtc/src/*.cc', + 'deps/exokit-bindings/webrtc/src/converters/*.cc', + 'deps/exokit-bindings/webrtc/src/webrtc/*.cc', 'deps/oculus/src/*.cpp', 'deps/openvr/src/*.cpp', 'deps/exokit-bindings/leapmotion/src/*.cc', @@ -38,6 +40,8 @@ " makeCanvasPattern(); Local makeAudio(); Local makeVideo(Local imageDataCons); Local makeBrowser(); -Local makeRtc(); +Local makeRtc(Local module); #endif diff --git a/deps/exokit-bindings/bindings/src/bindings.cc b/deps/exokit-bindings/bindings/src/bindings.cc index 16b1898ffd..64b6121f50 100644 --- a/deps/exokit-bindings/bindings/src/bindings.cc +++ b/deps/exokit-bindings/bindings/src/bindings.cc @@ -113,11 +113,7 @@ Local makeAudio() { exports->Set(JS_STR("AudioProcessingEvent"), audioProcessingEventCons); Local scriptProcessorNodeCons = webaudio::ScriptProcessorNode::Initialize(isolate, audioBufferCons, audioProcessingEventCons); exports->Set(JS_STR("ScriptProcessorNode"), scriptProcessorNodeCons); - Local mediaStreamTrackCons = webaudio::MediaStreamTrack::Initialize(isolate); - exports->Set(JS_STR("MediaStreamTrack"), mediaStreamTrackCons); - Local microphoneMediaStreamCons = webaudio::MicrophoneMediaStream::Initialize(isolate, mediaStreamTrackCons); - exports->Set(JS_STR("MicrophoneMediaStream"), microphoneMediaStreamCons); - exports->Set(JS_STR("AudioContext"), webaudio::AudioContext::Initialize(isolate, audioListenerCons, audioSourceNodeCons, audioDestinationNodeCons, gainNodeCons, analyserNodeCons, pannerNodeCons, audioBufferCons, audioBufferSourceNodeCons, audioProcessingEventCons, stereoPannerNodeCons, oscillatorNodeCons, scriptProcessorNodeCons, mediaStreamTrackCons, microphoneMediaStreamCons)); + exports->Set(JS_STR("AudioContext"), webaudio::AudioContext::Initialize(isolate, audioListenerCons, audioSourceNodeCons, audioDestinationNodeCons, gainNodeCons, analyserNodeCons, pannerNodeCons, audioBufferCons, audioBufferSourceNodeCons, audioProcessingEventCons, stereoPannerNodeCons, oscillatorNodeCons, scriptProcessorNodeCons)); return scope.Escape(exports); } @@ -136,14 +132,14 @@ Local makeBrowser() { } #endif -Local makeRtc() { +Local makeRtc(Local module) { Isolate *isolate = Isolate::GetCurrent(); Nan::EscapableHandleScope scope; Local exports = Nan::New(); - node_webrtc::init(exports); + node_webrtc::init(exports, module); return scope.Escape(exports); } diff --git a/deps/exokit-bindings/webaudiocontext/include/AudioContext.h b/deps/exokit-bindings/webaudiocontext/include/AudioContext.h index 281a85591c..36fca0c49a 100644 --- a/deps/exokit-bindings/webaudiocontext/include/AudioContext.h +++ b/deps/exokit-bindings/webaudiocontext/include/AudioContext.h @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include using namespace std; @@ -32,7 +30,7 @@ lab::AudioContext *getDefaultAudioContext(float sampleRate = lab::DefaultSampleR class AudioContext : public ObjectWrap { public: - static Local Initialize(Isolate *isolate, Local audioListenerCons, Local audioSourceNodeCons, Local audioDestinationNodeCons, Local gainNodeCons, Local analyserNodeCons, Local pannerNodeCons, Local audioBufferCons, Local audioBufferSourceNodeCons, Local audioProcessingEventCons, Local stereoPannerNodeCons, Local oscillatorNodeCons, Local scriptProcessorNodeCons, Local mediaStreamTrackCons, Local microphoneMediaStreamCons); + static Local Initialize(Isolate *isolate, Local audioListenerCons, Local audioSourceNodeCons, Local audioDestinationNodeCons, Local gainNodeCons, Local analyserNodeCons, Local pannerNodeCons, Local audioBufferCons, Local audioBufferSourceNodeCons, Local audioProcessingEventCons, Local stereoPannerNodeCons, Local oscillatorNodeCons, Local scriptProcessorNodeCons); void Close(); Local CreateMediaElementSource(Local audioDestinationNodeConstructor, Local mediaElement, Local audioContextObj); Local CreateMediaStreamSource(Local audioSourceNodeConstructor, Local mediaStream, Local audioContextObj); diff --git a/deps/exokit-bindings/webaudiocontext/include/MediaStreamTrack.h b/deps/exokit-bindings/webaudiocontext/include/MediaStreamTrack.h deleted file mode 100644 index c57413e4b5..0000000000 --- a/deps/exokit-bindings/webaudiocontext/include/MediaStreamTrack.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _MEDIA_STREAM_TRACK_H_ -#define _MEDIA_STREAM_TRACK_H_ - -#include -#include -#include -#include "LabSound/extended/LabSound.h" -#include -#include - -using namespace std; -using namespace v8; -using namespace node; - -namespace webaudio { - -class MediaStream; - -class MediaStreamTrack : public ObjectWrap { -public: - static Local Initialize(Isolate *isolate); - static void InitializePrototype(Local proto); - -protected: - static NAN_METHOD(New); - static NAN_GETTER(EnabledGetter); - static NAN_GETTER(KindGetter); - static NAN_GETTER(LabelGetter); - static NAN_GETTER(MutedGetter); - static NAN_SETTER(MutedSetter); - static NAN_GETTER(ReadyStateGetter); - static NAN_METHOD(Stop); - - MediaStreamTrack(MediaStream *mediaStream); - ~MediaStreamTrack(); - -protected: - std::shared_ptr audioNode; - MediaStream *mediaStream; - bool live; - bool muted; - - friend class AudioSourceNode; -}; - -} - -#endif diff --git a/deps/exokit-bindings/webaudiocontext/include/MicrophoneMediaStream.h b/deps/exokit-bindings/webaudiocontext/include/MicrophoneMediaStream.h deleted file mode 100644 index e77218effc..0000000000 --- a/deps/exokit-bindings/webaudiocontext/include/MicrophoneMediaStream.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _MICROPHONE_MEDIA_STREAM_H_ -#define _MICROPHONE_MEDIA_STREAM_H_ - -#include -#include -#include -#include "LabSound/extended/LabSound.h" -#include -#include - -using namespace std; -using namespace v8; -using namespace node; - -namespace webaudio { - -class MediaStreamTrack; - -class MediaStream : public ObjectWrap { - protected: - // std::shared_ptr audioNode; - - friend class MediaStreamTrack; -}; - -class MicrophoneMediaStream : public MediaStream { -public: - static Local Initialize(Isolate *isolate, Local mediaStreamTrackCons); - static void InitializePrototype(Local proto); - -protected: - static NAN_METHOD(New); - static NAN_METHOD(GetTracks); - - MicrophoneMediaStream(); - ~MicrophoneMediaStream(); - -protected: - Nan::Persistent tracks; - - friend class AudioSourceNode; -}; - -} - -#endif diff --git a/deps/exokit-bindings/webaudiocontext/src/AudioContext.cpp b/deps/exokit-bindings/webaudiocontext/src/AudioContext.cpp index 4732e67def..bcc59baa6f 100644 --- a/deps/exokit-bindings/webaudiocontext/src/AudioContext.cpp +++ b/deps/exokit-bindings/webaudiocontext/src/AudioContext.cpp @@ -21,7 +21,7 @@ AudioContext::AudioContext(float sampleRate) { AudioContext::~AudioContext() {} -Local AudioContext::Initialize(Isolate *isolate, Local audioListenerCons, Local audioSourceNodeCons, Local audioDestinationNodeCons, Local gainNodeCons, Local analyserNodeCons, Local pannerNodeCons, Local audioBufferCons, Local audioBufferSourceNodeCons, Local audioProcessingEventCons, Local stereoPannerNodeCons, Local oscillatorNodeCons, Local scriptProcessorNodeCons, Local mediaStreamTrackCons, Local microphoneMediaStreamCons) { +Local AudioContext::Initialize(Isolate *isolate, Local audioListenerCons, Local audioSourceNodeCons, Local audioDestinationNodeCons, Local gainNodeCons, Local analyserNodeCons, Local pannerNodeCons, Local audioBufferCons, Local audioBufferSourceNodeCons, Local audioProcessingEventCons, Local stereoPannerNodeCons, Local oscillatorNodeCons, Local scriptProcessorNodeCons) { #if defined(ANDROID) || defined(LUMIN) lab::SetGenericFunctions( adgCreate, @@ -83,8 +83,6 @@ Local AudioContext::Initialize(Isolate *isolate, Local audioListe ctorFn->Set(JS_STR("AudioBufferSourceNode"), audioBufferSourceNodeCons); ctorFn->Set(JS_STR("AudioProcessingEvent"), audioProcessingEventCons); ctorFn->Set(JS_STR("ScriptProcessorNode"), scriptProcessorNodeCons); - ctorFn->Set(JS_STR("MediaStreamTrack"), mediaStreamTrackCons); - ctorFn->Set(JS_STR("MicrophoneMediaStream"), microphoneMediaStreamCons); return scope.Escape(ctorFn); } @@ -314,14 +312,14 @@ NAN_METHOD(AudioContext::CreateMediaElementSource) { NAN_METHOD(AudioContext::CreateMediaStreamSource) { Nan::HandleScope scope; - if (info[0]->IsObject() && JS_OBJ(JS_OBJ(info[0])->Get(JS_STR("constructor")))->Get(JS_STR("name"))->StrictEquals(JS_STR("MicrophoneMediaStream"))) { - Local microphoneMediaStream = Local::Cast(info[0]); + if (info[0]->IsObject() && JS_OBJ(JS_OBJ(info[0])->Get(JS_STR("constructor")))->Get(JS_STR("name"))->StrictEquals(JS_STR("MediaStream"))) { + Local mediaStream = Local::Cast(info[0]); Local audioContextObj = info.This(); AudioContext *audioContext = ObjectWrap::Unwrap(audioContextObj); Local audioSourceNodeConstructor = Local::Cast(JS_OBJ(audioContextObj->Get(JS_STR("constructor")))->Get(JS_STR("AudioSourceNode"))); - Local audioNodeObj = audioContext->CreateMediaStreamSource(audioSourceNodeConstructor, microphoneMediaStream, audioContextObj); + Local audioNodeObj = audioContext->CreateMediaStreamSource(audioSourceNodeConstructor, mediaStream, audioContextObj); info.GetReturnValue().Set(audioNodeObj); } else { diff --git a/deps/exokit-bindings/webaudiocontext/src/AudioSourceNode.cpp b/deps/exokit-bindings/webaudiocontext/src/AudioSourceNode.cpp index 666264c737..b79a2913f2 100644 --- a/deps/exokit-bindings/webaudiocontext/src/AudioSourceNode.cpp +++ b/deps/exokit-bindings/webaudiocontext/src/AudioSourceNode.cpp @@ -52,7 +52,7 @@ NAN_METHOD(AudioSourceNode::New) { } else { Nan::ThrowError("AudioSourceNode: invalid audio element state"); } - } else if (info[0]->IsObject() && JS_OBJ(JS_OBJ(info[0])->Get(JS_STR("constructor")))->Get(JS_STR("name"))->StrictEquals(JS_STR("MicrophoneMediaStream"))) { + } else if (info[0]->IsObject() && JS_OBJ(JS_OBJ(info[0])->Get(JS_STR("constructor")))->Get(JS_STR("name"))->StrictEquals(JS_STR("MediaStream"))) { AudioSourceNode *audioSourceNode = new AudioSourceNode(); Local audioSourceNodeObj = info.This(); audioSourceNode->Wrap(audioSourceNodeObj); @@ -66,22 +66,6 @@ NAN_METHOD(AudioSourceNode::New) { } info.GetReturnValue().Set(audioSourceNodeObj); - - /* Local microphoneMediaStreamObj = Local::Cast(info[0]); - MicrophoneMediaStream *microphoneMediaStream = ObjectWrap::Unwrap(Local::Cast(microphoneMediaStreamObj)); - - if (microphoneMediaStream->audioNode) { - AudioSourceNode *audioSourceNode = new AudioSourceNode(); - Local audioSourceNodeObj = info.This(); - audioSourceNode->Wrap(audioSourceNodeObj); - - audioSourceNode->context.Reset(audioContextObj); - audioSourceNode->audioNode = microphoneMediaStream->audioNode; - - info.GetReturnValue().Set(audioSourceNodeObj); - } else { - Nan::ThrowError("AudioSourceNode: media stream is not live"); - } */ } else { Nan::ThrowError("AudioSourceNode: invalid media element"); } diff --git a/deps/exokit-bindings/webaudiocontext/src/MediaStreamTrack.cpp b/deps/exokit-bindings/webaudiocontext/src/MediaStreamTrack.cpp deleted file mode 100644 index 493f8f9482..0000000000 --- a/deps/exokit-bindings/webaudiocontext/src/MediaStreamTrack.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include - -namespace webaudio { - -MediaStreamTrack::MediaStreamTrack(MediaStream *mediaStream) : mediaStream(mediaStream), live(true), muted(false) {} - -MediaStreamTrack::~MediaStreamTrack() {} - -Local MediaStreamTrack::Initialize(Isolate *isolate) { - Nan::EscapableHandleScope scope; - - // constructor - Local ctor = Nan::New(New); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(JS_STR("MediaStreamTrack")); - - // prototype - Local proto = ctor->PrototypeTemplate(); - MediaStreamTrack::InitializePrototype(proto); - - Local ctorFn = Nan::GetFunction(ctor).ToLocalChecked(); - - return scope.Escape(ctorFn); -} - -void MediaStreamTrack::InitializePrototype(Local proto) { - Nan::SetAccessor(proto, JS_STR("enabled"), EnabledGetter); - Nan::SetAccessor(proto, JS_STR("kind"), KindGetter); - Nan::SetAccessor(proto, JS_STR("label"), LabelGetter); - Nan::SetAccessor(proto, JS_STR("muted"), MutedGetter, MutedSetter); - Nan::SetAccessor(proto, JS_STR("readyState"), ReadyStateGetter); - Nan::SetMethod(proto, "stop", Stop); -} - -NAN_METHOD(MediaStreamTrack::New) { - // Nan::HandleScope scope; - - if (info[0]->IsObject() && JS_OBJ(JS_OBJ(info[0])->Get(JS_STR("constructor")))->Get(JS_STR("name"))->StrictEquals(JS_STR("MicrophoneMediaStream"))) { - Local microphoneMediaStreamObj = Local::Cast(info[0]); - MicrophoneMediaStream *microphoneMediaStream = ObjectWrap::Unwrap(microphoneMediaStreamObj); - - MediaStreamTrack *mediaStreamTrack = new MediaStreamTrack(microphoneMediaStream); - Local mediaStreamTrackObj = info.This(); - mediaStreamTrack->Wrap(mediaStreamTrackObj); - - info.GetReturnValue().Set(mediaStreamTrackObj); - } else { - Nan::ThrowError("MediaStreamTrack: invalid arguments"); - } -} - -NAN_GETTER(MediaStreamTrack::EnabledGetter) { - // Nan::HandleScope scope; - - info.GetReturnValue().Set(JS_BOOL(true)); -} - -NAN_GETTER(MediaStreamTrack::KindGetter) { - // Nan::HandleScope scope; - - info.GetReturnValue().Set(JS_STR("audio")); -} - -NAN_GETTER(MediaStreamTrack::LabelGetter) { - // Nan::HandleScope scope; - - info.GetReturnValue().Set(JS_STR("microphone")); -} - -NAN_GETTER(MediaStreamTrack::MutedGetter) { - // Nan::HandleScope scope; - - MediaStreamTrack *mediaStreamTrack = ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(JS_BOOL(mediaStreamTrack->muted)); -} - -NAN_SETTER(MediaStreamTrack::MutedSetter) { - // Nan::HandleScope scope; - - if (value->IsBoolean()) { - MediaStreamTrack *mediaStreamTrack = ObjectWrap::Unwrap(info.This()); - - bool muted = TO_BOOL(value); - mediaStreamTrack->muted = muted; - } else { - Nan::ThrowError("invalid arguments"); - } -} - -NAN_GETTER(MediaStreamTrack::ReadyStateGetter) { - // Nan::HandleScope scope; - - MediaStreamTrack *mediaStreamTrack = ObjectWrap::Unwrap(info.This()); - - info.GetReturnValue().Set(JS_STR(mediaStreamTrack->live ? "live" : "ended")); -} - -NAN_METHOD(MediaStreamTrack::Stop) { - // Nan::HandleScope scope; - - /* MediaStreamTrack *mediaStreamTrack = ObjectWrap::Unwrap(info.This()); // XXX actually stop the audio node - MediaStream *mediaStream = mediaStreamTrack->mediaStream; - mediaStream->audioNode.reset(); */ -} - -} diff --git a/deps/exokit-bindings/webaudiocontext/src/MicrophoneMediaStream.cpp b/deps/exokit-bindings/webaudiocontext/src/MicrophoneMediaStream.cpp deleted file mode 100644 index dd97271743..0000000000 --- a/deps/exokit-bindings/webaudiocontext/src/MicrophoneMediaStream.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include - -namespace webaudio { - -MicrophoneMediaStream::MicrophoneMediaStream() : tracks(Nan::New(0)) {} - -MicrophoneMediaStream::~MicrophoneMediaStream() {} - -Local MicrophoneMediaStream::Initialize(Isolate *isolate, Local mediaStreamTrackCons) { - Nan::EscapableHandleScope scope; - - // constructor - Local ctor = Nan::New(New); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(JS_STR("MicrophoneMediaStream")); - - // prototype - Local proto = ctor->PrototypeTemplate(); - // AudioNode::InitializePrototype(proto); - // AudioSourceNode::InitializePrototype(proto); - MicrophoneMediaStream::InitializePrototype(proto); - - Local ctorFn = Nan::GetFunction(ctor).ToLocalChecked(); - ctorFn->Set(JS_STR("MediaStreamTrack"), mediaStreamTrackCons); - - return scope.Escape(ctorFn); -} - -void MicrophoneMediaStream::InitializePrototype(Local proto) { - Nan::SetMethod(proto, "getTracks", GetTracks); -} - -NAN_METHOD(MicrophoneMediaStream::New) { - Nan::HandleScope scope; - - MicrophoneMediaStream *microphoneMediaStream = new MicrophoneMediaStream(); - Local microphoneMediaStreamObj = info.This(); - microphoneMediaStream->Wrap(microphoneMediaStreamObj); - - Local mediaStreamTrackConstructor = Local::Cast(JS_OBJ(microphoneMediaStreamObj->Get(JS_STR("constructor")))->Get(JS_STR("MediaStreamTrack"))); - Local argv[] = { - microphoneMediaStreamObj, - }; - Local mediaStreamTrackObj = mediaStreamTrackConstructor->NewInstance(Isolate::GetCurrent()->GetCurrentContext(), sizeof(argv)/sizeof(argv[0]), argv).ToLocalChecked(); - Local localTracks = Nan::New(microphoneMediaStream->tracks); - localTracks->Set(0, mediaStreamTrackObj); - - /* { - lab::ContextRenderLock r(getDefaultAudioContext(), "MicrophoneMediaStream::New"); - microphoneMediaStream->audioNode = lab::MakeHardwareSourceNode(r); - } */ - - info.GetReturnValue().Set(microphoneMediaStreamObj); -} - -NAN_METHOD(MicrophoneMediaStream::GetTracks) { - Nan::HandleScope scope; - - MicrophoneMediaStream *microphoneMediaStream = ObjectWrap::Unwrap(info.This()); - Local localTracks = Nan::New(microphoneMediaStream->tracks); - info.GetReturnValue().Set(localTracks); -} - -} diff --git a/deps/exokit-bindings/webrtc/include/create-answer-observer.h b/deps/exokit-bindings/webrtc/include/create-answer-observer.h deleted file mode 100644 index dad3405e82..0000000000 --- a/deps/exokit-bindings/webrtc/include/create-answer-observer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_CREATE_ANSWER_OBSERVER_H_ -#define SRC_CREATE_ANSWER_OBSERVER_H_ - -#include - -#include "webrtc/api/jsep.h" - -namespace node_webrtc { - -class PeerConnection; - -class CreateAnswerObserver - : public webrtc::CreateSessionDescriptionObserver { - private: - PeerConnection* parent; - - public: - explicit CreateAnswerObserver(PeerConnection* connection): parent(connection) {} - - virtual void OnSuccess(webrtc::SessionDescriptionInterface* sdp); - virtual void OnFailure(const std::string& msg); -}; - -} // namespace node_webrtc - -#endif // SRC_CREATE_ANSWER_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/include/create-offer-observer.h b/deps/exokit-bindings/webrtc/include/create-offer-observer.h deleted file mode 100644 index 9aa45df0c9..0000000000 --- a/deps/exokit-bindings/webrtc/include/create-offer-observer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_CREATE_OFFER_OBSERVER_H_ -#define SRC_CREATE_OFFER_OBSERVER_H_ - -#include - -#include "webrtc/api/jsep.h" -#include "webrtc/api/rtcerror.h" - -namespace node_webrtc { - -class PeerConnection; - -class CreateOfferObserver - : public webrtc::CreateSessionDescriptionObserver { - private: - PeerConnection* parent; - - public: - explicit CreateOfferObserver(PeerConnection* connection): parent(connection) {} - - virtual void OnSuccess(webrtc::SessionDescriptionInterface* sdp) override; - virtual void OnFailure(webrtc::RTCError error) override; - virtual void OnFailure(const std::string& msg) override; -}; - -} // namespace node_webrtc - -#endif // SRC_CREATE_OFFER_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/include/datachannel.h b/deps/exokit-bindings/webrtc/include/datachannel.h deleted file mode 100644 index 3cb76f1ea9..0000000000 --- a/deps/exokit-bindings/webrtc/include/datachannel.h +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_DATACHANNEL_H_ -#define SRC_DATACHANNEL_H_ - -#include - -#include -#include - -#include "nan.h" -#include "uv.h" -#include "v8.h" // IWYU pragma: keep - -#include "webrtc/api/datachannelinterface.h" -#include "webrtc/rtc_base/buffer.h" -#include "webrtc/rtc_base/scoped_ref_ptr.h" - -#include "peerconnectionfactory.h" - -#undef ERROR - -namespace node_webrtc { - -class DataChannelObserver; - -class DataChannel - : public Nan::ObjectWrap - , public webrtc::DataChannelObserver { - friend class node_webrtc::DataChannelObserver; - public: - struct ErrorEvent { - explicit ErrorEvent(const std::string& msg) - : msg(msg) {} - - std::string msg; - }; - - struct MessageEvent { - explicit MessageEvent(const webrtc::DataBuffer* buffer) { - binary = buffer->binary; - size = buffer->size(); - message = std::shared_ptr(new char[size], std::default_delete()); - memcpy(static_cast(message.get()), static_cast(buffer->data.data()), size); - } - - bool binary; - std::shared_ptr message; - size_t size; - }; - - struct StateEvent { - explicit StateEvent(const webrtc::DataChannelInterface::DataState state) - : state(state) {} - - webrtc::DataChannelInterface::DataState state; - }; - - enum AsyncEventType { - MESSAGE = 0x1 << 0, // 1 - ERROR = 0x1 << 1, // 2 - STATE = 0x1 << 2, // 4 - }; - - enum BinaryType { - BLOB = 0x0, - ARRAY_BUFFER = 0x1 - }; - - explicit DataChannel(node_webrtc::DataChannelObserver* observer); - ~DataChannel(); - - // - // DataChannelObserver implementation. - // - - virtual void OnStateChange(); - virtual void OnMessage(const webrtc::DataBuffer& buffer); - - // - // Nodejs wrapping. - // - static void Init(v8::Local exports); - static Nan::Persistent constructor; - static NAN_METHOD(New); - - static NAN_METHOD(Send); - static NAN_METHOD(Close); - - static NAN_GETTER(GetBufferedAmount); - static NAN_GETTER(GetLabel); - static NAN_GETTER(GetBinaryType); - static NAN_GETTER(GetReadyState); - static NAN_SETTER(SetBinaryType); - static NAN_SETTER(ReadOnly); - - void QueueEvent(DataChannel::AsyncEventType type, void* data); - - private: - static void Run(uv_async_t* handle, int status); - - struct AsyncEvent { - AsyncEventType type; - void* data; - }; - - uv_mutex_t lock; - uv_async_t async; - uv_loop_t* loop; - std::queue _events; - - std::shared_ptr _factory; - rtc::scoped_refptr _jingleDataChannel; - BinaryType _binaryType; - -#if NODE_MODULE_VERSION < 0x000C - static Nan::Persistent ArrayBufferConstructor; - -#endif -}; - -class DataChannelObserver - : public webrtc::DataChannelObserver { - friend class node_webrtc::DataChannel; - public: - explicit DataChannelObserver(std::shared_ptr factory, - rtc::scoped_refptr jingleDataChannel); - virtual ~DataChannelObserver(); - - virtual void OnStateChange(); - virtual void OnMessage(const webrtc::DataBuffer& buffer); - - private: - void QueueEvent(DataChannel::AsyncEventType type, void* data); - - uv_mutex_t lock; - std::queue _events; - std::shared_ptr _factory; - rtc::scoped_refptr _jingleDataChannel; -}; - -} // namespace node_webrtc - -#endif // SRC_DATACHANNEL_H_ diff --git a/deps/exokit-bindings/webrtc/include/peerconnection.h b/deps/exokit-bindings/webrtc/include/peerconnection.h deleted file mode 100644 index bba0637d1e..0000000000 --- a/deps/exokit-bindings/webrtc/include/peerconnection.h +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_PEERCONNECTION_H_ -#define SRC_PEERCONNECTION_H_ - -#include - -#include -#include - -#include "nan.h" -#include "uv.h" -#include "v8.h" // IWYU pragma: keep - -#include "webrtc/api/datachannelinterface.h" // IWYU pragma: keep -#include "webrtc/api/jsep.h" -#include "webrtc/api/peerconnectioninterface.h" -#include "webrtc/api/statstypes.h" -#include "webrtc/rtc_base/scoped_ref_ptr.h" - -#include "peerconnectionfactory.h" - -namespace node_webrtc { - -class CreateOfferObserver; -class CreateAnswerObserver; -class DataChannelObserver; -class SetLocalDescriptionObserver; -class SetRemoteDescriptionObserver; - -class PeerConnection - : public Nan::ObjectWrap - , public webrtc::PeerConnectionObserver { - public: - struct ErrorEvent { - explicit ErrorEvent(const std::string& msg) - : msg(msg) {} - - std::string msg; - }; - - struct SdpEvent { - explicit SdpEvent(webrtc::SessionDescriptionInterface* sdp) { - if (!sdp->ToString(&desc)) { - desc = ""; - } - type = sdp->type(); - } - - std::string type; - std::string desc; - }; - - struct IceEvent { - explicit IceEvent(const webrtc::IceCandidateInterface* ice_candidate) - : sdpMLineIndex(ice_candidate->sdp_mline_index()) - , sdpMid(ice_candidate->sdp_mid()) { - ice_candidate->ToString(&candidate); - } - - uint32_t sdpMLineIndex; - std::string sdpMid; - std::string candidate; - }; - - struct StateEvent { - explicit StateEvent(uint32_t state) - : state(state) {} - - uint32_t state; - }; - - struct DataChannelEvent { - explicit DataChannelEvent(DataChannelObserver* observer) - : observer(observer) {} - - DataChannelObserver* observer; - }; - - struct GetStatsEvent { - GetStatsEvent(Nan::Callback* callback, webrtc::StatsReports reports) - : callback(callback), reports(reports) {} - - Nan::Callback* callback; - webrtc::StatsReports reports; - }; - - enum AsyncEventType { - CREATE_OFFER_SUCCESS = 0x1 << 0, // 1 - CREATE_OFFER_ERROR = 0x1 << 1, // 2 - CREATE_ANSWER_SUCCESS = 0x1 << 2, // 4 - CREATE_ANSWER_ERROR = 0x1 << 3, // 8 - SET_LOCAL_DESCRIPTION_SUCCESS = 0x1 << 4, // 16 - SET_LOCAL_DESCRIPTION_ERROR = 0x1 << 5, // 32 - SET_REMOTE_DESCRIPTION_SUCCESS = 0x1 << 6, // 64 - SET_REMOTE_DESCRIPTION_ERROR = 0x1 << 7, // 128 - ADD_ICE_CANDIDATE_SUCCESS = 0x1 << 8, // 256 - ADD_ICE_CANDIDATE_ERROR = 0x1 << 9, // 512 - NOTIFY_DATA_CHANNEL = 0x1 << 10, // 1024 - NOTIFY_CONNECTION = 0x1 << 11, // 2048 - NOTIFY_CLOSED_CONNECTION = 0x1 << 12, // 4096 - ICE_CANDIDATE = 0x1 << 13, // 8192 - SIGNALING_STATE_CHANGE = 0x1 << 14, // 16384 - ICE_CONNECTION_STATE_CHANGE = 0x1 << 15, // 32768 - ICE_GATHERING_STATE_CHANGE = 0x1 << 16, // 65536 - NOTIFY_ADD_STREAM = 0x1 << 17, // 131072 - NOTIFY_REMOVE_STREAM = 0x1 << 18, // 262144 - GET_STATS_SUCCESS = 0x1 << 19, // 524288 - - ERROR_EVENT = CREATE_OFFER_ERROR | CREATE_ANSWER_ERROR | - SET_LOCAL_DESCRIPTION_ERROR | SET_REMOTE_DESCRIPTION_ERROR | - ADD_ICE_CANDIDATE_ERROR, - SDP_EVENT = CREATE_OFFER_SUCCESS | CREATE_ANSWER_SUCCESS, - VOID_EVENT = SET_LOCAL_DESCRIPTION_SUCCESS | SET_REMOTE_DESCRIPTION_SUCCESS | - ADD_ICE_CANDIDATE_SUCCESS | GET_STATS_SUCCESS, - STATE_EVENT = SIGNALING_STATE_CHANGE | ICE_CONNECTION_STATE_CHANGE | - ICE_GATHERING_STATE_CHANGE - }; - - explicit PeerConnection(webrtc::PeerConnectionInterface::IceServers iceServerList); - ~PeerConnection(); - - // - // PeerConnectionObserver implementation. - // - - virtual void OnError(); - - virtual void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state); - virtual void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state); - virtual void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state); - virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate); - virtual void OnRenegotiationNeeded(); - - virtual void OnDataChannel(rtc::scoped_refptr data_channel); - - virtual void OnAddStream(rtc::scoped_refptr stream); - virtual void OnRemoveStream(rtc::scoped_refptr stream); - - // - // Nodejs wrapping. - // - static void Init(v8::Local exports); - static Nan::Persistent constructor; - static NAN_METHOD(New); - - static NAN_METHOD(CreateOffer); - static NAN_METHOD(CreateAnswer); - static NAN_METHOD(SetLocalDescription); - static NAN_METHOD(SetRemoteDescription); - static NAN_METHOD(UpdateIce); - static NAN_METHOD(AddIceCandidate); - static NAN_METHOD(CreateDataChannel); - /* - static NAN_METHOD(GetLocalStreams); - static NAN_METHOD(GetRemoteStreams); - static NAN_METHOD(GetStreamById); - static NAN_METHOD(AddStream); - static NAN_METHOD(RemoveStream); - */ - static NAN_METHOD(GetStats); - static NAN_METHOD(Close); - static NAN_METHOD(GetSenders); - static NAN_METHOD(GetReceivers); - - static NAN_GETTER(GetLocalDescription); - static NAN_GETTER(GetRemoteDescription); - static NAN_GETTER(GetIceConnectionState); - static NAN_GETTER(GetSignalingState); - static NAN_GETTER(GetIceGatheringState); - static NAN_SETTER(ReadOnly); - - void QueueEvent(AsyncEventType type, void* data); - - private: - static void Run(uv_async_t* handle, int status); - - struct AsyncEvent { - AsyncEventType type; - void* data; - }; - - uv_mutex_t lock; - uv_async_t async; - uv_loop_t* loop; - std::queue _events; - webrtc::PeerConnectionInterface::IceServers _iceServers; - - rtc::scoped_refptr _createOfferObserver; - rtc::scoped_refptr _createAnswerObserver; - rtc::scoped_refptr _setLocalDescriptionObserver; - rtc::scoped_refptr _setRemoteDescriptionObserver; - - // webrtc::AudioDeviceModule* _audioDeviceModule; - rtc::scoped_refptr _jinglePeerConnection; - - std::shared_ptr _factory; - bool _shouldReleaseFactory; -}; - -} // namespace node_webrtc - -#endif // SRC_PEERCONNECTION_H_ diff --git a/deps/exokit-bindings/webrtc/include/rtcstatsresponse.h b/deps/exokit-bindings/webrtc/include/rtcstatsresponse.h deleted file mode 100644 index b35f03c832..0000000000 --- a/deps/exokit-bindings/webrtc/include/rtcstatsresponse.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_RTCSTATSRESPONSE_H_ -#define SRC_RTCSTATSRESPONSE_H_ - -#include "nan.h" -#include "v8.h" // IWYU pragma: keep - -#include "webrtc/api/statstypes.h" - -namespace node_webrtc { - -class RTCStatsResponse - : public Nan::ObjectWrap { - public: - explicit RTCStatsResponse(webrtc::StatsReports reports): reports(reports) {} - ~RTCStatsResponse() {} - - // - // Nodejs wrapping. - // - static void Init(v8::Local exports); - static Nan::Persistent constructor; - static NAN_METHOD(New); - - static NAN_METHOD(result); - - private: - webrtc::StatsReports reports; -}; - -} // namespace node_webrtc - -#endif // SRC_RTCSTATSRESPONSE_H_ diff --git a/deps/exokit-bindings/webrtc/include/set-local-description-observer.h b/deps/exokit-bindings/webrtc/include/set-local-description-observer.h deleted file mode 100644 index 7a816f23ee..0000000000 --- a/deps/exokit-bindings/webrtc/include/set-local-description-observer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_SET_LOCAL_DESCRIPTION_OBSERVER_H_ -#define SRC_SET_LOCAL_DESCRIPTION_OBSERVER_H_ - -#include - -#include "webrtc/api/jsep.h" - -namespace node_webrtc { - -class PeerConnection; - -class SetLocalDescriptionObserver - : public webrtc::SetSessionDescriptionObserver { - private: - PeerConnection* parent; - - public: - explicit SetLocalDescriptionObserver(PeerConnection* connection): parent(connection) {} - - virtual void OnSuccess(); - virtual void OnFailure(const std::string& msg); -}; - -} // namespace node_webrtc - -#endif // SRC_SET_LOCAL_DESCRIPTION_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/include/set-remote-description-observer.h b/deps/exokit-bindings/webrtc/include/set-remote-description-observer.h deleted file mode 100644 index 147a7b4ea3..0000000000 --- a/deps/exokit-bindings/webrtc/include/set-remote-description-observer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_SET_REMOTE_DESCRIPTION_OBSERVER_H_ -#define SRC_SET_REMOTE_DESCRIPTION_OBSERVER_H_ - -#include - -#include "webrtc/api/jsep.h" - -namespace node_webrtc { - -class PeerConnection; - -class SetRemoteDescriptionObserver - : public webrtc::SetSessionDescriptionObserver { - private: - PeerConnection* parent; - - public: - explicit SetRemoteDescriptionObserver(PeerConnection* connection): parent(connection) {} - - virtual void OnSuccess(); - virtual void OnFailure(const std::string& msg); -}; - -} // namespace node_webrtc - -#endif // SRC_SET_REMOTE_DESCRIPTION_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/include/stats-observer.h b/deps/exokit-bindings/webrtc/include/stats-observer.h deleted file mode 100644 index bf8a67e9ad..0000000000 --- a/deps/exokit-bindings/webrtc/include/stats-observer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#ifndef SRC_STATS_OBSERVER_H_ -#define SRC_STATS_OBSERVER_H_ - -#include "nan.h" // IWYU pragma: keep - -#include "webrtc/api/peerconnectioninterface.h" -#include "webrtc/api/statstypes.h" - -namespace node_webrtc { - -class PeerConnection; - -class StatsObserver - : public webrtc::StatsObserver { - private: - PeerConnection* parent; - Nan::Callback* callback; - - public: - explicit StatsObserver(PeerConnection* parent, Nan::Callback* callback) - : parent(parent), callback(callback) {} - - virtual void OnComplete(const webrtc::StatsReports& reports); -}; - -} // namespace node_webrtc - -#endif // SRC_STATS_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/include/webrtc.h b/deps/exokit-bindings/webrtc/include/webrtc.h deleted file mode 100644 index 3242503c58..0000000000 --- a/deps/exokit-bindings/webrtc/include/webrtc.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "node.h" - -using namespace v8; - -namespace node_webrtc { - -void dispose(void*); -void init(v8::Local exports); - -} diff --git a/deps/exokit-bindings/webrtc/src/asyncobjectwrap.cc b/deps/exokit-bindings/webrtc/src/asyncobjectwrap.cc new file mode 100644 index 0000000000..8823ef6015 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/asyncobjectwrap.cc @@ -0,0 +1,72 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/asyncobjectwrap.h" + +#include + +using node_webrtc::AsyncObjectWrap; + +AsyncObjectWrap::AsyncObjectWrap(const char* name) + : _async_resource(new Nan::AsyncResource(name)) { + uv_mutex_init(&_async_resource_lock); +} + +AsyncObjectWrap::~AsyncObjectWrap() { + DestroyAsyncResource(); + uv_mutex_destroy(&_async_resource_lock); +} + +void AsyncObjectWrap::AddRef() { + if (_reference_count.fetch_add(1) == 0) { + Ref(); + } +} + +void AsyncObjectWrap::RemoveRef() { + if (_reference_count.fetch_sub(1) == 1) { + Unref(); + } +} + +v8::Local AsyncObjectWrap::ToObject() { + Nan::EscapableHandleScope scope; + return scope.Escape(handle()); +} + +void AsyncObjectWrap::Wrap(v8::Local object) { + Nan::ObjectWrap::Wrap(object); +} + +void AsyncObjectWrap::MakeCallback(const char* name, const int argc, v8::Local* argv) { + uv_mutex_lock(&_async_resource_lock); + if (_async_resource) { + _async_resource->runInAsyncScope(ToObject(), name, argc, argv); + } + uv_mutex_unlock(&_async_resource_lock); +} + +void AsyncObjectWrap::DestroyAsyncResource() { + uv_mutex_lock(&_async_resource_lock); + if (_async_resource) { +#if NODE_MAJOR_VERSION >= 9 + if (!Nan::GetCurrentContext().IsEmpty()) { + delete _async_resource; + } else { + // HACK(mroberts): This is probably unsafe, but sometimes the current Context is null... + auto context = v8::Isolate::GetCurrent()->GetEnteredContext(); + context->Enter(); + delete _async_resource; + context->Exit(); + } +#else + delete _async_resource; +#endif + _async_resource = nullptr; + } + uv_mutex_unlock(&_async_resource_lock); +} diff --git a/deps/exokit-bindings/webrtc/src/asyncobjectwrap.h b/deps/exokit-bindings/webrtc/src/asyncobjectwrap.h new file mode 100644 index 0000000000..bf42541e5e --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/asyncobjectwrap.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_ASYNCOBJECTWRAP_H_ +#define SRC_ASYNCOBJECTWRAP_H_ + +#include + +#include +#include // IWYU pragma: keep +#include + +// IWYU pragma: no_include + +namespace node_webrtc { + +/** + * AsyncObjectWrap combines AsyncResource and ObjectWrap. You cannot subclass both AsyncResource and ObjectWrap, as this + * will cause issues when the AsyncResource emits its "destroy" event. So, instead, AsyncObjectWrap subclasses + * ObjectWrap and contains an AsyncResource member. AsyncObjectWraps support reference counting. + */ +class AsyncObjectWrap: private Nan::ObjectWrap { + public: + /** + * Construct an AsyncObjectWrap. The name you provide is the name of the AsyncResource. + * @param name the name of the AsyncResource + */ + explicit AsyncObjectWrap(const char* name); + + ~AsyncObjectWrap() override; + + /** + * Increment the reference count. + */ + void AddRef(); + + /** + * Decrement the reference count. + */ + void RemoveRef(); + + /** + * Convert the AsyncObjectWrap to an Object. + * @return object the Object + */ + v8::Local ToObject(); + + /** + * Convert an Object to an AsyncObjectWrap. + * @tparam T the AsyncObjectWrap type + * @param object the Object to convert + * @return asyncObjectWrap an AsyncObjectWrap + */ + template + static T* Unwrap(v8::Local object) { + auto unwrapped = Nan::ObjectWrap::Unwrap(object); + return reinterpret_cast(unwrapped); + } + + /** + * Wrap an Object. + * @param object the Object to wrap + */ + void Wrap(v8::Local object); + + protected: + /** + * Make a callback. + * @param name the name of the callback + * @param argc the number of arguments + * @param argv the arguments + */ + void MakeCallback(const char* name, int argc, v8::Local* argv); + + private: + Nan::AsyncResource* _async_resource; + uv_mutex_t _async_resource_lock; + std::atomic_int _reference_count = {0}; + + /** + * Destroy the AsyncResource. + */ + void DestroyAsyncResource(); +}; + +} // namespace node_webrtc + +#endif // SRC_ASYNCOBJECTWRAP_H_ diff --git a/deps/exokit-bindings/webrtc/src/asyncobjectwrapwithloop.h b/deps/exokit-bindings/webrtc/src/asyncobjectwrapwithloop.h new file mode 100644 index 0000000000..581e75a51b --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/asyncobjectwrapwithloop.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_ASYNCOBJECTWRAPWITHLOOP_H_ +#define SRC_ASYNCOBJECTWRAPWITHLOOP_H_ + +#include + +#include +#include + +#include "src/asyncobjectwrap.h" +#include "src/promisefulfillingeventloop.h" + +namespace node_webrtc { + +/** + * AsyncObjectWrapWithLoop combines AsyncObjectWrap and PromiseFulfillingEventLoop. Since the class instance is + * associated with an event loop, we should AddRef when we Wrap it, and RemoveRef when the event loop closes. + * @tparam T the Event target type + */ +template +class AsyncObjectWrapWithLoop + : private AsyncObjectWrap + , private PromiseFulfillingEventLoop { + public: + /** + * Construct an AsyncObjectWrapWithLoop. The name you provide is the name of the AsyncResource, and the target you + * provide is the Event target. + * @param name the name of the AsyncResource + * @param target the Event target + */ + AsyncObjectWrapWithLoop(const char* name, T& target): AsyncObjectWrap(name), PromiseFulfillingEventLoop(target) {} + + ~AsyncObjectWrapWithLoop() override = default; + + /** + * Dispatch an event to the AsyncObjectWrapWithLoop. + * @param event the event to dispatch + */ + void Dispatch(std::unique_ptr> event) { + PromiseFulfillingEventLoop::Dispatch(std::move(event)); + } + + /** + * Convert the AsyncObjectWrapWithLoop to an Object. + * @return object the Object + */ + v8::Local ToObject() { + Nan::EscapableHandleScope scope; + return scope.Escape(AsyncObjectWrap::ToObject()); + } + + /** + * Convert an Object to an AsyncObjectWrapWithLoop. + * @tparam T the AsyncObjectWrapWithLoop type + * @param object the Object to convert + * @return asyncObjectWrapWithLoop an AsyncObjectWrapWithLoop + */ + inline static T* Unwrap(v8::Local object) { + return AsyncObjectWrap::Unwrap(object); + } + + /** + * Wrap an Object. This also calls AddRef once; therefore, Wrap initializes the reference count to one. + * @param object the Object to wrap + */ + void Wrap(v8::Local object) { + AsyncObjectWrap::Wrap(object); + AsyncObjectWrap::AddRef(); + } + + protected: + /** + * Invoked when the event loop closes. This calls RemoveRef. + */ + void DidStop() override { + AsyncObjectWrap::RemoveRef(); + } + + /** + * Make a callback. + * @param name the name of the callback + * @param argc the number of arguments + * @param argv the arguments + */ + void MakeCallback(const char* name, const int argc, v8::Local* argv) { + AsyncObjectWrap::MakeCallback(name, argc, argv); + } + + /** + * Stop the event loop. + */ + void Stop() override { + PromiseFulfillingEventLoop::Stop(); + } +}; + +} // namespace node_webrtc + +#endif // SRC_ASYNCOBJECTWRAPWITHLOOP_H_ diff --git a/deps/exokit-bindings/webrtc/src/bidimap.h b/deps/exokit-bindings/webrtc/src/bidimap.h new file mode 100644 index 0000000000..0c7bcc7bf2 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/bidimap.h @@ -0,0 +1,202 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_BIDIMAP_H_ +#define SRC_BIDIMAP_H_ + +#include +#include +#include + +#include "src/functional/maybe.h" +#include "src/functional/operators.h" + +namespace node_webrtc { + +/** + * A BidiMap is a "bidirectional map" supporting get and set operations on both + * keys and values. + * @tparam K the type of keys + * @tparam V the type of values + */ +template +class BidiMap { + public: + /** + * Construct an empty BidiMap. + */ + BidiMap() = default; + + /** + * Remove all keys and values from the BidiMap. + */ + void clear() { + _keyToValue.clear(); + _valueToKey.clear(); + } + + /** + * Compute, set, and return a key's value if it's absent; otherwise, return + * the key's value. + * @param key + * @param computeValue + * @return the existing or newly set value + */ + V computeIfAbsent(K key, std::function computeValue) { + return get(key).Or([this, key, computeValue]() { + auto value = computeValue(); + this->set(key, value); + return value; + }); + } + + /** + * Get the key's value from the BidiMap. + * @param key + * @return Nothing if the key was not present + */ + Maybe get(K key) const { + return has(key) + ? Maybe::Just(_keyToValue.at(key)) + : Maybe::Nothing(); + } + + /** + * Check if the BidiMap contains a value for the key. + * @param key + * @return true if the BidiMap contains a value for the key + */ + bool has(K key) const { + return _keyToValue.count(key) > 0; + } + + /** + * Remove the key and its value from the BidiMap. + * @param key + * @return Nothing if the key was not present + */ + Maybe remove(K key) { + return [this, key](V value) { + this->_keyToValue.erase(key); + this->_valueToKey.erase(value); + return value; + } % get(key); + } + + /** + * Return a BidiMap with its keys and values swapped. + * @return a BidiMap with its keys and values swapped + */ + BidiMap reverse() const { + return BidiMap(_valueToKey, _keyToValue); + } + + /** + * Compute, set, and return a value's key if it's absent; otherwise, return + * the value's key. + * @param value + * @param computeKey + * @return the existing or newly set key + */ + K reverseComputeIfAbsent(V value, std::function computeKey) { + return reverseGet(value).Or([this, value, computeKey]() { + auto key = computeKey(); + this->reverseSet(value, key); + return key; + }); + } + + /** + * Get the value's key from the BidiMap. + * @param value + * @return Nothing if the value was not present + */ + Maybe reverseGet(V value) const { + return reverseHas(value) + ? Maybe::Just(_valueToKey.at(value)) + : Maybe::Nothing(); + } + + /** + * Check if the BidiMap contains a key for the value. + * @param value + * @return true if the BidiMap contains a key for the value + */ + bool reverseHas(V value) const { + return _valueToKey.count(value) > 0; + } + + /** + * Remove a value and its key from the BidiMap. + * @param value + * @return Nothing if the value was not present + */ + Maybe reverseRemove(V value) { + return [this, value](K key) { + this->_keyToValue.erase(key); + this->_valueToKey.erase(value); + return key; + } % reverseGet(value); + } + + /** + * Set a value and its key in the BidiMap. + * @param value + * @param key + * @return a pair of the previously set key (if any) and the previously set + * value (if any) + */ + std::pair, Maybe> reverseSet(V value, K key) { + auto pair = std::make_pair(reverseGet(value), get(key)); + remove(key); + _valueToKey[value] = key; + _keyToValue[key] = value; + return pair; + } + + /** + * Set a key and its value in the BidiMap. + * @param key + * @param value + * @return a pair of the previously set value (if any) and the previously set + * key (if any) + */ + std::pair, Maybe> set(K key, V value) { + auto pair = std::make_pair(get(key), reverseGet(value)); + reverseRemove(value); + _keyToValue[key] = value; + _valueToKey[value] = key; + return pair; + } + + /** + * Construct a BidiMap from a map. + * @param map + * @return Nothing if the map contains duplicate values + */ + static Maybe> FromMap(std::map map) { + BidiMap bidiMap; + for (auto pair : map) { + auto previousKey = bidiMap.reverseSet(pair.second, pair.first); + if (previousKey.IsJust()) { + return Maybe>::Nothing(); + } + } + return Maybe>::Just(bidiMap); + } + + private: + BidiMap(const std::map& keyToValue, const std::map& valueToKey) + : _keyToValue(keyToValue), _valueToKey(valueToKey) {} + + std::map _keyToValue; + std::map _valueToKey; +}; + +} // namespace node_webrtc + +#endif // SRC_BIDIMAP_H_ diff --git a/deps/exokit-bindings/webrtc/src/binding.cc b/deps/exokit-bindings/webrtc/src/binding.cc index be05d15fd5..11327a7ed8 100644 --- a/deps/exokit-bindings/webrtc/src/binding.cc +++ b/deps/exokit-bindings/webrtc/src/binding.cc @@ -1,34 +1,54 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. * * Use of this source code is governed by a BSD-style license that can be found * in the LICENSE.md file in the root of the source tree. All contributing * project authors may be found in the AUTHORS file in the root of the source * tree. */ -#include "node.h" +#include +#include // IWYU pragma: keep -#include "webrtc.h" -#include "datachannel.h" -#include "rtcstatsreport.h" -#include "rtcstatsresponse.h" -#include "peerconnection.h" -#include "peerconnectionfactory.h" +#include "src/datachannel.h" // IWYU pragma: keep +#include "src/errorfactory.h" // IWYU pragma: keep +#include "src/getusermedia.h" // IWYU pragma: keep +#include "src/i420helpers.h" // IWYU pragma: keep +#include "src/legacyrtcstatsreport.h" // IWYU pragma: keep +#include "src/mediastream.h" // IWYU pragma: keep +#include "src/mediastreamtrack.h" // IWYU pragma: keep +#include "src/peerconnection.h" // IWYU pragma: keep +#include "src/peerconnectionfactory.h" +#include "src/rtcrtpreceiver.h" // IWYU pragma: keep +#include "src/rtcrtpsender.h" // IWYU pragma: keep +#include "src/rtcrtptransceiver.h" // IWYU pragma: keep +#include "src/rtcstatsresponse.h" // IWYU pragma: keep +#include "src/rtcvideosink.h" // IWYU pragma: keep +#include "src/rtcvideosource.h" // IWYU pragma: keep -using namespace v8; +/* static void dispose(void*) { + node_webrtc::PeerConnectionFactory::Dispose(); +} */ namespace node_webrtc { -void dispose(void*) { - node_webrtc::PeerConnectionFactory::Dispose(); -} - -void init(v8::Local exports) { +void init(v8::Local exports, v8::Local module) { + node_webrtc::ErrorFactory::Init(module); + node_webrtc::GetUserMedia::Init(exports); + node_webrtc::I420Helpers::Init(exports); node_webrtc::PeerConnectionFactory::Init(exports); node_webrtc::PeerConnection::Init(exports); node_webrtc::DataChannel::Init(exports); - node_webrtc::RTCStatsReport::Init(exports); + node_webrtc::MediaStream::Init(exports); + node_webrtc::MediaStreamTrack::Init(exports); + node_webrtc::RTCRtpReceiver::Init(exports); + node_webrtc::RTCRtpSender::Init(exports); + node_webrtc::RTCRtpTransceiver::Init(exports); + node_webrtc::LegacyStatsReport::Init(exports); node_webrtc::RTCStatsResponse::Init(exports); - node::AtExit(dispose); + node_webrtc::RTCVideoSink::Init(exports); + node_webrtc::RTCVideoSource::Init(exports); + // node::AtExit(dispose); } } + +// NODE_MODULE(wrtc, init) diff --git a/deps/exokit-bindings/webrtc/include/common.h b/deps/exokit-bindings/webrtc/src/common.h similarity index 50% rename from deps/exokit-bindings/webrtc/include/common.h rename to deps/exokit-bindings/webrtc/src/common.h index 7b0f8ccd75..34fa0e535d 100644 --- a/deps/exokit-bindings/webrtc/include/common.h +++ b/deps/exokit-bindings/webrtc/src/common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. * * Use of this source code is governed by a BSD-style license that can be found * in the LICENSE.md file in the root of the source tree. All contributing @@ -9,12 +9,10 @@ #define SRC_COMMON_H_ #include -#include +#include #include "nan.h" -#include - #define WARN(msg) fprintf(stdout, "\033[01;33m native:%s \033[00m\n", msg) #define ERROR(msg) fprintf(stdout, "\033[01;32m native:%s \033[00m\n", msg) #define INFO(msg) fprintf(stdout, "\033[01;34m native:%s \033[00m\n", msg) @@ -47,41 +45,4 @@ #endif -#define THROW_TYPE_ERROR(MSG) \ - return Nan::ThrowTypeError(MSG); - -#define CHECK_ARG(I, CHECK, DO_TRUE, DO_FALSE) \ - if (info.Length() <= (I) || !info[I]->CHECK) { DO_FALSE; } else { DO_TRUE; } - -#define REQUIRE_ARG(I, CHECK) \ - CHECK_ARG(I, CHECK, , THROW_TYPE_ERROR("Argument " #I " must be an object")) - -#define REQ_OBJ_ARG(I, VAR) \ - REQUIRE_ARG(I, IsObject()) \ - Local VAR = Local::Cast(info[I]) - -#define OPT_INT_ARG(I, VAR, DEFAULT) \ - int VAR; \ - CHECK_ARG(I, IsNumber(), VAR = info[I]->Int32Value(), VAR = DEFAULT) - -#define REQ_INT_ARG(I, VAR) \ - REQUIRE_ARG(I, IsNumber()) \ - int VAR = info[I]->Int32Value(); - -#define REQ_FUN_ARG(I, VAR) \ - if (info.Length() <= (I) || !info[I]->IsFunction()) \ - return Nan::ThrowTypeError("Argument " #I " must be a function"); \ - Local VAR = Local::Cast(info[I]); - -#define CREATE_BUFFER(name, data, length) \ - Local name ## _buf = Nan::NewBuffer(length).ToLocalChecked(); \ - memcpy(Buffer::Data(name ## _buf), data, length); \ - Local name; \ - Handle ctorArgs[3] = { name ## _buf, Nan::New(length), Nan::New(0) }; \ - name = Local::Cast(\ - Nan::GetCurrentContext() \ - ->Global() \ - ->Get(Nan::New("Buffer").ToLocalChecked()) \ - )->NewInstance(3, ctorArgs); - #endif // SRC_COMMON_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters.h b/deps/exokit-bindings/webrtc/src/converters.h new file mode 100644 index 0000000000..0ab2822636 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines conversion functions and short-hand for converting between + * different types. Conversions can fail, hence we use Validation. + */ + +#ifndef SRC_CONVERTERS_H_ +#define SRC_CONVERTERS_H_ + +#include "src/functional/curry.h" +#include "src/functional/either.h" +#include "src/functional/maybe.h" +#include "src/functional/operators.h" +#include "src/functional/validation.h" + +/** + * This macro declares a node_webrtc::Converter from I to O. + * + * @param I the input type + * @param O the output type + */ +#define DECLARE_CONVERTER(I, O) \ + template <> \ + struct Converter { \ + static Validation Convert(I); \ + }; + +/** + * This macro declares a node_webrtc::Converter from T to v8::Local. + * + * @param T the input type + */ +#define DECLARE_TO_JS(T) DECLARE_CONVERTER(T, v8::Local) + +/** + * This macro declares a node_webrtc::Converter from v8::Local to T. + * + * @param T the output type + */ +#define DECLARE_FROM_JS(T) DECLARE_CONVERTER(v8::Local, T) + +/** + * This macro declares node_webrtc::Converter instances between T and v8::Local. + * + * @param T the type to convert + */ +#define DECLARE_TO_AND_FROM_JS(T) \ + DECLARE_TO_JS(T) \ + DECLARE_FROM_JS(T) + +/** + * This macro simplifies defining a node_webrtc::Converter from I to O. + * + * @param I the input type + * @param O the output type + * @param V the name of the input variable to convert + */ +#define CONVERTER_IMPL(I, O, V) node_webrtc::Validation node_webrtc::Converter::Convert(I V) + +/** + * This macro simplifies defining a node_webrtc::Converter from T to v8::Local. + * + * @param T the input type + * @param V the name of the input variable to convert + */ +#define TO_JS_IMPL(T, V) CONVERTER_IMPL(T, v8::Local, V) + +/** + * This macro simplifies defining a node_webrtc::Converter from v8::Local to T. + * + * @param T the output type + * @param V the name of the input variable to convert + */ +#define FROM_JS_IMPL(T, V) CONVERTER_IMPL(v8::Local, T, V) + +/** + * This macro defines a node_webrtc::Converter from I to O when node_webrtc::Converter instances from + * + * 1. I to M, and + * 2. M to O + * + * exist. + * + * @param I the input type + * @param M the intermediate type + * @param O the output type + */ +#define CONVERT_VIA(I, M, O) \ + CONVERTER_IMPL(I, O, value) { \ + return node_webrtc::Converter::Convert(value).FlatMap(node_webrtc::Converter::Convert); \ + } +// TODO(mroberts): This stuff is not so general as to warrant inclusion in converters.h. + + +namespace node_webrtc { + +/** + * A Converter converts values from some "source" type S to values of some + * "target" type T. + * @tparam S the source type + * @tparam T the target type + */ +template +struct Converter {}; + +/** + * From is short-hand for invoking a particular Converter. + * @tparam T the target type + * @tparam S the source type + * @param s the source value + * @return the target value + */ +template +static Validation From(const S s) { + return Converter::Convert(s); +} + +/** + * There is an "identity" Converter between values of the same type T. + * @tparam T the source and target type + */ +template +struct Converter { + static Validation Convert(const T t) { + return Validation(t); + } +}; + +/** + * There is a Converter that tries first one conversion, then another. + * @tparam S the source type + * @tparam L a target type L + * @tparam R a target type R + */ +template +struct Converter> { + static Validation> Convert(const S s) { + return From(s).Map(&MakeLeft) + | (From(s).Map(&MakeRight)); + } +}; + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/arguments.h b/deps/exokit-bindings/webrtc/src/converters/arguments.h new file mode 100644 index 0000000000..9524c99b26 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/arguments.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines functions for decomposing method arguments. + */ + +#ifndef SRC_CONVERTERS_ARGUMENTS_H_ +#define SRC_CONVERTERS_ARGUMENTS_H_ + +#include + +#include "nan.h" + +#include "src/converters.h" +#include "src/functional/curry.h" +#include "src/functional/validation.h" + +namespace node_webrtc { + +struct Arguments { + Nan::NAN_METHOD_ARGS_TYPE info; + explicit Arguments(Nan::NAN_METHOD_ARGS_TYPE info): info(info) {} +}; + +template +struct Converter { + static Validation Convert(Arguments args) { + return From(args.info[0]); + } +}; + +template +struct Converter> { + static Validation> Convert(Arguments args) { + return From(args).Map(&Either::Left) + | (From(args).Map(&Either::Right)); + } +}; + +template +static std::tuple Make2Tuple(A a, B b) { + return std::make_tuple(a, b); +} + +template +struct Converter> { + static Validation> Convert(Arguments args) { + return curry(Make2Tuple) + % From(args.info[0]) + * From(args.info[1]); + } +}; + +template +static std::tuple Make3Tuple(A a, B b, C c) { + return std::make_tuple(a, b, c); +} + +template +struct Converter> { + static Validation> Convert(Arguments args) { + return curry(Make3Tuple) + % From(args.info[0]) + * From(args.info[1]) + * From(args.info[2]); + } +}; + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_ARGUMENTS_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/dictionaries.cc b/deps/exokit-bindings/webrtc/src/converters/dictionaries.cc new file mode 100644 index 0000000000..747612a1e8 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/dictionaries.cc @@ -0,0 +1,763 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/converters/dictionaries.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include + +#include "src/asyncobjectwrapwithloop.h" // IWYU pragma: keep +#include "src/converters.h" +#include "src/converters/interfaces.h" // IWYU pragma: keep +#include "src/converters/object.h" // IWYU pragma: keep +#include "src/converters/v8-converters.h" // IWYU pragma: keep +#include "src/functional/either.h" // IWYU pragma: keep +#include "src/i420helpers.h" // IWYU pragma: keep +#include "src/errorfactory.h" // IWYU pragma: keep +#include "src/mediastream.h" // IWYU pragma: keep +#include "src/mediastreamtrack.h" // IWYU pragma: keep +#include "src/rtcrtpreceiver.h" // IWYU pragma: keep +#include "src/rtcrtpsender.h" // IWYU pragma: keep +#include "src/rtcrtptransceiver.h" // IWYU pragma: keep +#include "src/rtcstatsresponse.h" // IWYU pragma: keep + +using node_webrtc::BinaryType; // * +using node_webrtc::RTCAnswerOptions; // * +using node_webrtc::RTCDtlsFingerprint; // * +using node_webrtc::RTCIceCredentialType; // * +using node_webrtc::RTCOAuthCredential; // * +using node_webrtc::RTCOfferOptions; // * +using node_webrtc::RTCPeerConnectionState; // * +using node_webrtc::RTCPriorityType; // * +using node_webrtc::RTCSdpType; // * +using node_webrtc::RTCSessionDescriptionInit; // * +using node_webrtc::RTCVideoSourceInit; // * +using node_webrtc::UnsignedShortRange; // * + +typedef node_webrtc::Either, std::string> stringOrStrings; +typedef node_webrtc::Either stringOrCredential; + +#define EXPAND_OBJ_FROM_JS_DEFAULT(TYPE, NAME, DEFAULT) * GetOptional(object, NAME, DEFAULT) + +#define EXPAND_OBJ_FROM_JS_OPTIONAL(TYPE, NAME) * GetOptional(object, NAME) + +#define EXPAND_OBJ_FROM_JS_REQUIRED(TYPE, NAME) * GetRequired(object, NAME) + +// TODO(mroberts): Use CONVERT_VIA and go through v8::Local? +// TODO(mroberts): Explain when to use _IMPL1 versus _IMPL2. +// TODO(mroberts): Correct usage of `T` versus `TYPE`, etc., in comments. + +#define OBJ_FROM_JS_IMPL1(TYPE, FN) \ + FROM_JS_IMPL(TYPE, value) { \ + return node_webrtc::From>(value).FlatMap( \ + [](const v8::Local object) { \ + return node_webrtc::Pure(curry(FN)) \ + TYPE ## _LIST \ + ; \ + }); \ + } + +#define OBJ_FROM_JS_IMPL2(TYPE, FN) \ + FROM_JS_IMPL(TYPE, value) { \ + return node_webrtc::From>(value).FlatMap( \ + [](const v8::Local object) { \ + return node_webrtc::Validation::Join( \ + node_webrtc::Pure(curry(FN)) \ + TYPE ## _LIST \ + ); \ + }); \ + } + +static node_webrtc::RTCOAuthCredential CreateRTCOAuthCredential( + const std::string& macKey, + const std::string& accessToken) { + return {macKey, accessToken}; +} + +static node_webrtc::Validation CreateIceServer( + const node_webrtc::Either, std::string>& urlsOrUrl, + const std::string& username, + const node_webrtc::Either& credential, + const node_webrtc::RTCIceCredentialType credentialType) { + if (credential.IsRight() || credentialType != node_webrtc::RTCIceCredentialType::kPassword) { + return node_webrtc::Validation::Invalid("OAuth is not currently supported"); + } + webrtc::PeerConnectionInterface::IceServer iceServer; + iceServer.urls = urlsOrUrl.FromLeft(std::vector()); + iceServer.uri = urlsOrUrl.FromRight(""); + iceServer.username = username; + iceServer.password = credential.UnsafeFromLeft(); + return node_webrtc::Pure(iceServer); +} + +TO_JS_IMPL(webrtc::PeerConnectionInterface::IceServer, iceServer) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + if (!iceServer.uri.empty()) { + object->Set(Nan::New("urls").ToLocalChecked(), Nan::New(iceServer.uri).ToLocalChecked()); + } else { + auto maybeArray = node_webrtc::From>(iceServer.urls); + if (maybeArray.IsInvalid()) { + return node_webrtc::Validation>::Invalid(maybeArray.ToErrors()); + } + object->Set(Nan::New("urls").ToLocalChecked(), maybeArray.UnsafeFromValid()); + } + if (!iceServer.username.empty()) { + object->Set(Nan::New("username").ToLocalChecked(), Nan::New(iceServer.username).ToLocalChecked()); + } + if (!iceServer.password.empty()) { + object->Set(Nan::New("credential").ToLocalChecked(), Nan::New(iceServer.password).ToLocalChecked()); + object->Set(Nan::New("credentialType").ToLocalChecked(), Nan::New("password").ToLocalChecked()); + } + return node_webrtc::Pure(scope.Escape(object.As())); +} + +static node_webrtc::RTCDtlsFingerprint CreateRTCDtlsFingerprint( + const node_webrtc::Maybe& algorithm, + const node_webrtc::Maybe& value) { + return {algorithm, value}; +} + +static node_webrtc::Validation CreateUnsignedShortRange( + const node_webrtc::Maybe& maybeMin, + const node_webrtc::Maybe& maybeMax) { + auto min = maybeMin.FromMaybe(0); + auto max = maybeMax.FromMaybe(65535); + if (min > max) { + return node_webrtc::Validation::Invalid("Expected min to be less than max"); + } + return node_webrtc::Pure({maybeMin, maybeMax}); +} + +TO_JS_IMPL(node_webrtc::UnsignedShortRange, value) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + if (value.min.IsJust()) { + object->Set(Nan::New("min").ToLocalChecked(), Nan::New(value.min.UnsafeFromJust())); + } + if (value.max.IsJust()) { + object->Set(Nan::New("max").ToLocalChecked(), Nan::New(value.max.UnsafeFromJust())); + } + return node_webrtc::Pure(scope.Escape(object.As())); +} + +static webrtc::PeerConnectionInterface::RTCConfiguration CreateRTCConfiguration( + const std::vector& iceServers, + const webrtc::PeerConnectionInterface::IceTransportsType iceTransportsPolicy, + const webrtc::PeerConnectionInterface::BundlePolicy bundlePolicy, + const webrtc::PeerConnectionInterface::RtcpMuxPolicy rtcpMuxPolicy, + const node_webrtc::Maybe&, + const node_webrtc::Maybe>>&, + const uint32_t iceCandidatePoolSize, + const webrtc::SdpSemantics sdpSemantics) { + webrtc::PeerConnectionInterface::RTCConfiguration configuration; + configuration.servers = iceServers; + configuration.type = iceTransportsPolicy; + configuration.bundle_policy = bundlePolicy; + configuration.rtcp_mux_policy = rtcpMuxPolicy; + configuration.ice_candidate_pool_size = iceCandidatePoolSize; + configuration.sdp_semantics = sdpSemantics; + return configuration; +} + +FROM_JS_IMPL(webrtc::PeerConnectionInterface::RTCConfiguration, value) { + // NOTE(mroberts): Allow overriding the default SdpSemantics via environment variable. + // Makes web-platform-tests easier to run. + auto sdp_semantics_env = std::getenv("SDP_SEMANTICS"); + auto sdp_semantics_str = sdp_semantics_env ? std::string(sdp_semantics_env) : std::string(); + auto sdp_semantics = node_webrtc::From(sdp_semantics_str).FromValidation(webrtc::SdpSemantics::kPlanB); + return node_webrtc::From>(value).FlatMap( + [sdp_semantics](const v8::Local object) { + return curry(CreateRTCConfiguration) + % node_webrtc::GetOptional>(object, "iceServers", std::vector()) + * node_webrtc::GetOptional(object, "iceTransportPolicy", webrtc::PeerConnectionInterface::IceTransportsType::kAll) + * node_webrtc::GetOptional(object, "bundlePolicy", webrtc::PeerConnectionInterface::BundlePolicy::kBundlePolicyBalanced) + * node_webrtc::GetOptional(object, "rtcpMuxPolicy", webrtc::PeerConnectionInterface::RtcpMuxPolicy::kRtcpMuxPolicyRequire) + * node_webrtc::GetOptional(object, "peerIdentity") + * node_webrtc::GetOptional>>(object, "certificates") + // TODO(mroberts): Implement EnforceRange and change to uint8_t. + * node_webrtc::GetOptional(object, "iceCandidatePoolSize", 0) + * node_webrtc::GetOptional(object, "sdpSemantics", sdp_semantics); + }); +} + +static node_webrtc::ExtendedRTCConfiguration CreateExtendedRTCConfiguration( + const webrtc::PeerConnectionInterface::RTCConfiguration configuration, + const node_webrtc::UnsignedShortRange portRange) { + return node_webrtc::ExtendedRTCConfiguration(configuration, portRange); +} + +FROM_JS_IMPL(node_webrtc::ExtendedRTCConfiguration, value) { + return node_webrtc::From>(value).FlatMap( + [](const v8::Local object) { + return curry(CreateExtendedRTCConfiguration) + % node_webrtc::From(static_cast>(object)) + * node_webrtc::GetOptional(object, "portRange", node_webrtc::UnsignedShortRange()); + }); +} + +static v8::Local ExtendedRTCConfigurationToJavaScript( + const v8::Local iceServers, + const v8::Local iceTransportPolicy, + const v8::Local bundlePolicy, + const v8::Local rtcpMuxPolicy, + const v8::Local iceCandidatePoolSize, + const v8::Local portRange, + const v8::Local sdpSemantics) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + object->Set(Nan::New("iceServers").ToLocalChecked(), iceServers); + object->Set(Nan::New("iceTransportPolicy").ToLocalChecked(), iceTransportPolicy); + object->Set(Nan::New("bundlePolicy").ToLocalChecked(), bundlePolicy); + object->Set(Nan::New("rtcpMuxPolicy").ToLocalChecked(), rtcpMuxPolicy); + object->Set(Nan::New("iceCandidatePoolSize").ToLocalChecked(), iceCandidatePoolSize); + object->Set(Nan::New("portRange").ToLocalChecked(), portRange); + object->Set(Nan::New("sdpSemantics").ToLocalChecked(), sdpSemantics); + return scope.Escape(object); +} + +TO_JS_IMPL(node_webrtc::ExtendedRTCConfiguration, configuration) { + return curry(ExtendedRTCConfigurationToJavaScript) + % node_webrtc::From>(configuration.configuration.servers) + * node_webrtc::From>(configuration.configuration.type) + * node_webrtc::From>(configuration.configuration.bundle_policy) + * node_webrtc::From>(configuration.configuration.rtcp_mux_policy) + * node_webrtc::Pure(Nan::New(configuration.configuration.ice_candidate_pool_size)) + * node_webrtc::From>(configuration.portRange) + * node_webrtc::From>(configuration.configuration.sdp_semantics); +} + +static node_webrtc::RTCOfferOptions CreateRTCOfferOptions( + const bool voiceActivityDetection, + const bool iceRestart, + const node_webrtc::Maybe offerToReceiveAudio, + const node_webrtc::Maybe offerToReceiveVideo) { + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + options.ice_restart = iceRestart; + options.voice_activity_detection = voiceActivityDetection; + options.offer_to_receive_audio = offerToReceiveAudio.Map( + [](const bool boolean) { return boolean ? webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0; } + ).FromMaybe(webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined); + options.offer_to_receive_video = offerToReceiveVideo.Map( + [](const bool boolean) { return boolean ? webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0; } + ).FromMaybe(webrtc::PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined); + return node_webrtc::RTCOfferOptions(options); +} + +static node_webrtc::RTCAnswerOptions CreateRTCAnswerOptions(const bool voiceActivityDetection) { + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + options.voice_activity_detection = voiceActivityDetection; + return node_webrtc::RTCAnswerOptions(options); +} + +static node_webrtc::RTCSessionDescriptionInit CreateRTCSessionDescriptionInit( + const node_webrtc::RTCSdpType type, + const std::string sdp) { + return {type, sdp}; +} + +static node_webrtc::RTCVideoSourceInit CreateRTCVideoSourceInit( + const bool isScreencast, + const node_webrtc::Maybe needsDenoising +) { + return {isScreencast, needsDenoising}; +} + +node_webrtc::Validation node_webrtc::Converter::Convert( + const node_webrtc::RTCSessionDescriptionInit init) { + std::string type_; + switch (init.type) { + case node_webrtc::RTCSdpType::kOffer: + type_ = "offer"; + break; + case node_webrtc::RTCSdpType::kPrAnswer: + type_ = "pranswer"; + break; + case node_webrtc::RTCSdpType::kAnswer: + type_ = "answer"; + break; + case node_webrtc::RTCSdpType::kRollback: + return node_webrtc::Validation::Invalid("Rollback is not currently supported"); + } + webrtc::SdpParseError error; + auto description = webrtc::CreateSessionDescription(type_, init.sdp, &error); + if (!description) { + return node_webrtc::Validation::Invalid(error.description); + } + return node_webrtc::Pure(description); +} + +TO_JS_IMPL(node_webrtc::RTCSessionDescriptionInit, init) { + Nan::EscapableHandleScope scope; + auto maybeType = node_webrtc::From(init.type); + if (maybeType.IsInvalid()) { + return node_webrtc::Validation>::Invalid(maybeType.ToErrors()[0]); + } + auto object = Nan::New(); + object->Set(Nan::New("sdp").ToLocalChecked(), Nan::New(init.sdp).ToLocalChecked()); + object->Set(Nan::New("type").ToLocalChecked(), Nan::New(maybeType.UnsafeFromValid()).ToLocalChecked()); + return node_webrtc::Pure(scope.Escape(object.As())); +} + +node_webrtc::Validation node_webrtc::Converter::Convert( + webrtc::SessionDescriptionInterface* description) { + if (!description) { + return node_webrtc::Validation::Invalid("RTCSessionDescription is null"); + } + std::string sdp; + if (!description->ToString(&sdp)) { + return node_webrtc::Validation::Invalid("Failed to print the SDP. This is pretty weird. File a bug on https://github.com/js-platform/node-webrtc"); + } + return curry(CreateRTCSessionDescriptionInit) + % node_webrtc::From(description->type()) + * node_webrtc::Validation(sdp); +} + +FROM_JS_IMPL(webrtc::SessionDescriptionInterface*, value) { + return node_webrtc::From(value) + .FlatMap(node_webrtc::Converter::Convert); +} + +TO_JS_IMPL(const webrtc::SessionDescriptionInterface*, value) { + Nan::EscapableHandleScope scope; + + if (!value) { + return node_webrtc::Validation>::Invalid("RTCSessionDescription is null"); + } + + std::string sdp; + if (!value->ToString(&sdp)) { + return node_webrtc::Validation>::Invalid("Failed to print the SDP. This is pretty weird. File a bug on https://github.com/js-platform/node-webrtc"); + } + + auto object = Nan::New(); + object->Set(Nan::New("sdp").ToLocalChecked(), Nan::New(sdp).ToLocalChecked()); + object->Set(Nan::New("type").ToLocalChecked(), Nan::New(value->type()).ToLocalChecked()); + + return node_webrtc::Pure(scope.Escape(object.As())); +} + +static node_webrtc::Validation CreateIceCandidateInterface( + const std::string& candidate, + const std::string& sdpMid, + const int sdpMLineIndex, + const node_webrtc::Maybe&) { + webrtc::SdpParseError error; + auto candidate_ = webrtc::CreateIceCandidate(sdpMid, sdpMLineIndex, candidate, &error); + if (!candidate_) { + return node_webrtc::Validation::Invalid(error.description); + } + return node_webrtc::Pure(candidate_); +} + +TO_JS_IMPL(const webrtc::IceCandidateInterface*, value) { + Nan::EscapableHandleScope scope; + + if (!value) { + return node_webrtc::Validation>::Invalid("RTCIceCandidate is null"); + } + + std::string candidate; + if (!value->ToString(&candidate)) { + return node_webrtc::Validation>::Invalid("Failed to print the candidate string. This is pretty weird. File a bug on https://github.com/js-platform/node-webrtc"); + } + + auto object = Nan::New(); + object->Set(Nan::New("candidate").ToLocalChecked(), Nan::New(candidate).ToLocalChecked()); + object->Set(Nan::New("sdpMid").ToLocalChecked(), Nan::New(value->sdp_mid()).ToLocalChecked()); + object->Set(Nan::New("sdpMLineIndex").ToLocalChecked(), Nan::New(value->sdp_mline_index())); + + return node_webrtc::Pure(scope.Escape(object.As())); +} + +static node_webrtc::Validation CreateDataChannelInit( + const bool ordered, + const node_webrtc::Maybe maxPacketLifeTime, + const node_webrtc::Maybe maxRetransmits, + const std::string& protocol, + const bool negotiated, + const node_webrtc::Maybe id, + const node_webrtc::RTCPriorityType) { + if (id.FromMaybe(0) > UINT16_MAX) { + return node_webrtc::Validation::Invalid("id must be between 0 and 65534, inclusive"); + } else if (maxPacketLifeTime.IsJust() && maxRetransmits.IsJust()) { + return node_webrtc::Validation::Invalid("You cannot set both maxPacketLifeTime and maxRetransmits"); + } + webrtc::DataChannelInit init; + init.ordered = ordered; + init.maxRetransmitTime = maxPacketLifeTime.Map([](const uint32_t i) { return static_cast(i); }).FromMaybe(-1); + init.maxRetransmits = maxRetransmits.Map([](const uint32_t i) { return static_cast(i); }).FromMaybe(-1); + init.protocol = protocol; + init.negotiated = negotiated; + init.id = id.Map([](const uint32_t i) { return static_cast(i); }).FromMaybe(-1); + return node_webrtc::Pure(init); +} + +TO_JS_IMPL(webrtc::RTCError*, error) { + return node_webrtc::Converter>::Convert(error); +} + +TO_JS_IMPL(const webrtc::RTCError*, error) { + Nan::EscapableHandleScope scope; + if (!error) { + return node_webrtc::Validation>::Invalid("No error? Please file a bug on https://github.com/js-platform/node-webrtc"); + } + switch (error->type()) { + case webrtc::RTCErrorType::NONE: + return node_webrtc::Validation>::Invalid("No error? Please file a bug on https://github.com/js-platform/node-webrtc"); + case webrtc::RTCErrorType::UNSUPPORTED_PARAMETER: + case webrtc::RTCErrorType::INVALID_PARAMETER: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateInvalidAccessError(error->message()))); + case webrtc::RTCErrorType::INVALID_RANGE: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateRangeError(error->message()))); + case webrtc::RTCErrorType::SYNTAX_ERROR: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateSyntaxError(error->message()))); + case webrtc::RTCErrorType::INVALID_STATE: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateInvalidStateError(error->message()))); + case webrtc::RTCErrorType::INVALID_MODIFICATION: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateInvalidModificationError(error->message()))); + case webrtc::RTCErrorType::NETWORK_ERROR: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateNetworkError(error->message()))); + // NOTE(mroberts): SetLocalDescription in the wrong state can throw this. + case webrtc::RTCErrorType::INTERNAL_ERROR: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateInvalidStateError(error->message()))); + case webrtc::RTCErrorType::UNSUPPORTED_OPERATION: + case webrtc::RTCErrorType::RESOURCE_EXHAUSTED: + return node_webrtc::Pure(scope.Escape(node_webrtc::ErrorFactory::CreateOperationError(error->message()))); + } +} + +node_webrtc::Validation node_webrtc::Converter::Convert(webrtc::RTCError* error) { + return node_webrtc::Converter::Convert(error); +} + +node_webrtc::Validation node_webrtc::Converter::Convert(const webrtc::RTCError* error) { + if (!error) { + return node_webrtc::Validation::Invalid("No error? Please file a bug on https://github.com/js-platform/node-webrtc"); + } + auto type = node_webrtc::MakeRight(node_webrtc::ErrorFactory::ErrorName::kError); + switch (error->type()) { + case webrtc::RTCErrorType::NONE: + return node_webrtc::Validation::Invalid("No error? Please file a bug on https://github.com/js-platform/node-webrtc"); + case webrtc::RTCErrorType::UNSUPPORTED_PARAMETER: + case webrtc::RTCErrorType::INVALID_PARAMETER: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kInvalidAccessError); + break; + case webrtc::RTCErrorType::INVALID_RANGE: + type = node_webrtc::MakeRight(node_webrtc::ErrorFactory::ErrorName::kRangeError); + break; + case webrtc::RTCErrorType::SYNTAX_ERROR: + type = node_webrtc::MakeRight(node_webrtc::ErrorFactory::ErrorName::kSyntaxError); + break; + case webrtc::RTCErrorType::INVALID_STATE: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kInvalidStateError); + break; + case webrtc::RTCErrorType::INVALID_MODIFICATION: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kInvalidModificationError); + break; + case webrtc::RTCErrorType::NETWORK_ERROR: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kNetworkError); + break; + // NOTE(mroberts): SetLocalDescription in the wrong state can throw this. + case webrtc::RTCErrorType::INTERNAL_ERROR: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kInvalidStateError); + break; + case webrtc::RTCErrorType::UNSUPPORTED_OPERATION: + case webrtc::RTCErrorType::RESOURCE_EXHAUSTED: + type = node_webrtc::MakeLeft(node_webrtc::ErrorFactory::DOMExceptionName::kOperationError); + break; + } + return node_webrtc::Pure(node_webrtc::SomeError(error->message(), type)); +} + +node_webrtc::Validation node_webrtc::Converter::Convert(webrtc::PeerConnectionInterface::IceConnectionState state) { + switch (state) { + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionNew: + return node_webrtc::Pure(kNew); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionChecking: + return node_webrtc::Pure(kConnecting); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionConnected: + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionCompleted: + return node_webrtc::Pure(kConnected); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionDisconnected: + return node_webrtc::Pure(kDisconnected); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionFailed: + return node_webrtc::Pure(kFailed); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionClosed: + return node_webrtc::Pure(kClosed); + case webrtc::PeerConnectionInterface::IceConnectionState::kIceConnectionMax: + return node_webrtc::Validation::Invalid( + "WebRTC\'s RTCPeerConnection has an ICE connection state \"max\", but I have no idea" + "what this means. If you see this error, file a bug on https://github.com/js-platform/node-webrtc"); + } +} + +TO_JS_IMPL(node_webrtc::RTCStatsResponseInit, init) { + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(node_webrtc::RTCStatsResponse::Create(init.first, init.second)->handle().As())); +} + +TO_JS_IMPL(webrtc::RtpSource, source) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + object->Set(Nan::New("timestamp").ToLocalChecked(), Nan::New(source.timestamp_ms())); + object->Set(Nan::New("source").ToLocalChecked(), Nan::New(source.source_id())); + return node_webrtc::Pure(scope.Escape(object).As()); +} + +TO_JS_IMPL(webrtc::RtpHeaderExtensionParameters, params) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + object->Set(Nan::New("uri").ToLocalChecked(), Nan::New(params.uri).ToLocalChecked()); + object->Set(Nan::New("id").ToLocalChecked(), Nan::New(params.id)); + return node_webrtc::Pure(scope.Escape(object.As())); +} + +TO_JS_IMPL(webrtc::RtpCodecParameters, params) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + object->Set(Nan::New("payloadType").ToLocalChecked(), Nan::New(params.payload_type)); + object->Set(Nan::New("mimeType").ToLocalChecked(), Nan::New(params.mime_type()).ToLocalChecked()); + if (params.clock_rate) { + object->Set(Nan::New("clockRate").ToLocalChecked(), Nan::New(*params.clock_rate)); + } + if (params.num_channels) { + object->Set(Nan::New("channels").ToLocalChecked(), Nan::New(*params.num_channels)); + } + if (!params.parameters.empty()) { + std::string fmtp("a=fmtp:" + std::to_string(params.payload_type)); + unsigned long i = 0; + for (auto param : params.parameters) { + fmtp += " " + param.first + "=" + param.second; + if (i < params.parameters.size() - 1) { + fmtp += ";"; + } + i++; + } + object->Set(Nan::New("sdpFmtpLine").ToLocalChecked(), Nan::New(fmtp).ToLocalChecked()); + } + return node_webrtc::Pure(scope.Escape(object).As()); +} + +TO_JS_IMPL(webrtc::RtcpParameters, params) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + if (!params.cname.empty()) { + object->Set(Nan::New("cname").ToLocalChecked(), Nan::New(params.cname).ToLocalChecked()); + } + object->Set(Nan::New("reducedSize").ToLocalChecked(), Nan::New(params.reduced_size)); + return node_webrtc::Pure(scope.Escape(object.As())); +} + +static v8::Local CreateRtpParameters(v8::Local headerExtensions, v8::Local codecs, v8::Local rtcp) { + Nan::EscapableHandleScope scope; + auto object = Nan::New(); + object->Set(Nan::New("headerExtensions").ToLocalChecked(), headerExtensions); + object->Set(Nan::New("codecs").ToLocalChecked(), codecs); + object->Set(Nan::New("encodings").ToLocalChecked(), Nan::New()); + object->Set(Nan::New("rtcp").ToLocalChecked(), rtcp); + return scope.Escape(object); +} + +TO_JS_IMPL(webrtc::RtpParameters, params) { + return curry(CreateRtpParameters) + % node_webrtc::From>(params.header_extensions) + * node_webrtc::From>(params.codecs) + * node_webrtc::From>(params.rtcp); +} + +static webrtc::RtpTransceiverInit CreateRtpTransceiverInit( + const webrtc::RtpTransceiverDirection direction, + const std::vector streams) { + webrtc::RtpTransceiverInit init; + init.direction = direction; + std::vector stream_ids; + for (const auto& stream : streams) { + stream_ids.emplace_back(stream->stream()->id()); + } + init.stream_ids = stream_ids; + return init; +} + +TO_JS_IMPL(const webrtc::RTCStatsMemberInterface*, value) { + switch (value->type()) { + case webrtc::RTCStatsMemberInterface::Type::kBool: // bool + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kInt32: // int32_t + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kUint32: // uint32_t + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kInt64: // int64_t + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kUint64: // uint64_t + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kDouble: // double + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kString: // std::string + return node_webrtc::From>(*value->cast_to>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceBool: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceInt32: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceUint32: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceInt64: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceUint64: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceDouble: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + case webrtc::RTCStatsMemberInterface::Type::kSequenceString: // std::vector + return node_webrtc::From>(*value->cast_to>>()); + } +} + +TO_JS_IMPL(const webrtc::RTCStats*, value) { + Nan::EscapableHandleScope scope; + auto stats = Nan::New(); + stats->Set(Nan::New("id").ToLocalChecked(), node_webrtc::From>(value->id()).UnsafeFromValid()); + stats->Set(Nan::New("timestamp").ToLocalChecked(), node_webrtc::From>(value->timestamp_us() / 1000.0).UnsafeFromValid()); + stats->Set(Nan::New("type").ToLocalChecked(), node_webrtc::From>(std::string(value->type())).UnsafeFromValid()); + for (const webrtc::RTCStatsMemberInterface* member : value->Members()) { + if (member->is_defined()) { + stats->Set(Nan::New(member->name()).ToLocalChecked(), node_webrtc::From>(member).UnsafeFromValid()); + } + } + return node_webrtc::Pure(scope.Escape(stats).As()); +} + +TO_JS_IMPL(rtc::scoped_refptr, value) { + Nan::EscapableHandleScope scope; + auto context = Nan::GetCurrentContext(); + auto report = v8::Map::New(context->GetIsolate()); + for (const webrtc::RTCStats& stats : *value) { + report->Set(context, Nan::New(stats.id()).ToLocalChecked(), node_webrtc::From>(&stats).UnsafeFromValid()).IsEmpty(); + } + return node_webrtc::Pure(scope.Escape(report).As()); +} + +namespace node_webrtc { + +DECLARE_CONVERTER(v8::Local, node_webrtc::ImageData) +CONVERTER_IMPL(v8::Local, node_webrtc::ImageData, value) { + return node_webrtc::From>(value).FlatMap( + [](const v8::Local object) { + return curry(node_webrtc::ImageData::Create) + % node_webrtc::GetRequired(object, "width") + * node_webrtc::GetRequired(object, "height") + * node_webrtc::GetRequired(object, "data"); + }); +} + +DECLARE_CONVERTER(node_webrtc::ImageData, I420ImageData) +CONVERTER_IMPL(node_webrtc::ImageData, I420ImageData, imageData) { + return imageData.toI420(); +} + +CONVERT_VIA(v8::Local, node_webrtc::ImageData, I420ImageData) + +DECLARE_CONVERTER(node_webrtc::ImageData, RgbaImageData) +CONVERTER_IMPL(node_webrtc::ImageData, RgbaImageData, imageData) { + return imageData.toRgba(); +} + +CONVERT_VIA(v8::Local, node_webrtc::ImageData, RgbaImageData) + +DECLARE_CONVERTER(node_webrtc::I420ImageData, rtc::scoped_refptr) +CONVERT_VIA(v8::Local, node_webrtc::I420ImageData, rtc::scoped_refptr) + +} // namespace node_webrtc + +static rtc::scoped_refptr CreateI420Buffer( + node_webrtc::I420ImageData i420Frame +) { + auto buffer = webrtc::I420Buffer::Create(i420Frame.width(), i420Frame.height()); + memcpy(buffer->MutableDataY(), i420Frame.dataY(), i420Frame.sizeOfLuminancePlane()); + memcpy(buffer->MutableDataU(), i420Frame.dataU(), i420Frame.sizeOfChromaPlane()); + memcpy(buffer->MutableDataV(), i420Frame.dataV(), i420Frame.sizeOfChromaPlane()); + return buffer; +} + +CONVERTER_IMPL(node_webrtc::I420ImageData, rtc::scoped_refptr, value) { + return node_webrtc::Pure(CreateI420Buffer(value)); +} + +#define REQUIRED(type, memberName, stringValue) EXPAND_OBJ_FROM_JS_REQUIRED(type, stringValue) +#define OPTIONAL(type, memberName, stringValue) EXPAND_OBJ_FROM_JS_OPTIONAL(type, stringValue) +#define DEFAULT(type, memberName, stringValue, defaultValue) EXPAND_OBJ_FROM_JS_DEFAULT(type, stringValue, defaultValue) +OBJ_FROM_JS_IMPL1(RTCOAUTHCREDENTIAL, CreateRTCOAuthCredential) +OBJ_FROM_JS_IMPL2(ICESERVER, CreateIceServer) +OBJ_FROM_JS_IMPL1(RTCDTLSFINGERPRINT, CreateRTCDtlsFingerprint) +OBJ_FROM_JS_IMPL2(UNSIGNEDSHORTRANGE, CreateUnsignedShortRange) +OBJ_FROM_JS_IMPL1(RTCOFFEROPTIONS, CreateRTCOfferOptions) +OBJ_FROM_JS_IMPL1(RTCANSWEROPTIONS, CreateRTCAnswerOptions) +OBJ_FROM_JS_IMPL1(RTCSESSIONDESCRIPTIONINIT, CreateRTCSessionDescriptionInit) +OBJ_FROM_JS_IMPL1(RTCVIDEOSOURCEINIT, CreateRTCVideoSourceInit) +OBJ_FROM_JS_IMPL2(ICECANDIDATEINTERFACE, CreateIceCandidateInterface) +OBJ_FROM_JS_IMPL2(DATACHANNELINIT, CreateDataChannelInit) +OBJ_FROM_JS_IMPL1(RTCRTPTRANSCEIVERINIT, CreateRtpTransceiverInit) +#undef REQUIRED +#undef OPTIONAL +#undef DEFAULT + +TO_JS_IMPL(rtc::scoped_refptr, value) { + return value->type() == webrtc::VideoFrameBuffer::Type::kI420 + ? node_webrtc::From>(value->GetI420()) + : node_webrtc::Validation>::Invalid("Unsupported RTCVideoFrame type (file a node-webrtc bug, please!)"); +} + +TO_JS_IMPL(rtc::scoped_refptr, value) { + Nan::EscapableHandleScope scope; + auto isolate = Nan::GetCurrentContext()->GetIsolate(); + + auto sizeOfYPlane = value->StrideY() * value->height(); + auto sizeOfUPlane = value->StrideU() * value->height() / 2; + auto sizeOfVPlane = value->StrideV() * value->height() / 2; + + auto byteLength = sizeOfYPlane + sizeOfUPlane + sizeOfVPlane; + auto arrayBuffer = v8::ArrayBuffer::New(isolate, byteLength); + uint8_t* data = static_cast(arrayBuffer->GetContents().Data()); + + auto srcYPlane = value->DataY(); + auto srcUPlane = value->DataU(); + auto srcVPlane = value->DataV(); + + auto dstYPlane = data; + auto dstUPlane = data + sizeOfYPlane; + auto dstVPlane = dstUPlane + sizeOfUPlane; + + memcpy(dstYPlane, srcYPlane, sizeOfYPlane); + memcpy(dstUPlane, srcUPlane, sizeOfUPlane); + memcpy(dstVPlane, srcVPlane, sizeOfVPlane); + + auto uint8Array = v8::Uint8ClampedArray::New(arrayBuffer, 0, byteLength); + return node_webrtc::Pure(scope.Escape(uint8Array.As())); +} + +TO_JS_IMPL(webrtc::VideoFrame, value) { + Nan::EscapableHandleScope scope; + auto frame = Nan::New(); + frame->Set(Nan::New("width").ToLocalChecked(), node_webrtc::From>(value.width()).UnsafeFromValid()); + frame->Set(Nan::New("height").ToLocalChecked(), node_webrtc::From>(value.height()).UnsafeFromValid()); + frame->Set(Nan::New("rotation").ToLocalChecked(), node_webrtc::From>(static_cast(value.rotation())).UnsafeFromValid()); + auto maybeData = node_webrtc::From>(value.video_frame_buffer()); + if (maybeData.IsInvalid()) { + return node_webrtc::Validation>::Invalid(maybeData.ToErrors()[0]); + } + auto data = maybeData.UnsafeFromValid(); + frame->Set(Nan::New("data").ToLocalChecked(), data); + return node_webrtc::Pure(scope.Escape(frame).As()); +} diff --git a/deps/exokit-bindings/webrtc/src/converters/dictionaries.h b/deps/exokit-bindings/webrtc/src/converters/dictionaries.h new file mode 100644 index 0000000000..0013b820be --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/dictionaries.h @@ -0,0 +1,234 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines conversion functions between WebRTC and v8 data types. We + * try to match the W3C-specced Web IDL as closely as possible. + */ + +#ifndef SRC_DECLARE_CONVERTERS_WEBRTC_H_ +#define SRC_DECLARE_CONVERTERS_WEBRTC_H_ + +#include + +#ifdef _WIN32 +#include +#endif + +#include +#include // IWYU pragma: keep +#include +#include +#include // IWYU pragma: keep + +#include "src/converters.h" +#include "src/converters/enums.h" +#include "src/functional/maybe.h" +#include "src/functional/validation.h" + +namespace rtc { + +template class scoped_refptr; + +} // namespace rtc + +namespace webrtc { + +class I420Buffer; +class I420BufferInterface; +class IceCandidateInterface; +struct DataChannelInit; +class RTCError; +class RTCStats; +class RTCStatsMemberInterface; +class RTCStatsReport; +struct RtcpParameters; +struct RtpCodecParameters; +struct RtpExtension; +struct RtpParameters; +class RtpSource; +enum class RtpTransceiverDirection; +struct RtpTransceiverInit; +enum class SdpSemantics; +class SessionDescriptionInterface; +class VideoFrame; +class VideoFrameBuffer; + +} // namespace webrtc; + +namespace node_webrtc { + +class I420ImageData; +class MediaStream; // IWYU pragma: keep +class RgbaImageData; +class SomeError; + +#define DECLARE_STRUCT(TYPE) \ + struct TYPE { \ + TYPE ## _LIST \ + }; + +#define DECLARE_STRUCT_OPTIONAL(TYPE, VAR) node_webrtc::Maybe VAR; + +#define DECLARE_STRUCT_REQUIRED(TYPE, VAR) TYPE VAR; + +#define EXPAND_DEFAULT_STRUCT(TYPE, VAR) TYPE VAR; + +#define DATACHANNELINIT webrtc::DataChannelInit +#define DATACHANNELINIT_LIST \ + DEFAULT(bool, ordered, "ordered", true) \ + OPTIONAL(uint32_t, maxPacketLifeTime, "maxPacketLifeTime") \ + OPTIONAL(uint32_t, maxRetransmits, "maxRetransmits") \ + DEFAULT(std::string, protocol, "protocol", "") \ + DEFAULT(bool, negotiated, "negotiated", false) \ + OPTIONAL(uint32_t, id, "id") \ + DEFAULT(node_webrtc::RTCPriorityType, priority, "priority", node_webrtc::RTCPriorityType::kLow) + +#define ICECANDIDATEINTERFACE webrtc::IceCandidateInterface* +#define ICECANDIDATEINTERFACE_LIST \ + DEFAULT(std::string, candidate, "candidate", "") \ + DEFAULT(std::string, sdpMid, "sdpMid", "") \ + DEFAULT(int, sdpMLineIndex, "sdpMLineIndex", 0) \ + OPTIONAL(std::string, usernameFragment, "usernameFragment") + +#define ICESERVER webrtc::PeerConnectionInterface::IceServer +#define ICESERVER_LIST \ + REQUIRED(stringOrStrings, urls, "urls") \ + DEFAULT(std::string, username, "username", "") \ + DEFAULT(stringOrCredential, credential, "credential", node_webrtc::MakeLeft(std::string(""))) \ + DEFAULT(node_webrtc::RTCIceCredentialType, credentialType, "credentialType", node_webrtc::RTCIceCredentialType::kPassword) + +#define RTCANSWEROPTIONS RTCAnswerOptions +#define RTCANSWEROPTIONS_LIST \ + DEFAULT(bool, voiceActivityDetection, "voiceActivityDetection", true) + +#define RTCOAUTHCREDENTIAL RTCOAuthCredential +#define RTCOAUTHCREDENTIAL_LIST \ + REQUIRED(std::string, macKey, "macKey") \ + REQUIRED(std::string, accessToken, "accessToken") + +#define RTCOFFEROPTIONS RTCOfferOptions +#define RTCOFFEROPTIONS_LIST \ + DEFAULT(bool, voiceActivityDetection, "voiceActivityDetection", true) \ + DEFAULT(bool, iceRestart, "iceRestart", false) \ + OPTIONAL(bool, offerToReceiveAudio, "offerToReceiveAudio") \ + OPTIONAL(bool, offerToReceiveVideo, "offerToReceiveVideo") + +#define RTCDTLSFINGERPRINT RTCDtlsFingerprint +#define RTCDTLSFINGERPRINT_LIST \ + OPTIONAL(std::string, algorithm, "algorithm") \ + OPTIONAL(std::string, value, "value") + +#define RTCRTPTRANSCEIVERINIT webrtc::RtpTransceiverInit +#define RTCRTPTRANSCEIVERINIT_LIST \ + DEFAULT(webrtc::RtpTransceiverDirection, direciton, "direction", webrtc::RtpTransceiverDirection::kSendRecv) \ + DEFAULT(std::vector, streams, "streams", std::vector()) + +#define RTCSESSIONDESCRIPTIONINIT RTCSessionDescriptionInit +#define RTCSESSIONDESCRIPTIONINIT_LIST \ + REQUIRED(node_webrtc::RTCSdpType, type, "type") \ + DEFAULT(std::string, sdp, "sdp", "") + +#define RTCVIDEOSOURCEINIT RTCVideoSourceInit +#define RTCVIDEOSOURCEINIT_LIST \ + DEFAULT(bool, isScreencast, "isScreencast", false) \ + OPTIONAL(bool, needsDenoising, "needsDenoising") + +#define UNSIGNEDSHORTRANGE UnsignedShortRange +#define UNSIGNEDSHORTRANGE_LIST \ + OPTIONAL(uint16_t, min, "min") \ + OPTIONAL(uint16_t, max, "max") + +#define REQUIRED(TYPE, VAR, PROP) DECLARE_STRUCT_REQUIRED(TYPE, VAR) +#define OPTIONAL(TYPE, VAR, PROP) DECLARE_STRUCT_OPTIONAL(TYPE, VAR) +#define DEFAULT(TYPE, VAR, PROP, DEFAULT) EXPAND_DEFAULT_STRUCT(TYPE, VAR) +DECLARE_STRUCT(RTCDTLSFINGERPRINT) +DECLARE_STRUCT(RTCOAUTHCREDENTIAL) +DECLARE_STRUCT(RTCSESSIONDESCRIPTIONINIT) +DECLARE_STRUCT(RTCVIDEOSOURCEINIT) +DECLARE_STRUCT(UNSIGNEDSHORTRANGE) +#undef REQUIRED +#undef OPTIONAL +#undef DEFAULT + +struct ExtendedRTCConfiguration { + ExtendedRTCConfiguration(): configuration(webrtc::PeerConnectionInterface::RTCConfiguration()), portRange(UnsignedShortRange()) {} + ExtendedRTCConfiguration(const webrtc::PeerConnectionInterface::RTCConfiguration configuration, const UnsignedShortRange portRange): configuration(configuration), portRange(portRange) {} + webrtc::PeerConnectionInterface::RTCConfiguration configuration; + UnsignedShortRange portRange; +}; + +struct RTCOfferOptions { + RTCOfferOptions(): options(webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()) {} + explicit RTCOfferOptions(const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options): options(options) {} + const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; +}; + +struct RTCAnswerOptions { + RTCAnswerOptions(): options(webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()) {} + explicit RTCAnswerOptions(const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options): options(options) {} + const webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; +}; + +// TODO(mroberts): Move this elsewhere. +template +struct Converter, v8::Local> { + static Validation> Convert(absl::optional value) { + if (value) { + return Converter>::Convert(*value); + } + return Pure(Nan::Null().As()); + } +}; + +DECLARE_TO_JS(rtc::scoped_refptr) +DECLARE_FROM_JS(webrtc::DataChannelInit) +DECLARE_CONVERTER(webrtc::RTCError*, SomeError) +DECLARE_FROM_JS(webrtc::IceCandidateInterface*) +DECLARE_TO_JS(const webrtc::IceCandidateInterface*) +DECLARE_TO_AND_FROM_JS(webrtc::PeerConnectionInterface::IceServer) +DECLARE_CONVERTER(webrtc::PeerConnectionInterface::IceConnectionState, RTCPeerConnectionState) +DECLARE_FROM_JS(webrtc::PeerConnectionInterface::RTCConfiguration) +DECLARE_TO_JS(webrtc::RtpCodecParameters) +DECLARE_TO_JS(webrtc::RTCError*) +DECLARE_TO_JS(const webrtc::RTCError*) +DECLARE_CONVERTER(const webrtc::RTCError*, SomeError) +DECLARE_TO_JS(webrtc::RtcpParameters) +DECLARE_TO_JS(webrtc::RtpParameters) +DECLARE_TO_JS(webrtc::RtpExtension) +DECLARE_TO_JS(webrtc::RtpSource) +DECLARE_TO_JS(const webrtc::RTCStats*) +DECLARE_TO_JS(const webrtc::RTCStatsMemberInterface*) +DECLARE_FROM_JS(webrtc::RtpTransceiverInit) +DECLARE_FROM_JS(webrtc::SessionDescriptionInterface*) +DECLARE_CONVERTER(webrtc::SessionDescriptionInterface*, RTCSessionDescriptionInit) +DECLARE_TO_JS(const webrtc::SessionDescriptionInterface*) + +typedef std::pair>> RTCStatsResponseInit; + +DECLARE_TO_AND_FROM_JS(ExtendedRTCConfiguration) +DECLARE_FROM_JS(RTCAnswerOptions) +DECLARE_FROM_JS(RTCDtlsFingerprint) +DECLARE_FROM_JS(RTCOAuthCredential) +DECLARE_FROM_JS(RTCOfferOptions) +DECLARE_TO_AND_FROM_JS(RTCSessionDescriptionInit) +DECLARE_CONVERTER(RTCSessionDescriptionInit, webrtc::SessionDescriptionInterface*) +DECLARE_TO_JS(RTCStatsResponseInit) +DECLARE_FROM_JS(RTCVideoSourceInit) +DECLARE_TO_AND_FROM_JS(UnsignedShortRange) +DECLARE_TO_JS(webrtc::VideoTrackSourceInterface::Stats) +DECLARE_FROM_JS(rtc::scoped_refptr) +DECLARE_TO_JS(rtc::scoped_refptr) +DECLARE_TO_JS(rtc::scoped_refptr) +DECLARE_TO_JS(webrtc::VideoFrame) +DECLARE_FROM_JS(node_webrtc::I420ImageData) +DECLARE_FROM_JS(node_webrtc::RgbaImageData) + +} // namespace node_webrtc + +#endif // SRC_DECLARE_CONVERTERS_WEBRTC_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/enums.cc b/deps/exokit-bindings/webrtc/src/converters/enums.cc new file mode 100644 index 0000000000..b0764909cf --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/enums.cc @@ -0,0 +1,95 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/converters/enums.h" + +#include "src/converters/v8-converters.h" // IWYU pragma: keep + +using node_webrtc::BinaryType; +using node_webrtc::RTCIceCredentialType; +using node_webrtc::RTCPeerConnectionState; +using node_webrtc::RTCPriorityType; +using node_webrtc::RTCSdpType; + +#define ENUM_TO_STRING(ENUM) \ + CONVERTER_IMPL(ENUM, std::string, value) { \ + switch (value) { \ + ENUM ## _LIST \ + } \ + } \ + \ + CONVERT_VIA(v8::Local, std::string, ENUM) + +#define ENUM_TO_STRING_SUPPORTED(ENUM, VALUE, STRING) \ + case ENUM::VALUE: \ + return node_webrtc::Pure(STRING); + +#define ENUM_TO_STRING_UNSUPPORTED(ENUM, VALUE, ERROR) \ + case ENUM::VALUE: \ + return node_webrtc::Validation::Invalid(ERROR); + +#define STRING_TO_ENUM(ENUM, ERROR) \ + CONVERTER_IMPL(std::string, ENUM, value) { \ + ENUM ## _LIST \ + return node_webrtc::Validation::Invalid(ERROR); \ + } \ + \ + CONVERT_VIA(ENUM, std::string, v8::Local) + +#define STRING_TO_ENUM_SUPPORTED(ENUM, VALUE, STRING) \ + if (value == STRING) { \ + return node_webrtc::Pure(ENUM::VALUE); \ + } + +#define STRING_TO_ENUM_UNSUPPORTED(ENUM, STRING, ERROR) \ + if (value == STRING) { \ + return node_webrtc::Validation::Invalid(ERROR); \ + } + +#define SUPPORTED(ENUM, VALUE, STRING) ENUM_TO_STRING_SUPPORTED(ENUM, VALUE, STRING) +#define UNSUPPORTED(ENUM, VALUE, STRING, ERROR) ENUM_TO_STRING_UNSUPPORTED(ENUM, VALUE, ERROR) +ENUM_TO_STRING(BINARYTYPE) +ENUM_TO_STRING(BUNDLEPOLICY) +ENUM_TO_STRING(DATASTATE) +ENUM_TO_STRING(ICECONNECTIONSTATE) +ENUM_TO_STRING(ICEGATHERINGSTATE) +ENUM_TO_STRING(ICETRANSPORTSTYPE) +ENUM_TO_STRING(MEDIATYPE) +ENUM_TO_STRING(RTCICECREDENTIALTYPE) +ENUM_TO_STRING(RTCPEERCONNECTIONSTATE) +ENUM_TO_STRING(RTCPMUXPOLICY) +ENUM_TO_STRING(RTCPRIORITYTYPE) +ENUM_TO_STRING(RTCRTPTRANSCEIVERDIRECTION) +ENUM_TO_STRING(RTCSDPTYPE) +ENUM_TO_STRING(SDPSEMANTICS) +ENUM_TO_STRING(SIGNALINGSTATE) +ENUM_TO_STRING(TRACKSTATE) +ENUM_TO_STRING(VIDEO_FRAME_BUFFER_TYPE) +#undef SUPPORTED +#undef UNSUPPORTED + +#define SUPPORTED(ENUM, VALUE, STRING) STRING_TO_ENUM_SUPPORTED(ENUM, VALUE, STRING) +#define UNSUPPORTED(ENUM, VALUE, STRING, ERROR) STRING_TO_ENUM_UNSUPPORTED(ENUM, STRING, ERROR) +STRING_TO_ENUM(BINARYTYPE, "Invalid binaryType") +STRING_TO_ENUM(BUNDLEPOLICY, "Invalid RTCBundlePolicy") +STRING_TO_ENUM(DATASTATE, "Invalid RTCDataChannelState") +STRING_TO_ENUM(ICECONNECTIONSTATE, "Invalid RTCIceConnectionState") +STRING_TO_ENUM(ICEGATHERINGSTATE, "Invalid RTCIceGatheringState") +STRING_TO_ENUM(ICETRANSPORTSTYPE, "Invalid RTCIceTransportPolicy") +STRING_TO_ENUM(MEDIATYPE, "Invalid kind") +STRING_TO_ENUM(RTCICECREDENTIALTYPE, "Invalid RTCIceCredentialType") +STRING_TO_ENUM(RTCPEERCONNECTIONSTATE, "Invalid RTCPeerConnectionState") +STRING_TO_ENUM(RTCPMUXPOLICY, "Invalid RTCRtcpMuxPolicy") +STRING_TO_ENUM(RTCPRIORITYTYPE, "Invalid RTCPriorityType") +STRING_TO_ENUM(RTCRTPTRANSCEIVERDIRECTION, "Invalid RTCRtpTransceiverDirection") +STRING_TO_ENUM(RTCSDPTYPE, "Invalid RTCSdpType") +STRING_TO_ENUM(SDPSEMANTICS, "Invalid RTCSdpSemantics") +STRING_TO_ENUM(SIGNALINGSTATE, "Invalid RTCSignalingState") +STRING_TO_ENUM(TRACKSTATE, "Invalid MediaStreamTrackState") +STRING_TO_ENUM(VIDEO_FRAME_BUFFER_TYPE, "Invalid VideoFrameBufferType") +#undef SUPPORTED +#undef UNSUPPORTED diff --git a/deps/exokit-bindings/webrtc/src/converters/enums.h b/deps/exokit-bindings/webrtc/src/converters/enums.h new file mode 100644 index 0000000000..ec8b0a7ee0 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/enums.h @@ -0,0 +1,184 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_CONVERTERS_ENUMS_H_ +#define SRC_CONVERTERS_ENUMS_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep + +#include "src/converters.h" +#include "src/functional/validation.h" // IWYU pragma: keep + +#define ENUM_IMPL(ENUM) \ + enum ENUM { \ + ENUM ## _LIST \ + }; + +#define ENUM_IMPL_VALUE(VALUE) VALUE, + +#define DECLARE_TO_AND_FROM_JS_ENUM(ENUM) \ + DECLARE_CONVERTER(ENUM, std::string) \ + DECLARE_CONVERTER(std::string, ENUM) \ + DECLARE_TO_AND_FROM_JS(ENUM) + +namespace node_webrtc { + +#define BINARYTYPE BinaryType +#define BINARYTYPE_LIST \ + UNSUPPORTED(BINARYTYPE, kBlob, "blob", "\"blob\" is not supported; see TODO") \ + SUPPORTED(BINARYTYPE, kArrayBuffer, "arraybuffer") + +#define BUNDLEPOLICY webrtc::PeerConnectionInterface::BundlePolicy +#define BUNDLEPOLICY_LIST \ + SUPPORTED(BUNDLEPOLICY, kBundlePolicyBalanced, "balanced") \ + SUPPORTED(BUNDLEPOLICY, kBundlePolicyMaxCompat, "max-compat") \ + SUPPORTED(BUNDLEPOLICY, kBundlePolicyMaxBundle, "max-bundle") + +#define DATASTATE webrtc::DataChannelInterface::DataState +#define DATASTATE_LIST \ + SUPPORTED(DATASTATE, kClosed, "closed") \ + SUPPORTED(DATASTATE, kClosing, "closing") \ + SUPPORTED(DATASTATE, kConnecting, "connecting") \ + SUPPORTED(DATASTATE, kOpen, "open") + +#define ICECONNECTIONSTATE webrtc::PeerConnectionInterface::IceConnectionState +#define ICECONNECTIONSTATE_LIST \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionNew, "new") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionChecking, "checking") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionClosed, "closed") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionCompleted, "completed") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionConnected, "connected") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionDisconnected, "disconnected") \ + SUPPORTED(ICECONNECTIONSTATE, kIceConnectionFailed, "failed") \ + UNSUPPORTED(ICECONNECTIONSTATE, kIceConnectionMax, "max", "\"max\" is not a valid RTCIceConnectionState") + +#define ICEGATHERINGSTATE webrtc::PeerConnectionInterface::IceGatheringState +#define ICEGATHERINGSTATE_LIST \ + SUPPORTED(ICEGATHERINGSTATE, kIceGatheringNew, "new") \ + SUPPORTED(ICEGATHERINGSTATE, kIceGatheringGathering, "gathering") \ + SUPPORTED(ICEGATHERINGSTATE, kIceGatheringComplete, "complete") + +#define ICETRANSPORTSTYPE webrtc::PeerConnectionInterface::IceTransportsType +#define ICETRANSPORTSTYPE_LIST \ + SUPPORTED(ICETRANSPORTSTYPE, kAll, "all") \ + SUPPORTED(ICETRANSPORTSTYPE, kRelay, "relay") \ + UNSUPPORTED(ICETRANSPORTSTYPE, kNoHost, "no-host", "\"no-host\" is not a valid RTCIceTransportPolicy") \ + UNSUPPORTED(ICETRANSPORTSTYPE, kNone, "none", "\"none\" is not a valid RTCIceTransportPolicy") + +#define MEDIATYPE cricket::MediaType +#define MEDIATYPE_LIST \ + SUPPORTED(MEDIATYPE, MEDIA_TYPE_AUDIO, "audio") \ + SUPPORTED(MEDIATYPE, MEDIA_TYPE_VIDEO, "video") \ + SUPPORTED(MEDIATYPE, MEDIA_TYPE_DATA, "data") + +#define RTCICECREDENTIALTYPE RTCIceCredentialType +#define RTCICECREDENTIALTYPE_LIST \ + SUPPORTED(RTCICECREDENTIALTYPE, kPassword, "password") \ + SUPPORTED(RTCICECREDENTIALTYPE, kOAuth, "oauth") + +#define RTCPEERCONNECTIONSTATE RTCPeerConnectionState +#define RTCPEERCONNECTIONSTATE_LIST \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kClosed, "closed") \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kConnected, "connected") \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kConnecting, "connecting") \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kDisconnected, "disconnected") \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kFailed, "failed") \ + SUPPORTED(RTCPEERCONNECTIONSTATE, kNew, "new") + +#define RTCPMUXPOLICY webrtc::PeerConnectionInterface::RtcpMuxPolicy +#define RTCPMUXPOLICY_LIST \ + SUPPORTED(RTCPMUXPOLICY, kRtcpMuxPolicyNegotiate, "negotiate") \ + SUPPORTED(RTCPMUXPOLICY, kRtcpMuxPolicyRequire, "require") + +#define RTCPRIORITYTYPE RTCPriorityType +#define RTCPRIORITYTYPE_LIST \ + SUPPORTED(RTCPRIORITYTYPE, kVeryLow, "very-low") \ + SUPPORTED(RTCPRIORITYTYPE, kLow, "low") \ + SUPPORTED(RTCPRIORITYTYPE, kMedium, "medium") \ + SUPPORTED(RTCPRIORITYTYPE, kHigh, "high") + +#define RTCSDPTYPE RTCSdpType +#define RTCSDPTYPE_LIST \ + SUPPORTED(RTCSDPTYPE, kOffer, "offer") \ + SUPPORTED(RTCSDPTYPE, kAnswer, "answer") \ + SUPPORTED(RTCSDPTYPE, kPrAnswer, "pranswer") \ + SUPPORTED(RTCSDPTYPE, kRollback, "rollback") + +#define RTCRTPTRANSCEIVERDIRECTION webrtc::RtpTransceiverDirection +#define RTCRTPTRANSCEIVERDIRECTION_LIST \ + SUPPORTED(RTCRTPTRANSCEIVERDIRECTION, kSendRecv, "sendrecv") \ + SUPPORTED(RTCRTPTRANSCEIVERDIRECTION, kSendOnly, "sendonly") \ + SUPPORTED(RTCRTPTRANSCEIVERDIRECTION, kRecvOnly, "recvonly") \ + SUPPORTED(RTCRTPTRANSCEIVERDIRECTION, kInactive, "inactive") + +#define SDPSEMANTICS webrtc::SdpSemantics +#define SDPSEMANTICS_LIST \ + SUPPORTED(SDPSEMANTICS, kPlanB, "plan-b") \ + SUPPORTED(SDPSEMANTICS, kUnifiedPlan, "unified-plan") + +#define SIGNALINGSTATE webrtc::PeerConnectionInterface::SignalingState +#define SIGNALINGSTATE_LIST \ + SUPPORTED(SIGNALINGSTATE, kStable, "stable") \ + SUPPORTED(SIGNALINGSTATE, kHaveLocalOffer, "have-local-offer") \ + SUPPORTED(SIGNALINGSTATE, kHaveRemoteOffer, "have-remote-offer") \ + SUPPORTED(SIGNALINGSTATE, kHaveLocalPrAnswer, "have-local-pranswer") \ + SUPPORTED(SIGNALINGSTATE, kHaveRemotePrAnswer, "have-remote-pranswer") \ + SUPPORTED(SIGNALINGSTATE, kClosed, "closed") + +#define TRACKSTATE webrtc::MediaStreamTrackInterface::TrackState +#define TRACKSTATE_LIST \ + SUPPORTED(TRACKSTATE, kEnded, "ended") \ + SUPPORTED(TRACKSTATE, kLive, "live") + +#define VIDEO_FRAME_BUFFER_TYPE webrtc::VideoFrameBuffer::Type +#define VIDEO_FRAME_BUFFER_TYPE_LIST \ + UNSUPPORTED(VIDEO_FRAME_BUFFER_TYPE, kNative, "native", "\"native\" is not a valid VideoFrameBufferType") \ + SUPPORTED(VIDEO_FRAME_BUFFER_TYPE, kI420, "I420") \ + SUPPORTED(VIDEO_FRAME_BUFFER_TYPE, kI420A, "I420A") \ + SUPPORTED(VIDEO_FRAME_BUFFER_TYPE, kI444, "I444") \ + SUPPORTED(VIDEO_FRAME_BUFFER_TYPE, kI010, "I010") + +#define SUPPORTED(ENUM, VALUE, STRING) ENUM_IMPL_VALUE(VALUE) +#define UNSUPPORTED(ENUM, VALUE, STRING, ERROR) ENUM_IMPL_VALUE(VALUE) +ENUM_IMPL(BINARYTYPE) +ENUM_IMPL(RTCICECREDENTIALTYPE) +ENUM_IMPL(RTCPEERCONNECTIONSTATE) +ENUM_IMPL(RTCPRIORITYTYPE) +ENUM_IMPL(RTCSDPTYPE) +#undef SUPPORTED +#undef UNSUPPORTED + +DECLARE_TO_AND_FROM_JS_ENUM(BinaryType) +DECLARE_TO_AND_FROM_JS_ENUM(cricket::MediaType) +DECLARE_TO_AND_FROM_JS_ENUM(RTCIceCredentialType) +DECLARE_TO_AND_FROM_JS_ENUM(RTCPeerConnectionState) +DECLARE_TO_AND_FROM_JS_ENUM(RTCPriorityType) +DECLARE_TO_AND_FROM_JS_ENUM(RTCSdpType) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::MediaStreamTrackInterface::TrackState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::BundlePolicy) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::DataChannelInterface::DataState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::IceConnectionState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::IceGatheringState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::IceTransportsType) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::RtcpMuxPolicy) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::PeerConnectionInterface::SignalingState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::RtpTransceiverDirection) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::SdpSemantics) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::MediaSourceInterface::SourceState) +DECLARE_TO_AND_FROM_JS_ENUM(webrtc::VideoFrameBuffer::Type) + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_ENUMS_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/interfaces.cc b/deps/exokit-bindings/webrtc/src/converters/interfaces.cc new file mode 100644 index 0000000000..6edf79cae0 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/interfaces.cc @@ -0,0 +1,70 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/converters/interfaces.h" + +#include +#include +#include + +#include "src/mediastream.h" // IWYU pragma: keep +#include "src/mediastreamtrack.h" // IWYU pragma: keep +#include "src/rtcrtpreceiver.h" // IWYU pragma: keep +#include "src/rtcrtpsender.h" // IWYU pragma: keep +#include "src/rtcrtptransceiver.h" // IWYU pragma: keep + +#define CONVERT_INTERFACE_TO_JS(IFACE, NAME, TO_FN) \ + TO_JS_IMPL(node_webrtc::IFACE*, value) { \ + Nan::EscapableHandleScope scope; \ + if (!value) { \ + return node_webrtc::Validation>::Invalid(NAME " is null"); \ + } \ + return node_webrtc::Pure(scope.Escape(value->TO_FN()).As()); \ + } + +// FIXME(mroberts): This is not safe. +#define CONVERT_INTERFACE_FROM_JS(IFACE, NAME, FROM_FN) \ + FROM_JS_IMPL(node_webrtc::IFACE*, value) { \ + auto isolate = Nan::GetCurrentContext()->GetIsolate(); \ + auto tpl = node_webrtc::IFACE::tpl().Get(isolate); \ + return tpl->HasInstance(value) \ + ? node_webrtc::Pure(FROM_FN(JS_OBJ(value))) \ + : node_webrtc::Validation::Invalid("This is not an instance of " NAME); \ + } + +#define CONVERT_INTERFACE_TO_AND_FROM_JS(IFACE, NAME, TO_FN, FROM_FN) \ + CONVERT_INTERFACE_TO_JS(IFACE, NAME, TO_FN) \ + CONVERT_INTERFACE_FROM_JS(IFACE, NAME, FROM_FN) + +CONVERT_INTERFACE_TO_AND_FROM_JS(MediaStream, "MediaStream", handle, Nan::ObjectWrap::Unwrap) +CONVERT_INTERFACE_TO_AND_FROM_JS(MediaStreamTrack, "MediaStreamTrack", ToObject, node_webrtc::AsyncObjectWrapWithLoop::Unwrap) +CONVERT_INTERFACE_TO_AND_FROM_JS(RTCRtpSender, "RTCRtpSender", ToObject, node_webrtc::AsyncObjectWrapWithLoop::Unwrap) +CONVERT_INTERFACE_TO_JS(RTCRtpReceiver, "RTCRtpReceiver", ToObject) +CONVERT_INTERFACE_TO_JS(RTCRtpTransceiver, "RTCRtpTransceiver", ToObject) + +CONVERTER_IMPL(node_webrtc::MediaStreamTrack*, rtc::scoped_refptr, mediaStreamTrack) { + auto track = mediaStreamTrack->track(); + if (track->kind() != webrtc::MediaStreamTrackInterface::kAudioKind) { + return node_webrtc::Validation>::Invalid( + "Expected an audio MediaStreamTrack"); + } + rtc::scoped_refptr audioTrack(static_cast(track.get())); + return node_webrtc::Pure(audioTrack); +} + +CONVERTER_IMPL(node_webrtc::MediaStreamTrack*, rtc::scoped_refptr, mediaStreamTrack) { + auto track = mediaStreamTrack->track(); + if (track->kind() != webrtc::MediaStreamTrackInterface::kVideoKind) { + return node_webrtc::Validation>::Invalid( + "Expected a video MediaStreamTrack"); + } + rtc::scoped_refptr videoTrack(static_cast(track.get())); + return node_webrtc::Pure(videoTrack); +} + +CONVERT_VIA(v8::Local, node_webrtc::MediaStreamTrack*, rtc::scoped_refptr) +CONVERT_VIA(v8::Local, node_webrtc::MediaStreamTrack*, rtc::scoped_refptr) diff --git a/deps/exokit-bindings/webrtc/src/converters/interfaces.h b/deps/exokit-bindings/webrtc/src/converters/interfaces.h new file mode 100644 index 0000000000..45c68a86fd --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/interfaces.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_CONVERTERS_INTERFACES_H_ +#define SRC_CONVERTERS_INTERFACES_H_ + +#include // IWYU pragma: keep + +#ifdef _WIN32 +#include +#endif + +#include "src/converters.h" + +namespace rtc { template class scoped_refptr; } +namespace webrtc { class AudioTrackInterface; } +namespace webrtc { class VideoTrackInterface; } + +namespace node_webrtc { + +class MediaStream; +class MediaStreamTrack; +class RTCRtpReceiver; +class RTCRtpSender; +class RTCRtpTransceiver; + +DECLARE_TO_AND_FROM_JS(MediaStream*) +DECLARE_TO_AND_FROM_JS(MediaStreamTrack*) +DECLARE_TO_JS(RTCRtpReceiver*) +DECLARE_TO_AND_FROM_JS(RTCRtpSender*) +DECLARE_TO_JS(RTCRtpTransceiver*) + +DECLARE_CONVERTER(MediaStreamTrack*, rtc::scoped_refptr) +DECLARE_CONVERTER(MediaStreamTrack*, rtc::scoped_refptr) +DECLARE_FROM_JS(rtc::scoped_refptr) +DECLARE_FROM_JS(rtc::scoped_refptr) + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_INTERFACES_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/object.h b/deps/exokit-bindings/webrtc/src/converters/object.h new file mode 100644 index 0000000000..5b7a2e85b9 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/object.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines functions for decomposing v8::Objects. + */ + +#ifndef SRC_CONVERTERS_OBJECT_H_ +#define SRC_CONVERTERS_OBJECT_H_ + +#include "nan.h" + +#include "src/converters.h" +#include "src/functional/validation.h" + +namespace node_webrtc { + +template +static Validation GetRequired(const v8::Local object, const std::string& property) { + return From(object->Get(Nan::New(property).ToLocalChecked())); +} + +template +static Validation> GetOptional(const v8::Local object, const std::string& property) { + auto value = object->Get(Nan::New(property).ToLocalChecked()); + if (value->IsUndefined()) { + return Pure(Maybe::Nothing()); + } + return From(value).Map(&Maybe::Just); +} + +template +static Validation GetOptional( + const v8::Local object, + const std::string& property, + T default_value) { + return GetOptional(object, property).Map( + [default_value](const Maybe maybeT) { return maybeT.FromMaybe(default_value); }); +} + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_OBJECT_H_ diff --git a/deps/exokit-bindings/webrtc/src/converters/v8-converters.cc b/deps/exokit-bindings/webrtc/src/converters/v8-converters.cc new file mode 100644 index 0000000000..b93c643fd5 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/v8-converters.cc @@ -0,0 +1,213 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/converters/v8-converters.h" + +TO_JS_IMPL(node_webrtc::SomeError, someError) { + Nan::EscapableHandleScope scope; + auto message = someError.message(); + return node_webrtc::Pure(scope.Escape(someError.name().FromEither>( + [message](node_webrtc::ErrorFactory::DOMExceptionName name) { + switch (name) { + case node_webrtc::ErrorFactory::DOMExceptionName::kInvalidAccessError: + return node_webrtc::ErrorFactory::CreateInvalidAccessError(message); + case node_webrtc::ErrorFactory::DOMExceptionName::kInvalidModificationError: + return node_webrtc::ErrorFactory::CreateInvalidModificationError(message); + case node_webrtc::ErrorFactory::DOMExceptionName::kInvalidStateError: + return node_webrtc::ErrorFactory::CreateInvalidStateError(message); + case node_webrtc::ErrorFactory::DOMExceptionName::kNetworkError: + return node_webrtc::ErrorFactory::CreateNetworkError(message); + case node_webrtc::ErrorFactory::DOMExceptionName::kOperationError: + return node_webrtc::ErrorFactory::CreateOperationError(message); + } + }, [message](node_webrtc::ErrorFactory::ErrorName name) { + switch (name) { + case node_webrtc::ErrorFactory::ErrorName::kError: + return node_webrtc::ErrorFactory::CreateError(message); + case node_webrtc::ErrorFactory::ErrorName::kRangeError: + return node_webrtc::ErrorFactory::CreateRangeError(message); + case node_webrtc::ErrorFactory::ErrorName::kSyntaxError: + return node_webrtc::ErrorFactory::CreateSyntaxError(message); + } + }))); +} + +FROM_JS_IMPL(node_webrtc::Null, value) { + return value->IsNull() + ? node_webrtc::Pure(node_webrtc::Null()) + : node_webrtc::Validation::Invalid("Expected null"); +} + +TO_JS_IMPL(node_webrtc::Undefined, value) { + (void) value; + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(Nan::Undefined().As())); +} + +FROM_JS_IMPL(bool, value) { + auto maybeBoolean = Nan::To(value); + if (maybeBoolean.IsEmpty()) { + return Validation::Invalid("Expected a bool"); + } + auto boolean = (*maybeBoolean.ToLocalChecked())->Value(); + return Pure(boolean); +} + +TO_JS_IMPL(bool, value) { + Nan::EscapableHandleScope scope; + return value + ? node_webrtc::Pure(scope.Escape(Nan::True()).As()) + : node_webrtc::Pure(scope.Escape(Nan::False()).As()); +} + +FROM_JS_IMPL(double, value) { + auto maybeNumber = Nan::To(value); + if (maybeNumber.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a double"); + } + auto number = (*maybeNumber.ToLocalChecked())->Value(); + return node_webrtc::Pure(number); +} + +TO_JS_IMPL(double, value) { + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(Nan::New(value)).As()); +} + +FROM_JS_IMPL(int32_t, value) { + auto maybeInt32 = Nan::To(value); + if (maybeInt32.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a 32-bit integer"); + } + auto int32 = (*maybeInt32.ToLocalChecked())->Value(); + return node_webrtc::Pure(int32); +} + +TO_JS_IMPL(int32_t, value) { + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(Nan::New(value)).As()); +} + +FROM_JS_IMPL(int64_t, value) { + auto maybeInteger = Nan::To(value); + if (maybeInteger.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a 64-bit integer"); + } + auto integer = (*maybeInteger.ToLocalChecked())->Value(); + return node_webrtc::Pure(integer); +} + +TO_JS_IMPL(int64_t, value) { + Nan::EscapableHandleScope scope; + // NOTE(mroberts): Is this correct? + return node_webrtc::Pure(scope.Escape(Nan::New(static_cast(value))).As()); +} + +FROM_JS_IMPL(uint8_t, value) { + auto maybeInt32 = Nan::To(value); + if (maybeInt32.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected an 8-bit unsigned integer"); + } + auto int32 = (*maybeInt32.ToLocalChecked())->Value(); + if (int32 < 0 || int32 > 255) { + return node_webrtc::Validation::Invalid("Expected an 8-bit unsigned integer"); + } + auto uint8 = static_cast(int32); + return node_webrtc::Validation(uint8); +} + +FROM_JS_IMPL(uint16_t, value) { + auto maybeInt32 = Nan::To(value); + if (maybeInt32.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a 16-bit unsigned integer"); + } + auto int32 = (*maybeInt32.ToLocalChecked())->Value(); + if (int32 < 0 || int32 > 65535) { + return node_webrtc::Validation::Invalid("Expected a 16-bit unsigned integer"); + } + auto uint16 = static_cast(int32); + return node_webrtc::Validation(uint16); +} + +FROM_JS_IMPL(uint32_t, value) { + auto maybeUint32 = Nan::To(value); + if (maybeUint32.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a 32-bit unsigned integer"); + } + auto uint32 = (*maybeUint32.ToLocalChecked())->Value(); + return node_webrtc::Pure(uint32); +} + +TO_JS_IMPL(uint32_t, value) { + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(Nan::New(value)).As()); +} + +TO_JS_IMPL(uint64_t, value) { + Nan::EscapableHandleScope scope; + // NOTE(mroberts): Is this correct? + return node_webrtc::Pure(scope.Escape(Nan::New(static_cast(value))).As()); +} + +FROM_JS_IMPL(std::string, value) { + auto maybeString = Nan::To(value); + if (maybeString.IsEmpty()) { + return node_webrtc::Validation::Invalid("Expected a string"); + } + auto string = std::string(*Nan::Utf8String(maybeString.ToLocalChecked())); + return node_webrtc::Pure(string); +} + +TO_JS_IMPL(std::string, value) { + Nan::EscapableHandleScope scope; + return node_webrtc::Pure(scope.Escape(Nan::New(value).ToLocalChecked()).As()); +} + +FROM_JS_IMPL(v8::Local, value) { + Nan::EscapableHandleScope scope; + return !value.IsEmpty() && value->IsExternal() + ? node_webrtc::Pure(scope.Escape(value).As()) + : node_webrtc::Validation>::Invalid("Expected an external"); +} + +FROM_JS_IMPL(v8::Local, value) { + if (!value->IsFunction()) { + return node_webrtc::Validation>::Invalid("Expected a function"); + } + auto function = v8::Local::Cast(value); + return node_webrtc::Pure(function); +} + +FROM_JS_IMPL(v8::Local, value) { + Nan::EscapableHandleScope scope; + auto maybeObject = Nan::To(value); + if (maybeObject.IsEmpty()) { + return node_webrtc::Validation>::Invalid("Expected an object"); + } + auto object = maybeObject.ToLocalChecked(); + return node_webrtc::Pure(scope.Escape(object)); +} + +TO_JS_IMPL(std::vector, values) { + Nan::EscapableHandleScope scope; + auto array = Nan::New(); + uint32_t i = 0; + for (bool value : values) { + auto maybeValue = node_webrtc::From>(value); + if (maybeValue.IsInvalid()) { + return node_webrtc::Validation>::Invalid(maybeValue.ToErrors()); + } + array->Set(i++, maybeValue.UnsafeFromValid()); + } + return node_webrtc::Pure(scope.Escape(array).As()); +} + +FROM_JS_IMPL(v8::ArrayBuffer::Contents, value) { + return value->IsArrayBufferView() + ? node_webrtc::Pure(value.As()->Buffer()->GetContents()) + : node_webrtc::Validation::Invalid("Expected an ArrayBuffer"); +} diff --git a/deps/exokit-bindings/webrtc/src/converters/v8-converters.h b/deps/exokit-bindings/webrtc/src/converters/v8-converters.h new file mode 100644 index 0000000000..f951e28d5f --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/converters/v8-converters.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines conversion functions between native and v8 data types. + */ + +#ifndef SRC_CONVERTERS_V8_H_ +#define SRC_CONVERTERS_V8_H_ + +#include +#include + +#include // IWYU pragma: keep +#include + +#include "src/converters.h" +#include "src/errorfactory.h" +#include "src/functional/either.h" +#include "src/functional/maybe.h" +#include "src/functional/validation.h" + +namespace node_webrtc { + +// TODO(mroberts): The following could probably all be moved into a v8.cc file. + +class SomeError { + public: + SomeError() {} + + explicit SomeError(const std::string& message) + : SomeError(message, MakeRight(ErrorFactory::kError)) {} + + SomeError(const std::string& message, const Either name) + : _message(message), _name(name) {} + + std::string message() const { + return _message; + } + + Either name() const { + return _name; + } + + private: + std::string _message; + Either _name; +}; + +DECLARE_TO_JS(SomeError) + +class Null { + public: + Null() {} +}; + +DECLARE_FROM_JS(Null) + +class Undefined { + public: + Undefined() {} +}; + +DECLARE_TO_JS(Undefined) + +DECLARE_TO_AND_FROM_JS(bool) +DECLARE_TO_AND_FROM_JS(double) +DECLARE_TO_AND_FROM_JS(int32_t) +DECLARE_TO_AND_FROM_JS(int64_t) +DECLARE_FROM_JS(uint8_t) +DECLARE_FROM_JS(uint16_t) +DECLARE_TO_AND_FROM_JS(uint32_t) +DECLARE_TO_JS(uint64_t) +DECLARE_TO_AND_FROM_JS(std::string) +DECLARE_FROM_JS(v8::Local) +DECLARE_FROM_JS(v8::Local) +DECLARE_FROM_JS(v8::Local) +DECLARE_TO_JS(std::vector) +DECLARE_FROM_JS(v8::ArrayBuffer::Contents) + +template +struct Converter, Maybe> { + static Validation> Convert(const v8::Local value) { + if (value.IsEmpty() || value->IsUndefined()) { + return Pure(Maybe::Nothing()); + } + return From(value).Map(&Maybe::Just); + } +}; + +template +struct Converter, v8::Local> { + static Validation> Convert(const Maybe value) { + return value.IsJust() + ? From>(value.UnsafeFromJust()) + : node_webrtc::Pure(Nan::Null().As()); + } +}; + +template +struct Converter, std::vector> { + static Validation> Convert(const v8::Local value) { + if (!value->IsArray()) { + return Validation>::Invalid("Expected an array"); + } + auto array = v8::Local::Cast(value); + auto validated = std::vector>(); + for (uint32_t i = 0; i < array->Length(); i++) { + validated.push_back(From(array->Get(i))); + } + return Validation::Sequence(validated); + } +}; + +template +struct Converter, v8::Local> { + static Validation> Convert(const std::vector& values) { + Nan::EscapableHandleScope scope; + auto array = Nan::New(); + uint32_t i = 0; + for (auto value : values) { + auto maybeValue = From>(value); + if (maybeValue.IsInvalid()) { + return Validation>::Invalid(maybeValue.ToErrors()); + } + array->Set(i++, maybeValue.UnsafeFromValid()); + } + return Pure(scope.Escape(array).As()); + } +}; + +} // namespace node_webrtc + +#endif // SRC_CONVERTERS_V8_H_ diff --git a/deps/exokit-bindings/webrtc/src/create-answer-observer.cc b/deps/exokit-bindings/webrtc/src/create-answer-observer.cc deleted file mode 100644 index 336b0befd2..0000000000 --- a/deps/exokit-bindings/webrtc/src/create-answer-observer.cc +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "create-answer-observer.h" - -#include "common.h" -#include "peerconnection.h" - -using node_webrtc::CreateAnswerObserver; -using node_webrtc::PeerConnection; - -void CreateAnswerObserver::OnSuccess(webrtc::SessionDescriptionInterface* sdp) { - TRACE_CALL; - PeerConnection::SdpEvent* data = new PeerConnection::SdpEvent(sdp); - parent->QueueEvent(PeerConnection::CREATE_ANSWER_SUCCESS, static_cast(data)); - TRACE_END; -} - -void CreateAnswerObserver::OnFailure(const std::string& msg) { - TRACE_CALL; - PeerConnection::ErrorEvent* data = new PeerConnection::ErrorEvent(msg); - parent->QueueEvent(PeerConnection::CREATE_ANSWER_ERROR, static_cast(data)); - TRACE_END; -} diff --git a/deps/exokit-bindings/webrtc/src/create-offer-observer.cc b/deps/exokit-bindings/webrtc/src/create-offer-observer.cc deleted file mode 100644 index a620503948..0000000000 --- a/deps/exokit-bindings/webrtc/src/create-offer-observer.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "create-offer-observer.h" - -#include "common.h" -#include "peerconnection.h" - -using node_webrtc::CreateOfferObserver; -using node_webrtc::PeerConnection; - -void CreateOfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* sdp) { - TRACE_CALL; - PeerConnection::SdpEvent* data = new PeerConnection::SdpEvent(sdp); - parent->QueueEvent(PeerConnection::CREATE_OFFER_SUCCESS, static_cast(data)); - TRACE_END; -} - -void CreateOfferObserver::OnFailure(webrtc::RTCError error) { - TRACE_CALL; - const char *msg = error.message(); - PeerConnection::ErrorEvent* data = new PeerConnection::ErrorEvent(msg); - parent->QueueEvent(PeerConnection::CREATE_OFFER_ERROR, static_cast(data)); - TRACE_END; -} - -void CreateOfferObserver::OnFailure(const std::string& msg) { - TRACE_CALL; - PeerConnection::ErrorEvent* data = new PeerConnection::ErrorEvent(msg); - parent->QueueEvent(PeerConnection::CREATE_OFFER_ERROR, static_cast(data)); - TRACE_END; -} diff --git a/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.cc b/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.cc new file mode 100644 index 0000000000..4f674a36a1 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.cc @@ -0,0 +1,55 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/createsessiondescriptionobserver.h" + +#include +#include + +#include "src/common.h" +#include "src/converters.h" +#include "src/converters/dictionaries.h" +#include "src/error.h" // IWYU pragma: keep +#include "src/functional/validation.h" +#include "src/peerconnection.h" // IWYU pragma: keep + +// IWYU pragma: no_forward_declare node_webrtc::SomeError + +using node_webrtc::CreateSessionDescriptionObserver; +using node_webrtc::Errors; +using node_webrtc::From; +using node_webrtc::PeerConnection; +using node_webrtc::SomeError; + +void CreateSessionDescriptionObserver::OnSuccess(webrtc::SessionDescriptionInterface* sdp) { + TRACE_CALL; + if (_promise) { + auto validation = From(sdp); + if (validation.IsInvalid()) { + _promise->Reject(SomeError(validation.ToErrors()[0])); + } else { + auto description = validation.UnsafeFromValid(); + parent->SaveLastSdp(description); + _promise->Resolve(description); + } + parent->Dispatch(std::move(_promise)); + } + delete sdp; + TRACE_END; +} + +void CreateSessionDescriptionObserver::OnFailure(const webrtc::RTCError error) { + TRACE_CALL; + if (_promise) { + auto someError = From(&error).FromValidation([](Errors errors) { + return SomeError(errors[0]); + }); + _promise->Reject(someError); + parent->Dispatch(std::move(_promise)); + } + TRACE_END; +} diff --git a/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.h b/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.h new file mode 100644 index 0000000000..b8c92f794f --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/createsessiondescriptionobserver.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ +#define SRC_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ + +#ifdef _WIN32 +#include +#endif + +#include + +#include "src/events.h" // IWYU pragma: keep + +namespace webrtc { + +class RTCError; + +} // namespace webrtc + +namespace node_webrtc { + +struct RTCSessionDescriptionInit; // IWYU pragma: keep +class PeerConnection; + +class CreateSessionDescriptionObserver + : public webrtc::CreateSessionDescriptionObserver { + private: + PeerConnection* parent; + std::unique_ptr> _promise; + + public: + CreateSessionDescriptionObserver( + PeerConnection* parent, + std::unique_ptr> promise) + : parent(parent), _promise(std::move(promise)) {} + + virtual void OnSuccess(webrtc::SessionDescriptionInterface* sdp); + virtual void OnFailure(webrtc::RTCError error); +}; + +} // namespace node_webrtc + +#endif // SRC_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ diff --git a/deps/exokit-bindings/webrtc/src/datachannel.cc b/deps/exokit-bindings/webrtc/src/datachannel.cc index c3d4686a74..5624f79f0d 100644 --- a/deps/exokit-bindings/webrtc/src/datachannel.cc +++ b/deps/exokit-bindings/webrtc/src/datachannel.cc @@ -1,18 +1,31 @@ -/* Copyright (c) 2018 The node-webrtc project authors. All rights reserved. +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. * * Use of this source code is governed by a BSD-style license that can be found * in the LICENSE.md file in the root of the source tree. All contributing * project authors may be found in the AUTHORS file in the root of the source * tree. */ -#include "datachannel.h" +#include "src/datachannel.h" -#include +#include +#include +#include +#include -#include "common.h" +#include "src/common.h" +#include "src/error.h" +#include "src/errorfactory.h" // IWYU pragma: keep +#include "src/events.h" +using node_webrtc::AsyncObjectWrapWithLoop; using node_webrtc::DataChannel; using node_webrtc::DataChannelObserver; +using node_webrtc::DataChannelStateChangeEvent; +using node_webrtc::ErrorEvent; +using node_webrtc::ErrorFactory; +using node_webrtc::Event; +using node_webrtc::MessageEvent; +using node_webrtc::StateEvent; using v8::External; using v8::Function; using v8::FunctionTemplate; @@ -23,87 +36,68 @@ using v8::Object; using v8::String; using v8::Value; -Nan::Persistent DataChannel::constructor; -#if NODE_MODULE_VERSION < 0x000C -Nan::Persistent DataChannel::ArrayBufferConstructor; -#endif +Nan::Persistent& DataChannel::constructor() { + static Nan::Persistent constructor; + return constructor; +} DataChannelObserver::DataChannelObserver(std::shared_ptr factory, - rtc::scoped_refptr jingleDataChannel) { + rtc::scoped_refptr jingleDataChannel) + : EventQueue() + , _factory(factory) + , _jingleDataChannel(jingleDataChannel) { TRACE_CALL; - uv_mutex_init(&lock); - _factory = factory; - _jingleDataChannel = jingleDataChannel; _jingleDataChannel->RegisterObserver(this); TRACE_END; } -DataChannelObserver::~DataChannelObserver() { - _factory = nullptr; - _jingleDataChannel = nullptr; - uv_mutex_destroy(&lock); -} - void DataChannelObserver::OnStateChange() { TRACE_CALL; - DataChannel::StateEvent* data = new DataChannel::StateEvent(_jingleDataChannel->state()); - QueueEvent(DataChannel::STATE, static_cast(data)); + Enqueue(DataChannelStateChangeEvent::Create(_jingleDataChannel->state())); TRACE_END; } void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) { TRACE_CALL; - DataChannel::MessageEvent* data = new DataChannel::MessageEvent(&buffer); - QueueEvent(DataChannel::MESSAGE, static_cast(data)); + Enqueue(MessageEvent::Create(&buffer)); TRACE_END; } -void DataChannelObserver::QueueEvent(DataChannel::AsyncEventType type, void* data) { - TRACE_CALL; - DataChannel::AsyncEvent evt; - evt.type = type; - evt.data = data; - uv_mutex_lock(&lock); - _events.push(evt); - uv_mutex_unlock(&lock); - TRACE_END; +static void requeue(DataChannelObserver& observer, DataChannel& channel) { + while (auto event = observer.Dequeue()) { + channel.Dispatch(std::move(event)); + } } DataChannel::DataChannel(node_webrtc::DataChannelObserver* observer) - : loop(uv_default_loop()), - _binaryType(DataChannel::ARRAY_BUFFER) { - uv_mutex_init(&lock); - uv_async_init(loop, &async, reinterpret_cast(Run)); - - _factory = observer->_factory; - _jingleDataChannel = observer->_jingleDataChannel; + : AsyncObjectWrapWithLoop("RTCDataChannel", *this) + , _binaryType(BinaryType::kArrayBuffer) + , _factory(observer->_factory) + , _jingleDataChannel(observer->_jingleDataChannel) { _jingleDataChannel->RegisterObserver(this); - async.data = this; - // Re-queue cached observer events - while (true) { - uv_mutex_lock(&observer->lock); - bool empty = observer->_events.empty(); - if (empty) { - uv_mutex_unlock(&observer->lock); - break; - } - AsyncEvent evt = observer->_events.front(); - observer->_events.pop(); - uv_mutex_unlock(&observer->lock); - QueueEvent(evt.type, evt.data); - } + requeue(*observer, *this); delete observer; } DataChannel::~DataChannel() { - TRACE_CALL; - _factory = nullptr; - _jingleDataChannel = nullptr; - uv_mutex_destroy(&lock); - TRACE_END; + wrap()->Release(this); +} + +void DataChannel::OnPeerConnectionClosed() { + if (_jingleDataChannel != nullptr) { + _jingleDataChannel->UnregisterObserver(); + _cached_id = _jingleDataChannel->id(); + _cached_label = _jingleDataChannel->label(); + _cached_max_retransmits = _jingleDataChannel->maxRetransmits(); + _cached_ordered = _jingleDataChannel->ordered(); + _cached_protocol = _jingleDataChannel->protocol(); + _cached_buffered_amount = _jingleDataChannel->buffered_amount(); + _jingleDataChannel = nullptr; + Stop(); + } } NAN_METHOD(DataChannel::New) { @@ -114,123 +108,73 @@ NAN_METHOD(DataChannel::New) { } Local _observer = Local::Cast(info[0]); - node_webrtc::DataChannelObserver* observer = static_cast(_observer->Value()); + auto observer = static_cast(_observer->Value()); - DataChannel* obj = new DataChannel(observer); + auto obj = new DataChannel(observer); obj->Wrap(info.This()); - obj->Ref(); TRACE_END; info.GetReturnValue().Set(info.This()); } -void DataChannel::QueueEvent(AsyncEventType type, void* data) { +void DataChannel::HandleErrorEvent(const ErrorEvent& event) { TRACE_CALL; - AsyncEvent evt; - evt.type = type; - evt.data = data; - uv_mutex_lock(&lock); - _events.push(evt); - uv_mutex_unlock(&lock); - - uv_async_send(&async); + Nan::HandleScope scope; + Local argv[1]; + argv[0] = Nan::Error(Nan::New(event.msg).ToLocalChecked()); + MakeCallback("onerror", 1, argv); TRACE_END; } -/*NAN_WEAK_CALLBACK(MessageWeakCallback) -{ - P* parameter = data.GetParameter(); - delete[] parameter->message; - parameter->message = nullptr; - delete parameter; - //Nan::AdjustExternalMemory(-parameter->size); -}*/ - -void DataChannel::Run(uv_async_t* handle, int status) { +void DataChannel::HandleStateEvent(const DataChannelStateChangeEvent& event) { + TRACE_CALL; Nan::HandleScope scope; - - auto self = static_cast(handle->data); - TRACE_CALL_P((uintptr_t)self); - auto do_shutdown = false; - - while (true) { - auto dc = self->handle(); - - uv_mutex_lock(&self->lock); - bool empty = self->_events.empty(); - if (empty) { - uv_mutex_unlock(&self->lock); - break; - } - AsyncEvent evt = self->_events.front(); - self->_events.pop(); - uv_mutex_unlock(&self->lock); - - TRACE_U("evt.type", evt.type); - if (DataChannel::ERROR & evt.type) { - DataChannel::ErrorEvent* data = static_cast(evt.data); - Local callback = Local::Cast(dc->Get(Nan::New("onerror").ToLocalChecked())); - Local argv[1]; - argv[0] = Nan::Error(data->msg.c_str()); - Nan::MakeCallback(dc, callback, 1, argv); - } else if (DataChannel::STATE & evt.type) { - StateEvent* data = static_cast(evt.data); - Local callback = Local::Cast(dc->Get(Nan::New("onstatechange").ToLocalChecked())); - Local argv[1]; - Local state = Nan::New((data->state)); - argv[0] = state; - Nan::MakeCallback(dc, callback, 1, argv); - - if (data->state == webrtc::DataChannelInterface::kClosed) { - do_shutdown = true; - } - } else if (DataChannel::MESSAGE & evt.type) { - MessageEvent* data = static_cast(evt.data); - Local callback = Local::Cast(dc->Get(Nan::New("onmessage").ToLocalChecked())); - - Local argv[1]; - - if (data->binary) { -#if NODE_MODULE_VERSION > 0x000B - Local array = v8::ArrayBuffer::New( - v8::Isolate::GetCurrent(), data->message.get(), data->size); -#else - Local array = Nan::New(ArrayBufferConstructor)->NewInstance(); - array->SetIndexedPropertiesToExternalArrayData( - data->message.get(), v8::kExternalByteArray, data->size); - array->ForceSet(Nan::New("byteLength").ToLocalChecked(), Nan::New(static_cast(data->size))); -#endif - // NanMakeWeakPersistent(callback, data, &MessageWeakCallback); - - argv[0] = array; - Nan::MakeCallback(dc, callback, 1, argv); - } else { - Local str = Nan::New(data->message.get(), data->size).ToLocalChecked(); - - // cleanup message event - delete data; - - argv[0] = str; - Nan::MakeCallback(dc, callback, 1, argv); - } - } + Local argv[1]; + if (event.state == webrtc::DataChannelInterface::kClosed) { + argv[0] = Nan::New("closed").ToLocalChecked(); + MakeCallback("onstatechange", 1, argv); + } else if (event.state == webrtc::DataChannelInterface::kOpen) { + argv[0] = Nan::New("open").ToLocalChecked(); + MakeCallback("onstatechange", 1, argv); } - if (do_shutdown) { - self->async.data = nullptr; - self->Unref(); - uv_close(reinterpret_cast(&self->async), nullptr); + if (event.state == webrtc::DataChannelInterface::kClosed) { + Stop(); } + TRACE_END; +} +void DataChannel::HandleMessageEvent(MessageEvent& event) { + TRACE_CALL; + Nan::HandleScope scope; + Local argv[1]; + if (event.binary) { + Local array = v8::ArrayBuffer::New( + v8::Isolate::GetCurrent(), + event.message.release(), + event.size, + v8::ArrayBufferCreationMode::kInternalized); + argv[0] = array; + } else { + Local str = Nan::New(event.message.get(), event.size).ToLocalChecked(); + argv[0] = str; + } + MakeCallback("onmessage", 1, argv); TRACE_END; } void DataChannel::OnStateChange() { TRACE_CALL; - StateEvent* data = new StateEvent(_jingleDataChannel->state()); - QueueEvent(DataChannel::STATE, static_cast(data)); - if (_jingleDataChannel->state() == webrtc::DataChannelInterface::kClosed) { + auto state = _jingleDataChannel->state(); + Dispatch(DataChannelStateChangeEvent::Create(state)); + if (state == webrtc::DataChannelInterface::kClosed) { _jingleDataChannel->UnregisterObserver(); + _cached_id = _jingleDataChannel->id(); + _cached_label = _jingleDataChannel->label(); + _cached_max_retransmits = _jingleDataChannel->maxRetransmits(); + _cached_ordered = _jingleDataChannel->ordered(); + _cached_protocol = _jingleDataChannel->protocol(); + _cached_buffered_amount = _jingleDataChannel->buffered_amount(); _jingleDataChannel = nullptr; } TRACE_END; @@ -238,17 +182,20 @@ void DataChannel::OnStateChange() { void DataChannel::OnMessage(const webrtc::DataBuffer& buffer) { TRACE_CALL; - MessageEvent* data = new MessageEvent(&buffer); - QueueEvent(DataChannel::MESSAGE, static_cast(data)); + Dispatch(MessageEvent::Create(&buffer)); TRACE_END; } NAN_METHOD(DataChannel::Send) { TRACE_CALL; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.This()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.This()); if (self->_jingleDataChannel != nullptr) { + if (self->_jingleDataChannel->state() != webrtc::DataChannelInterface::DataState::kOpen) { + TRACE_END; + return Nan::ThrowError(ErrorFactory::CreateInvalidStateError("RTCDataChannel.readyState is not 'open'")); + } if (info[0]->IsString()) { Local str = Local::Cast(info[0]); std::string data = *Nan::Utf8String(str); @@ -269,7 +216,8 @@ NAN_METHOD(DataChannel::Send) { arraybuffer = Local::Cast(info[0]); byte_length = arraybuffer->ByteLength(); } else { - // TODO(mroberts): Throw a TypeError. + TRACE_END; + return Nan::ThrowTypeError("Expected a Blob or ArrayBuffer"); } v8::ArrayBuffer::Contents content = arraybuffer->GetContents(); @@ -280,87 +228,189 @@ NAN_METHOD(DataChannel::Send) { webrtc::DataBuffer data_buffer(buffer, true); self->_jingleDataChannel->Send(data_buffer); } + } else { + TRACE_END; + return Nan::ThrowError(ErrorFactory::CreateInvalidStateError("RTCDataChannel.readyState is not 'open'")); } TRACE_END; - return; } NAN_METHOD(DataChannel::Close) { TRACE_CALL; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.This()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.This()); if (self->_jingleDataChannel != nullptr) { self->_jingleDataChannel->Close(); } TRACE_END; - return; } NAN_GETTER(DataChannel::GetBufferedAmount) { TRACE_CALL; + (void) property; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.Holder()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); uint64_t buffered_amount = self->_jingleDataChannel != nullptr ? self->_jingleDataChannel->buffered_amount() - : 0; + : self->_cached_buffered_amount; TRACE_END; info.GetReturnValue().Set(Nan::New(buffered_amount)); } +NAN_GETTER(DataChannel::GetId) { + TRACE_CALL; + (void) property; + + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + auto id = self->_jingleDataChannel + ? self->_jingleDataChannel->id() + : self->_cached_id; + + TRACE_END; + info.GetReturnValue().Set(Nan::New(id)); +} + NAN_GETTER(DataChannel::GetLabel) { TRACE_CALL; + (void) property; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.Holder()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); std::string label = self->_jingleDataChannel != nullptr ? self->_jingleDataChannel->label() - : ""; + : self->_cached_label; TRACE_END; info.GetReturnValue().Set(Nan::New(label).ToLocalChecked()); } +NAN_GETTER(DataChannel::GetMaxRetransmits) { + TRACE_CALL; + (void) property; + + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + auto max_retransmits = self->_jingleDataChannel + ? self->_jingleDataChannel->maxRetransmits() + : self->_cached_max_retransmits; + + TRACE_END; + info.GetReturnValue().Set(Nan::New(max_retransmits)); +} + +NAN_GETTER(DataChannel::GetOrdered) { + TRACE_CALL; + (void) property; + + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + auto ordered = self->_jingleDataChannel + ? self->_jingleDataChannel->ordered() + : self->_cached_ordered; + + TRACE_END; + info.GetReturnValue().Set(Nan::New(ordered)); +} + +NAN_GETTER(DataChannel::GetPriority) { + TRACE_CALL; + (void) property; + + TRACE_END; + info.GetReturnValue().Set(Nan::New("high").ToLocalChecked()); +} + +NAN_GETTER(DataChannel::GetProtocol) { + TRACE_CALL; + (void) property; + + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + auto protocol = self->_jingleDataChannel + ? self->_jingleDataChannel->protocol() + : self->_cached_protocol; + + TRACE_END; + info.GetReturnValue().Set(Nan::New(protocol).ToLocalChecked()); +} + NAN_GETTER(DataChannel::GetReadyState) { TRACE_CALL; + (void) property; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.Holder()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); - webrtc::DataChannelInterface::DataState state = self->_jingleDataChannel != nullptr + CONVERT_OR_THROW_AND_RETURN(self->_jingleDataChannel ? self->_jingleDataChannel->state() - : webrtc::DataChannelInterface::kClosed; + : webrtc::DataChannelInterface::kClosed, + state, + Local); TRACE_END; - info.GetReturnValue().Set(Nan::New(static_cast(state))); + info.GetReturnValue().Set(state); } NAN_GETTER(DataChannel::GetBinaryType) { TRACE_CALL; + (void) property; - DataChannel* self = Nan::ObjectWrap::Unwrap(info.Holder()); + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + CONVERT_OR_THROW_AND_RETURN(self->_binaryType, binaryType, Local); TRACE_END; - info.GetReturnValue().Set(Nan::New(static_cast(self->_binaryType))); + info.GetReturnValue().Set(binaryType); } NAN_SETTER(DataChannel::SetBinaryType) { TRACE_CALL; + (void) property; + + auto self = AsyncObjectWrapWithLoop::Unwrap(info.Holder()); + + CONVERT_OR_THROW_AND_RETURN(value, binaryType, BinaryType); - DataChannel* self = Nan::ObjectWrap::Unwrap(info.Holder()); - self->_binaryType = static_cast(TO_UINT32(value)); + self->_binaryType = binaryType; TRACE_END; } NAN_SETTER(DataChannel::ReadOnly) { - // INFO("PeerConnection::ReadOnly"); + (void) info; + (void) property; + (void) value; + INFO("PeerConnection::ReadOnly"); +} + +node_webrtc::Wrap < +DataChannel*, +rtc::scoped_refptr, +node_webrtc::DataChannelObserver* +> * DataChannel::wrap() { + static auto wrap = new node_webrtc::Wrap < + DataChannel*, + rtc::scoped_refptr, + node_webrtc::DataChannelObserver* + > (node_webrtc::DataChannel::Create); + return wrap; } -void DataChannel::Init(v8::Local exports) { +DataChannel* DataChannel::Create( + node_webrtc::DataChannelObserver* observer, + rtc::scoped_refptr) { + Nan::HandleScope scope; + Local cargv = Nan::New(static_cast(observer)); + auto channel = Nan::NewInstance(Nan::New(DataChannel::constructor()), 1, &cargv).ToLocalChecked(); + return AsyncObjectWrapWithLoop::Unwrap(channel); +} + +void DataChannel::Init(Local exports) { Local tpl = Nan::New(New); tpl->SetClassName(Nan::New("DataChannel").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); @@ -369,16 +419,15 @@ void DataChannel::Init(v8::Local exports) { Nan::SetPrototypeMethod(tpl, "send", Send); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("bufferedAmount").ToLocalChecked(), GetBufferedAmount, ReadOnly); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("id").ToLocalChecked(), GetId, ReadOnly); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("label").ToLocalChecked(), GetLabel, ReadOnly); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("maxRetransmits").ToLocalChecked(), GetMaxRetransmits, ReadOnly); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("ordered").ToLocalChecked(), GetOrdered, ReadOnly); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("priority").ToLocalChecked(), GetPriority, ReadOnly); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("protocol").ToLocalChecked(), GetProtocol, ReadOnly); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("binaryType").ToLocalChecked(), GetBinaryType, SetBinaryType); Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("readyState").ToLocalChecked(), GetReadyState, ReadOnly); - constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked()); + constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked()); exports->Set(Nan::New("DataChannel").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); - -#if NODE_MODULE_VERSION < 0x000C - Local global = Nan::GetCurrentContext()->Global(); - Local obj = global->Get(Nan::New("ArrayBuffer").ToLocalChecked()); - ArrayBufferConstructor.Reset(obj.As()); -#endif } diff --git a/deps/exokit-bindings/webrtc/src/datachannel.h b/deps/exokit-bindings/webrtc/src/datachannel.h new file mode 100644 index 0000000000..72a5de89f3 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/datachannel.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_DATACHANNEL_H_ +#define SRC_DATACHANNEL_H_ + +#include +#include + +#include +#include +#include + +#include "src/asyncobjectwrapwithloop.h" // IWYU pragma: keep +#include "src/converters/enums.h" +#include "src/eventqueue.h" // IWYU pragma: keep +#include "src/wrap.h" + +namespace node_webrtc { + +class DataChannelObserver; +class DataChannelStateChangeEvent; +class MessageEvent; +class PeerConnectionFactory; + +template class ErrorEvent; + +class DataChannel + : public node_webrtc::AsyncObjectWrapWithLoop + , public webrtc::DataChannelObserver { + friend class node_webrtc::DataChannelObserver; + public: + DataChannel() = delete; + + DataChannel(DataChannel const&) = delete; + + DataChannel& operator=(DataChannel const&) = delete; + + ~DataChannel() override; + + static void Init(v8::Local exports); + + // + // DataChannelObserver implementation. + // + void OnStateChange() override; + void OnMessage(const webrtc::DataBuffer& buffer) override; + + void HandleErrorEvent(const ErrorEvent& event); + void HandleStateEvent(const DataChannelStateChangeEvent& event); + void HandleMessageEvent(MessageEvent& event); + + void OnPeerConnectionClosed(); + + static ::node_webrtc::Wrap < + DataChannel*, + rtc::scoped_refptr, + node_webrtc::DataChannelObserver* + > * wrap(); + + private: + explicit DataChannel(node_webrtc::DataChannelObserver* observer); + + static DataChannel* Create( + node_webrtc::DataChannelObserver*, + rtc::scoped_refptr); + + static Nan::Persistent& constructor(); + + static NAN_METHOD(New); + + static NAN_METHOD(Send); + static NAN_METHOD(Close); + + static NAN_GETTER(GetBufferedAmount); + static NAN_GETTER(GetId); + static NAN_GETTER(GetLabel); + static NAN_GETTER(GetMaxRetransmits); + static NAN_GETTER(GetOrdered); + static NAN_GETTER(GetPriority); + static NAN_GETTER(GetProtocol); + static NAN_GETTER(GetBinaryType); + static NAN_GETTER(GetReadyState); + static NAN_SETTER(SetBinaryType); + static NAN_SETTER(ReadOnly); + + node_webrtc::BinaryType _binaryType; + int _cached_id; + std::string _cached_label; + uint16_t _cached_max_retransmits; + bool _cached_ordered; + std::string _cached_protocol; + uint64_t _cached_buffered_amount; + std::shared_ptr _factory; + rtc::scoped_refptr _jingleDataChannel; +}; + +class DataChannelObserver + : public node_webrtc::EventQueue + , public webrtc::DataChannelObserver { + friend class node_webrtc::DataChannel; + public: + explicit DataChannelObserver(std::shared_ptr factory, + rtc::scoped_refptr jingleDataChannel); + + void OnStateChange() override; + void OnMessage(const webrtc::DataBuffer& buffer) override; + + rtc::scoped_refptr channel() { return _jingleDataChannel; } + + private: + std::shared_ptr _factory; + rtc::scoped_refptr _jingleDataChannel; +}; + +} // namespace node_webrtc + +#endif // SRC_DATACHANNEL_H_ diff --git a/deps/exokit-bindings/webrtc/src/error.h b/deps/exokit-bindings/webrtc/src/error.h new file mode 100644 index 0000000000..a29c154b3c --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/error.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_ERROR_H_ +#define SRC_ERROR_H_ + +// https://stackoverflow.com/a/13842784 +template struct argument_type; +template struct argument_type { typedef U type; }; + +// https://stackoverflow.com/a/17624752 +#define NODE_WEBRTC_PP_CAT(a, b) NODE_WEBRTC_PP_CAT_I(a, b) +#define NODE_WEBRTC_PP_CAT_I(a, b) NODE_WEBRTC_PP_CAT_II(~, a ## b) +#define NODE_WEBRTC_PP_CAT_II(p, res) res +#define NODE_WEBRTC_UNIQUE_NAME(base) NODE_WEBRTC_PP_CAT(base, __LINE__) + +// https://stackoverflow.com/a/41566342 +#ifndef COMMA +#define COMMA , +#endif // COMMA + +#define CONVERT_ARGS_OR_THROW_AND_RETURN(O, T) \ + auto NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::Validation::type>::Invalid(std::vector()); \ + { \ + Nan::TryCatch tc; \ + NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::From::type>(node_webrtc::Arguments(info)); \ + if (tc.HasCaught()) { \ + tc.ReThrow(); \ + return; \ + } \ + } \ + if (NODE_WEBRTC_UNIQUE_NAME(validation).IsInvalid()) { \ + auto error = NODE_WEBRTC_UNIQUE_NAME(validation).ToErrors()[0]; \ + return Nan::ThrowTypeError(Nan::New(error).ToLocalChecked()); \ + } \ + auto O = NODE_WEBRTC_UNIQUE_NAME(validation).UnsafeFromValid(); + +#define CONVERT_OR_THROW_AND_RETURN(I, O, T) \ + auto NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::Validation::type>::Invalid(std::vector()); \ + { \ + Nan::TryCatch tc; \ + NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::From::type>(I); \ + if (tc.HasCaught()) { \ + tc.ReThrow(); \ + return; \ + } \ + } \ + if (NODE_WEBRTC_UNIQUE_NAME(validation).IsInvalid()) { \ + auto error = NODE_WEBRTC_UNIQUE_NAME(validation).ToErrors()[0]; \ + return Nan::ThrowTypeError(Nan::New(error).ToLocalChecked()); \ + } \ + auto O = NODE_WEBRTC_UNIQUE_NAME(validation).UnsafeFromValid(); + +#define CONVERT_ARGS_OR_REJECT_AND_RETURN(R, O, T) \ + auto NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::Validation::type>::Invalid(std::vector()); \ + { \ + Nan::TryCatch tc; \ + NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::From::type>(node_webrtc::Arguments(info)); \ + if (tc.HasCaught()) { \ + resolver->Resolve(Nan::GetCurrentContext(), tc.Exception()).IsNothing(); \ + return; \ + } \ + } \ + if (NODE_WEBRTC_UNIQUE_NAME(validation).IsInvalid()) { \ + auto error = NODE_WEBRTC_UNIQUE_NAME(validation).ToErrors()[0]; \ + resolver->Reject(Nan::GetCurrentContext(), Nan::TypeError(Nan::New(error).ToLocalChecked())).IsNothing(); \ + return; \ + } \ + auto O = NODE_WEBRTC_UNIQUE_NAME(validation).UnsafeFromValid(); + +#define CONVERT_OR_REJECT_AND_RETURN(R, I, O, T) \ + auto NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::Validation::type>::Invalid(std::vector()); \ + { \ + Nan::TryCatch tc; \ + NODE_WEBRTC_UNIQUE_NAME(validation) = node_webrtc::From::type>(I); \ + if (tc.HasCaught()) { \ + resolver->Resolve(Nan::GetCurrentContext(), tc.Exception()).IsNothing(); \ + return; \ + } \ + } \ + if (NODE_WEBRTC_UNIQUE_NAME(validation).IsInvalid()) { \ + auto error = NODE_WEBRTC_UNIQUE_NAME(validation).ToErrors()[0]; \ + resolver->Reject(Nan::GetCurrentContext(), Nan::TypeError(Nan::New(error).ToLocalChecked())).IsNothing(); \ + return; \ + } \ + auto O = NODE_WEBRTC_UNIQUE_NAME(validation).UnsafeFromValid(); + +#define SETUP_PROMISE(...) \ + auto NODE_WEBRTC_UNIQUE_NAME(pair) = PromiseEvent<__VA_ARGS__>::Create(); \ + auto resolver = NODE_WEBRTC_UNIQUE_NAME(pair).first; \ + auto promise = std::move(NODE_WEBRTC_UNIQUE_NAME(pair).second); \ + info.GetReturnValue().Set(resolver->GetPromise()); + +#endif // SRC_ERROR_H_ diff --git a/deps/exokit-bindings/webrtc/src/errorfactory.cc b/deps/exokit-bindings/webrtc/src/errorfactory.cc new file mode 100644 index 0000000000..d06e04f452 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/errorfactory.cc @@ -0,0 +1,123 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/errorfactory.h" + +#include + +#include "src/converters.h" +#include "src/converters/object.h" +#include "src/converters/v8-converters.h" // IWYU pragma: keep + +namespace node_webrtc { + +template class Maybe; +template class Validation; + +} // namespace node_webrtc + +using node_webrtc::ErrorFactory; +using node_webrtc::From; +using node_webrtc::GetRequired; +using node_webrtc::Maybe; +using node_webrtc::Validation; +using v8::Function; +using v8::Local; +using v8::Object; +using v8::Value; + +Nan::Persistent ErrorFactory::DOMException; + +void ErrorFactory::Init(Local module) { + Nan::TryCatch tc; + auto maybeRequire = GetRequired>(module, "require"); + if (tc.HasCaught() || maybeRequire.IsInvalid()) { + return; + } + auto require = maybeRequire.UnsafeFromValid(); + Local argv = Nan::New("domexception").ToLocalChecked(); + auto result = Nan::Call(require, module, 1, &argv); + if (tc.HasCaught() || result.IsEmpty()) { + return; + } + auto maybeDOMException = From>(result.ToLocalChecked()); + if (tc.HasCaught() || maybeDOMException.IsInvalid()) { + return; + } + DOMException.Reset(maybeDOMException.UnsafeFromValid()); +} + +Local ErrorFactory::CreateError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::Error(Nan::New(message).ToLocalChecked())); +} + +Local ErrorFactory::CreateInvalidAccessError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(CreateDOMException(message, kInvalidAccessError)); +} + +Local ErrorFactory::CreateInvalidModificationError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(CreateDOMException(message, kInvalidModificationError)); +} + +Local ErrorFactory::CreateInvalidStateError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(CreateDOMException(message, kInvalidStateError)); +} + +Local ErrorFactory::CreateNetworkError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(CreateDOMException(message, kNetworkError)); +} + +Local ErrorFactory::CreateOperationError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(CreateDOMException(message, kOperationError)); +} + +Local ErrorFactory::CreateRangeError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::RangeError(Nan::New(message).ToLocalChecked())); +} + +Local ErrorFactory::CreateSyntaxError(const std::string message) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::SyntaxError(Nan::New(message).ToLocalChecked())); +} + +const char* ErrorFactory::DOMExceptionNameToString(DOMExceptionName name) { + switch (name) { + case kInvalidAccessError: + return "InvalidAccessError"; + case kInvalidModificationError: + return "InvalidModificationError"; + case kInvalidStateError: + return "InvalidStateError"; + case kNetworkError: + return "NetworkError"; + case kOperationError: + return "OperationError"; + } +} + +Local ErrorFactory::CreateDOMException(const std::string message, const DOMExceptionName name) { + Nan::EscapableHandleScope scope; + auto prefix = DOMExceptionNameToString(name); + if (!DOMException.IsEmpty()) { + auto constructor = Nan::New(DOMException); + Local cargv[2]; + cargv[0] = Nan::New(message).ToLocalChecked(); + cargv[1] = Nan::New(prefix).ToLocalChecked(); + auto result = Nan::NewInstance(constructor, 2, cargv); + if (!result.IsEmpty()) { + return scope.Escape(result.ToLocalChecked()); + } + } + return scope.Escape(Nan::Error(Nan::New(std::string(prefix) + ": " + message).ToLocalChecked())); +} diff --git a/deps/exokit-bindings/webrtc/src/errorfactory.h b/deps/exokit-bindings/webrtc/src/errorfactory.h new file mode 100644 index 0000000000..98ac62987a --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/errorfactory.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_ERRORFACTORY_H_ +#define SRC_ERRORFACTORY_H_ + +#include + +#include // IWYU pragma: keep +#include + +namespace node_webrtc { + +class ErrorFactory { + public: + enum DOMExceptionName { + kInvalidAccessError, + kInvalidModificationError, + kInvalidStateError, + kNetworkError, + kOperationError + }; + + enum ErrorName { + kError, + kRangeError, + kSyntaxError + }; + + static void Init(v8::Local module); + + static v8::Local CreateError(std::string message); + static v8::Local CreateInvalidAccessError(std::string message); + static v8::Local CreateInvalidModificationError(std::string message); + static v8::Local CreateInvalidStateError(std::string message); + static v8::Local CreateNetworkError(std::string message); + static v8::Local CreateOperationError(std::string message); + static v8::Local CreateRangeError(std::string message); + static v8::Local CreateSyntaxError(std::string message); + + private: + static Nan::Persistent DOMException; + + static const char* DOMExceptionNameToString(DOMExceptionName name); + + static v8::Local CreateDOMException(std::string message, DOMExceptionName name); +}; + +} // namespace node_webrtc + +#endif // SRC_ERRORFACTORY_H_ diff --git a/deps/exokit-bindings/webrtc/src/eventloop.h b/deps/exokit-bindings/webrtc/src/eventloop.h new file mode 100644 index 0000000000..659c247ba4 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/eventloop.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_EVENTLOOP_H_ +#define SRC_EVENTLOOP_H_ + +#include +#include +#include + +#include + +#include "src/eventqueue.h" +#include "src/events.h" + +namespace node_webrtc { + +/** + * EventLoop is a thread-safe Event loop. It allows you to dispatch events from + * one thread and handle them in another (or the same). + * @tparam T the Event target type + */ +template +class EventLoop: private EventQueue { + public: + /** + * Dispatch an event to the EventLoop. + * @param event the event to dispatch + */ + void Dispatch(std::unique_ptr> event) { + this->Enqueue(std::move(event)); + uv_mutex_lock(&_lock); + if (!uv_is_closing(reinterpret_cast(&_async))) { + uv_async_send(&_async); + } + uv_mutex_unlock(&_lock); + } + + ~EventLoop() override { + uv_mutex_destroy(&_lock); + } + + bool should_stop() const { + return _should_stop; + } + + protected: + explicit EventLoop(T& target): EventQueue(), _loop(uv_default_loop()), _target(target) { + uv_async_init(_loop, &_async, [](uv_async_t* handle) { + auto self = static_cast*>(handle->data); + self->Run(); + }); + _async.data = this; + uv_mutex_init(&_lock); + } + + /** + * This method will be invoked once the EventLoop stops. + */ + virtual void DidStop() { + // Do nothing. + } + + virtual void Run() { + if (!_should_stop) { + while (auto event = this->Dequeue()) { + event->Dispatch(_target); + if (_should_stop) { + break; + } + } + } + if (_should_stop) { + uv_mutex_lock(&_lock); + uv_close(reinterpret_cast(&_async), [](uv_handle_t* handle) { + auto self = static_cast*>(handle->data); + self->DidStop(); + }); + uv_mutex_unlock(&_lock); + } + } + + /** + * Stop the EventLoop. + */ + virtual void Stop() { + _should_stop = true; + Dispatch(Event::Create()); + } + + private: + uv_async_t _async; + uv_mutex_t _lock; + uv_loop_t* _loop; + std::atomic _should_stop = {false}; + T& _target; +}; + +} // namespace node_webrtc + +#endif // SRC_EVENTLOOP_H_ diff --git a/deps/exokit-bindings/webrtc/src/eventqueue.h b/deps/exokit-bindings/webrtc/src/eventqueue.h new file mode 100644 index 0000000000..278fafb650 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/eventqueue.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_EVENTQUEUE_H_ +#define SRC_EVENTQUEUE_H_ + +#include +#include + +#include + +#include "src/events.h" + +namespace node_webrtc { + +/** + * EventQueue is a thread-safe Event queue. It allows you to enqueue events + * from one thread and dequeue them from another (or the same). + * @tparam T the Event target type + */ +template +class EventQueue { + public: + /** + * Enqueue an Event. + * @param event the event to enqueue + */ + void Enqueue(std::unique_ptr> event) { + uv_mutex_lock(&_lock); + _events.push(std::move(event)); + uv_mutex_unlock(&_lock); + } + + /** + * Attempt to dequeue an Event. If the EventQueue is empty, this method + * returns nullptr. + * @return the dequeued Event or nullptr + */ + std::unique_ptr> Dequeue() { + uv_mutex_lock(&_lock); + if (_events.empty()) { + uv_mutex_unlock(&_lock); + return nullptr; + } + auto event = std::move(_events.front()); + _events.pop(); + uv_mutex_unlock(&_lock); + return event; + } + + virtual ~EventQueue() { + uv_mutex_destroy(&_lock); + } + + protected: + EventQueue() { + uv_mutex_init(&_lock); + } + + private: + std::queue>> _events; + uv_mutex_t _lock; +}; + +} // namespace node_webrtc + +#endif // SRC_EVENTQUEUE_H_ diff --git a/deps/exokit-bindings/webrtc/src/events.cc b/deps/exokit-bindings/webrtc/src/events.cc new file mode 100644 index 0000000000..2c355a5f48 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/events.cc @@ -0,0 +1,77 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/events.h" + +#include "src/datachannel.h" +#include "src/peerconnection.h" +#include "src/rtcvideosink.h" + +using node_webrtc::DataChannel; +using node_webrtc::DataChannelEvent; +using node_webrtc::DataChannelStateChangeEvent; +using node_webrtc::ErrorEvent; +using node_webrtc::IceConnectionStateChangeEvent; +using node_webrtc::IceGatheringStateChangeEvent; +using node_webrtc::IceEvent; +using node_webrtc::MessageEvent; +using node_webrtc::NegotiationNeededEvent; +using node_webrtc::OnAddTrackEvent; +using node_webrtc::OnFrameEvent; +using node_webrtc::PeerConnection; +using node_webrtc::RTCVideoSink; +using node_webrtc::SignalingStateChangeEvent; + +void DataChannelEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleDataChannelEvent(*this); +} + +void DataChannelStateChangeEvent::Dispatch(DataChannel& dataChannel) { + dataChannel.HandleStateEvent(*this); +} + +// NOTE(mroberts): https://stackoverflow.com/a/25594741 +namespace node_webrtc { + +template <> +void ErrorEvent::Dispatch(DataChannel& dataChannel) { + dataChannel.HandleErrorEvent(*this); +} + +} // namespace node_webrtc + +void MessageEvent::Dispatch(DataChannel& dataChannel) { + dataChannel.HandleMessageEvent(*this); +} + +void NegotiationNeededEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleNegotiationNeededEvent(*this); +} + +void OnAddTrackEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleOnAddTrackEvent(*this); +} + +void IceEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleIceCandidateEvent(*this); +} + +void IceConnectionStateChangeEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleIceConnectionStateChangeEvent(*this); +} + +void IceGatheringStateChangeEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleIceGatheringStateChangeEvent(*this); +} + +void SignalingStateChangeEvent::Dispatch(PeerConnection& peerConnection) { + peerConnection.HandleSignalingStateChangeEvent(*this); +} + +void OnFrameEvent::Dispatch(RTCVideoSink& sink) { + sink.HandleOnFrameEvent(*this); +} diff --git a/deps/exokit-bindings/webrtc/src/events.h b/deps/exokit-bindings/webrtc/src/events.h new file mode 100644 index 0000000000..ce1d642ab8 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/events.h @@ -0,0 +1,311 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#ifndef SRC_EVENTS_H_ +#define SRC_EVENTS_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "src/converters/v8-converters.h" +#include "src/error.h" +#include "src/functional/either.h" + +namespace webrtc { + +class RtpReceiverInterface; +class RtpTransceiverInterface; + +} // namespace webrtc + +namespace node_webrtc { + +class DataChannel; +class DataChannelObserver; +class PeerConnection; +class RTCVideoSink; + +/** + * Event represents an event that can be dispatched to a target. + * @tparam T the target type + */ +template +class Event { + public: + /** + * Dispatch the Event to the target. + * @param target the target to dispatch to + */ + virtual void Dispatch(T&) { + // Do nothing. + } + + virtual ~Event() = default; + + static std::unique_ptr> Create() { + return std::unique_ptr>(new Event()); + } +}; + +/** + * A PromiseEvent can be dispatched to a PromiseFulfillingEventLoop in order to + * resolve or reject a Promise. + * @tparam T the PromiseFulfillingEventLoop type + * @tparam L the type of values representing failure + * @tparam R the type of values representing success + */ +template +class PromiseEvent: public Event { + public: + void Dispatch(T&) override { + Nan::HandleScope scope; + if (_resolver) { + auto resolver = Nan::New(*_resolver); + _result.template FromEither([resolver](L error) { + CONVERT_OR_REJECT_AND_RETURN(resolver, error, value, v8::Local); + resolver->Reject(Nan::GetCurrentContext(), value).IsNothing(); + }, [resolver](R result) { + CONVERT_OR_REJECT_AND_RETURN(resolver, result, value, v8::Local); + resolver->Resolve(Nan::GetCurrentContext(), value).IsNothing(); + }); + } + } + + void Reject(L error) { + _result = node_webrtc::Either::Left(error); + } + + void Resolve(R result) { + _result = node_webrtc::Either::Right(result); + } + + static std::pair, std::unique_ptr>> Create() { + Nan::EscapableHandleScope scope; + auto resolver = v8::Promise::Resolver::New(Nan::GetCurrentContext()).ToLocalChecked(); + auto event = std::unique_ptr>(new PromiseEvent( + std::unique_ptr>( + new Nan::Persistent(resolver)))); + return std::pair, std::unique_ptr>>( + scope.Escape(resolver), + std::move(event)); + } + + protected: + explicit PromiseEvent(std::unique_ptr> resolver) + : _resolver(std::move(resolver)) {} + + private: + std::unique_ptr> _resolver; + node_webrtc::Either _result; +}; + +class NegotiationNeededEvent: public Event { + public: + void Dispatch(PeerConnection&) override; + + static std::unique_ptr Create() { + return std::unique_ptr(new NegotiationNeededEvent()); + } + + private: + NegotiationNeededEvent() = default; +}; + +template +class ErrorEvent: public Event { + public: + const std::string msg; + + void Dispatch(T&) override {} + + protected: + explicit ErrorEvent(const std::string&& msg): msg(msg) {} +}; + +class MessageEvent: public Event { + public: + bool binary; + std::unique_ptr message; + size_t size; + + void Dispatch(DataChannel& dataChannel) override; + + static std::unique_ptr Create(const webrtc::DataBuffer* buffer) { + return std::unique_ptr(new MessageEvent(buffer)); + } + + private: + explicit MessageEvent(const webrtc::DataBuffer* buffer) { + binary = buffer->binary; + size = buffer->size(); + message = std::unique_ptr(new char[size]); + memcpy(reinterpret_cast(message.get()), reinterpret_cast(buffer->data.data()), size); + } +}; + +class IceEvent: public Event { + public: + std::string error; + std::unique_ptr candidate; + + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create(const webrtc::IceCandidateInterface* ice_candidate) { + return std::unique_ptr(new IceEvent(ice_candidate)); + } + + private: + explicit IceEvent(const webrtc::IceCandidateInterface* ice_candidate) { + std::string sdp; + if (!ice_candidate->ToString(&sdp)) { + error = "Failed to print the candidate string. This is pretty weird. File a bug on https://github.com/js-platform/node-webrtc"; + return; + } + webrtc::SdpParseError parseError; + candidate = std::unique_ptr( + webrtc::CreateIceCandidate(ice_candidate->sdp_mid(), ice_candidate->sdp_mline_index(), sdp, &parseError)); + if (!parseError.description.empty()) { + error = parseError.description; + } else if (!candidate) { + error = "Failed to copy RTCIceCandidate"; + } + } +}; + +template +class StateEvent: public Event { + public: + const S state; + + protected: + explicit StateEvent(const S state): state(state) {} +}; + +class DataChannelStateChangeEvent: public StateEvent { + public: + void Dispatch(DataChannel& dataChannel) override; + + static std::unique_ptr Create(const webrtc::DataChannelInterface::DataState state) { + return std::unique_ptr(new DataChannelStateChangeEvent(state)); + } + + protected: + explicit DataChannelStateChangeEvent(const webrtc::DataChannelInterface::DataState state): StateEvent(state) {} +}; + +class IceConnectionStateChangeEvent + : public StateEvent { + public: + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create( + const webrtc::PeerConnectionInterface::IceConnectionState state) { + return std::unique_ptr(new IceConnectionStateChangeEvent(state)); + } + + private: + explicit IceConnectionStateChangeEvent(const webrtc::PeerConnectionInterface::IceConnectionState state) + : StateEvent(state) {} +}; + +class IceGatheringStateChangeEvent + : public StateEvent { + public: + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create( + const webrtc::PeerConnectionInterface::IceGatheringState state) { + return std::unique_ptr(new IceGatheringStateChangeEvent(state)); + } + + private: + explicit IceGatheringStateChangeEvent(const webrtc::PeerConnectionInterface::IceGatheringState state) + : StateEvent(state) {} +}; + +class SignalingStateChangeEvent: public StateEvent { + public: + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create( + const webrtc::PeerConnectionInterface::SignalingState state) { + return std::unique_ptr(new SignalingStateChangeEvent(state)); + } + + private: + explicit SignalingStateChangeEvent(const webrtc::PeerConnectionInterface::SignalingState state): StateEvent(state) {} +}; + +class DataChannelEvent: public Event { + public: + DataChannelObserver* observer; + + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create(DataChannelObserver* observer) { + return std::unique_ptr(new DataChannelEvent(observer)); + } + + private: + explicit DataChannelEvent(DataChannelObserver* observer): observer(observer) {} +}; + +class OnAddTrackEvent: public Event { + public: + rtc::scoped_refptr transceiver; + rtc::scoped_refptr receiver; + const std::vector> streams; + + void Dispatch(PeerConnection& peerConnection) override; + + static std::unique_ptr Create( + rtc::scoped_refptr transceiver) { + return std::unique_ptr(new OnAddTrackEvent(transceiver)); + } + + static std::unique_ptr Create( + rtc::scoped_refptr receiver, + const std::vector>& streams) { + return std::unique_ptr(new OnAddTrackEvent(receiver, streams)); + } + + private: + explicit OnAddTrackEvent(rtc::scoped_refptr transceiver) + : transceiver(transceiver) + , receiver(transceiver->receiver()) + , streams(transceiver->receiver()->streams()) {} + + OnAddTrackEvent( + rtc::scoped_refptr receiver, + const std::vector>& streams) + : receiver(receiver) + , streams(streams) {} +}; + +class OnFrameEvent: public Event { + public: + const webrtc::VideoFrame frame; + + void Dispatch(RTCVideoSink& sink) override; + + static std::unique_ptr Create(const webrtc::VideoFrame& frame) { + return std::unique_ptr(new OnFrameEvent(frame)); + } + + private: + explicit OnFrameEvent(const webrtc::VideoFrame& frame): frame(frame) {} +}; + +} // namespace node_webrtc + +#endif // SRC_EVENTS_H_ diff --git a/deps/exokit-bindings/webrtc/src/functional/curry.h b/deps/exokit-bindings/webrtc/src/functional/curry.h new file mode 100644 index 0000000000..9266e59ef6 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/functional/curry.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines a function for currying functions. It is borrowed from + * https://stackoverflow.com/a/26768388 + */ + +#ifndef SRC_FUNCTIONAL_CURRY_H_ +#define SRC_FUNCTIONAL_CURRY_H_ + +#include + +namespace _dtl { + +template +struct _curry; + +// specialization for functions with a single argument +template +struct _curry> { + using type = std::function; + + const type result; + + _curry(type fun): result(fun) {} +}; + +// recursive specialization for functions with more arguments +template struct +_curry> { + using remaining_type = typename _curry>::type; + + using type = std::function; + + const type result; + + _curry(std::function fun): result( + [ = ](const T & t) { + return _curry>( + [ = ](const Ts & ...ts) { + return fun(t, ts...); + } + ).result; + } + ) {} +}; + +} // namespace _dtl + +template +auto curry(const std::function fun) -> typename _dtl::_curry>::type { + return _dtl::_curry>(fun).result; +} + +template +auto curry(R(* const fun)(Ts...)) -> typename _dtl::_curry>::type { + return _dtl::_curry>(fun).result; +} + +#endif // SRC_FUNCTIONAL_CURRY_H_ diff --git a/deps/exokit-bindings/webrtc/src/functional/either.h b/deps/exokit-bindings/webrtc/src/functional/either.h new file mode 100644 index 0000000000..e38d702a9f --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/functional/either.h @@ -0,0 +1,175 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines Either, a sum type, modeled after the Haskell's: + * https://hackage.haskell.org/package/base/docs/Data-Either.html + */ + +#ifndef SRC_FUNCTIONAL_EITHER_H_ +#define SRC_FUNCTIONAL_EITHER_H_ + +#include +#include + +namespace node_webrtc { + +/** + * Either is a sum type. It either contains a value of type L ("left") or a + * value of type R ("right"). + * @tparam L the left type + * @tparam R the right type + */ +template +class Either { + public: + // TODO(mroberts): This is no good. + Either(): _is_right(false), _left(L()), _right(R()) {} + + /** + * Either forms an applicative. Apply an Either. + * @tparam F the type of a function from R to S + * @param f an Either of a function from R to S + * @return the result of applying the Either + */ + template + Either::type> Apply(const Either f) const { + if (f.IsLeft()) { + return Either::Left(f.UnsafeFromLeft()); + } else if (IsLeft()) { + return Either::Left(_left); + } + return Either::Right(f.UnsafeFromRight()(_right)); + } + + /** + * Check whether or not the Either is "left". + * @return true if the Either is left; otherwise, false + */ + bool IsLeft() const { + return !_is_right; + } + + /** + * Check whether or not the Either is "right". + * @return true if the Either is right; otherwise, false + */ + bool IsRight() const { + return _is_right; + } + + /** + * Eliminate an Either. You must provide two functions for both the left and + * right cases that convert each value to some common type T. + * @tparam T the common type to convert to + * @param fromLeft a function that converts values of type L to T + * @param fromRight a function that converts values of type R to T + * @return a value of type T + */ + template + T FromEither(std::function fromLeft, std::function fromRight) const { + return _is_right ? fromRight(_right) : fromLeft(_left); + } + + /** + * Eliminate an Either. You must provide a default value to handle the "right" + * case. + * @param default_value the default value to use in the right case + * @return the value in the Either, if "left"; otherwise, the default value + */ + L FromLeft(const L default_value) const { + return _is_right ? default_value : _left; + } + + /** + * Eliminate an Either. You must provide a default value to handle the "left" + * case. + * @param default_value the default value to use in the left case + * @return the value in the Either, if "right"; otherwise, the default value + */ + R FromRight(const R default_value) const { + return _is_right ? _right : default_value; + } + + /** + * Either forms a functor. Map a function over Either. + * @tparam F the type of a function from R to S + * @param f a function from R to S + * @return the mapped Either + */ + template + Either::type> Map(F f) const { + return _is_right ? Either(f(_right)) : this; + } + + /** + * Either forms an alternative. If "this" is "right", return this; otherwise, that + * @param that another Either + * @return this or that + */ + Either Or(const Either that) const { + return _is_right ? this : that; + } + + /** + * Unsafely eliminate an Either. This only works if the Either is "left". + * @return the left value in the Either, if left; otherwise, undefined + */ + L UnsafeFromLeft() const { + assert(!_is_right); + return _left; + } + + /** + * Unsafely eliminate an Either. This only works if the Either is "right". + * @return the right value in the Either, if right; otherwise, undefined + */ + R UnsafeFromRight() const { + assert(_is_right); + return _right; + } + + /** + * Construct a "left" Either. + * @param left the value to inject into the Either + * @return a left Either + */ + static Either Left(const L left) { + return Either(false, left, R()); + } + + /** + * Construct a "right" Either. + * @param right the value to inject into the Either + * @return a right Either + */ + static Either Right(const R right) { + return Either(true, L(), right); + } + + private: + Either(bool is_right, const L left, const R right): _is_right(is_right), _left(left), _right(right) {} + + bool _is_right; + L _left; + R _right; +}; + +template +static Either MakeRight(const R right) { + return Either::Right(right); +} + +template +static Either MakeLeft(const L left) { + return Either::Left(left); +} + +} // namespace node_webrtc + +#endif // SRC_FUNCTIONAL_EITHER_H_ diff --git a/deps/exokit-bindings/webrtc/src/functional/maybe.h b/deps/exokit-bindings/webrtc/src/functional/maybe.h new file mode 100644 index 0000000000..98626cc266 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/functional/maybe.h @@ -0,0 +1,164 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines Maybe, modeled after the Haskell's: + * https://hackage.haskell.org/package/base/docs/Data-Maybe.html + */ + +#ifndef SRC_FUNCTIONAL_MAYBE_H_ +#define SRC_FUNCTIONAL_MAYBE_H_ + +#include +#include +#include + +namespace node_webrtc { + +/** + * Maybe represents an optional value. + * @tparam T the value type + */ +template +class Maybe { + public: + /** + * Construct an empty Maybe. + */ + Maybe(): _is_just(false), _value(T()) {} + + /** + * Construct a non-empty Maybe. + * @param value the value to inject into the Maybe + */ + explicit Maybe(const T& value): _is_just(true), _value(value) {} + + /** + * Maybe forms an applicative. Apply a Maybe. + * @tparam F the type of a function from T to S + * @param f a Maybe of a function from T to S + * @return the result of applying the Maybe + */ + template + Maybe::type> Apply(const Maybe f) const { + return f.IsJust() && _is_just ? Maybe(f.UnsafeFromJust()(_value)) : Nothing(); + } + + /** + * Maybe forms a Monad. FlatMap a Maybe. + * @tparam S some type S + * @param f a function from T to a Maybe of S + * @return a Maybe of S + */ + template + Maybe FlatMap(std::function(T)> f) const { + return _is_just ? f(_value) : Nothing(); + } + + /** + * Eliminate a Maybe. You must provide a default value to handle the empty + * case. + * @param default_value the default value to use in the empty case + * @return the value in the Maybe, if non-empty; otherwise, the default value + */ + T FromMaybe(T default_value) const { + return _is_just ? _value : default_value; + } + + /** + * Check whether or not the Maybe is non-empty. + * @return true if the Maybe is non-empty; otherwise, false + */ + bool IsJust() const { + return _is_just; + } + + /** + * Check whether or not the Maybe is empty. + * @return true if the Maybe is empty; otherwise, false + */ + bool IsNothing() const { + return !_is_just; + } + + /** + * Maybe forms a functor. Map a function over Maybe. + * @tparam F the type of a function from T to S + * @param f a function from T to S + * @return the mapped Maybe + */ + template + Maybe::type> Map(F f) const { + return _is_just + ? Maybe::type>::Just(f(_value)) + : Maybe::type>::Nothing(); + } + + /** + * Maybe forms an alternative. If "this" is non-empty, return this; otherwise, that + * @param that another Maybe + * @return this or that + */ + Maybe Or(const Maybe& that) const { + return _is_just ? this : that; + } + + /** + * If "this" contains a value, return it; otherwise, compute a value and + * return it + * @param compute + * @return + */ + T Or(std::function compute) const { + return _is_just ? _value : compute(); + } + + /** + * Unsafely eliminate a Maybe. This only works if the Maybe is non-empty. + * @return the value in the Maybe, if non-empty; otherwise, undefined + */ + T UnsafeFromJust() const { + assert(_is_just); + return _value; + } + + /** + * Construct an empty Maybe. + * @return an empty Maybe + */ + static Maybe Nothing() { + return Maybe(); + } + + /** + * Construct a non-empty Maybe. + * @param value the value present in the Maybe + * @return a non-empty Maybe + */ + static Maybe Just(const T& value) { + return Maybe(value); + } + + private: + bool _is_just; + T _value; +}; + +template +static Maybe MakeJust(const T& t) { + return Maybe::Just(t); +} + +template +static Maybe MakeNothing() { + return Maybe::Nothing(); +} + +} // namespace node_webrtc + +#endif // SRC_FUNCTIONAL_MAYBE_H_ diff --git a/deps/exokit-bindings/webrtc/src/functional/operators.h b/deps/exokit-bindings/webrtc/src/functional/operators.h new file mode 100644 index 0000000000..587d074552 --- /dev/null +++ b/deps/exokit-bindings/webrtc/src/functional/operators.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ + +/* + * This file defines operators that make it nicer to work with alternatives, + * applicatives, and functors. They are modeled after FTL: + * https://github.com/beark/ftl + */ + +#ifndef SRC_FUNCTIONAL_OPERATORS_H_ +#define SRC_FUNCTIONAL_OPERATORS_H_ + +namespace node_webrtc { + +/** + * Syntactic sugar for Or + * @tparam T the type of some alternative + * @tparam A some type A + * @param left an alternative of a value of type A + * @param right an alternative of a value of type A + * @return an alternative of a value of type A + */ +template