diff --git a/binding.gyp b/binding.gyp index d941c67..aba4881 100644 --- a/binding.gyp +++ b/binding.gyp @@ -4,15 +4,26 @@ 'target_name': 'webrtc', 'sources': [ 'src/event/createsessiondescriptionevent.cc', + 'src/event/setsessiondescriptionevent.cc', + 'src/event/emitterevent.cc', + 'src/event/peerconnectioniceevent.cc', + 'src/event/datachannelevent.cc', + 'src/event/mediastreamevent.cc', + 'src/event/messageevent.cc', 'src/event/eventqueue.cc', 'src/globals.cc', 'src/module.cc', + 'src/eventemitter.cc', 'src/observer/createsessiondescriptionobserver.cc', + 'src/observer/setsessiondescriptionobserver.cc', 'src/observer/peerconnectionobserver.cc', + 'src/observer/datachannelobserver.cc', 'src/rtccertificate.cc', 'src/rtcicecandidate.cc', 'src/rtcpeerconnection.cc', 'src/rtcsessiondescription.cc', + 'src/rtcdatachannel.cc', + 'src/rtcmediastream.cc', ], 'include_dirs' : [ 'build/include', diff --git a/index.js b/index.js index 9a28268..f167d55 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,20 @@ 'use strict'; -module.exports = require('bindings')('webrtc'); +const EventEmitter = require('events').EventEmitter; +const webrtc = require('bindings')('webrtc'); + +function inherit(cl,parent) { + for( var k in parent.prototype ) { + cl.prototype[k] = parent.prototype[k]; + } +} + +var EventTarget = function(){}; +inherit(EventTarget, EventEmitter); +EventTarget.prototype.addEventListener = EventEmitter.prototype.on; + +[webrtc.RTCPeerConnection, webrtc.RTCDataChannel].map((cl)=>{ + inherit(cl, EventTarget); +}); + +module.exports = webrtc; \ No newline at end of file diff --git a/src/common.h b/src/common.h index 9f5fe8c..89d4b6f 100644 --- a/src/common.h +++ b/src/common.h @@ -44,6 +44,12 @@ #define ERROR_PROPERTY_NOT_BOOLEAN(NAME) \ "The '" << NAME << "' property is not a boolean." +#define ERROR_PROPERTY_NOT_OBJECT(NAME) \ + "The '" << NAME << "' property is not an object." + +#define ERROR_PROPERTY_NOT_ARRAY(NAME) \ + "The '" << NAME << "' property is not an object." + #define ERROR_PROPERTY_NOT_UINT8ARRAY(NAME) \ "The '" << NAME << "' property is not a Uint8Array." @@ -56,6 +62,9 @@ #define ERROR_ARGUMENT_NOT_FUNCTION(INDEX, NAME) \ "parameter " << INDEX << " ('" << NAME << "') is not a function." +#define ERROR_ARGUMENT_NOT_STRING(INDEX, NAME) \ + "parameter " << INDEX << " ('" << NAME << "') is not a string." + #ifdef DEBUG #define CONSTRUCTOR_HEADER(NAME) \ LOG(LS_INFO) << __PRETTY_FUNCTION__; \ @@ -116,6 +125,14 @@ \ Local N = info[I].As(); +#define ASSERT_STRING_ARGUMENT(I, N) \ + if (!info[I]->IsString()) { \ + errorStream << ERROR_ARGUMENT_NOT_STRING(I + 1, #N); \ + return Nan::ThrowTypeError(errorStream.str().c_str()); \ + } \ + \ + String::Utf8Value N(info[I]->ToString()); + #define ASSERT_REJECT_OBJECT_ARGUMENT(I, N) \ if (!info[I]->IsObject()) { \ errorStream << ERROR_ARGUMENT_NOT_OBJECT(I + 1, #N); \ @@ -168,6 +185,23 @@ \ Local S(V->ToBoolean()); +#define ASSERT_PROPERTY_OBJECT(N, V, S) \ + if (!V->IsObject()) { \ + errorStream << ERROR_PROPERTY_NOT_OBJECT(N); \ + return Nan::ThrowTypeError(errorStream.str().c_str()); \ + } \ + \ + Local S(V->ToObject()); + +#define ASSERT_PROPERTY_ARRAY(N, V, S) \ + if (!V->IsArray()) { \ + errorStream << ERROR_PROPERTY_NOT_ARRAY(N); \ + return Nan::ThrowTypeError(errorStream.str().c_str()); \ + } \ + \ + Local S = Local::Cast(V); + + #define ASSERT_REJECT_PROPERTY_BOOLEAN(N, V, S) \ if (!V->IsBoolean()) { \ errorStream << ERROR_PROPERTY_NOT_BOOLEAN(N); \ diff --git a/src/event/datachannelevent.cc b/src/event/datachannelevent.cc new file mode 100644 index 0000000..1beecfd --- /dev/null +++ b/src/event/datachannelevent.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "datachannelevent.h" +#include "eventemitter.h" +#include "rtcdatachannel.h" + +using namespace v8; + +static const char kChannel[] = "channel"; + +DataChannelEvent::DataChannelEvent(EventEmitter *eventEmitter, + const rtc::scoped_refptr &datachannel) + : EmitterEvent(eventEmitter), _channel(datachannel) { +} + +void DataChannelEvent::Handle() { + Nan::HandleScope scope; + + // FIXME: make proper DataChannelEvent ? + Local e = Nan::New(); + // FIXME: move to observer ? + Nan::Persistent channel(RTCDataChannel::Create(_channel)); + e->Set(LOCAL_STRING(kChannel), Nan::New(channel)); + + _eventEmitter->EmitData(LOCAL_STRING(_type), e); +} diff --git a/src/event/datachannelevent.h b/src/event/datachannelevent.h new file mode 100644 index 0000000..7780d06 --- /dev/null +++ b/src/event/datachannelevent.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_DATACHANNELEVENT_H_ +#define EVENT_DATACHANNELEVENT_H_ + +#include "emitterevent.h" +#include "eventemitter.h" +#include +#include + +using namespace v8; + +class DataChannelEvent : public EmitterEvent { + public: + explicit DataChannelEvent(EventEmitter *eventEmitter, + const rtc::scoped_refptr& datachannel); + + void Handle(); + + private: + const rtc::scoped_refptr _channel; +}; + +#endif // EVENT_DATACHANNELEVENT_H_ diff --git a/src/event/emitterevent.cc b/src/event/emitterevent.cc new file mode 100644 index 0000000..737ac41 --- /dev/null +++ b/src/event/emitterevent.cc @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "emitterevent.h" + +using namespace v8; + +EmitterEvent::EmitterEvent(EventEmitter *eventEmitter) + : _eventEmitter(eventEmitter) {} + +void EmitterEvent::Handle() { + Nan::HandleScope scope; + _eventEmitter->Emit(LOCAL_STRING(_type)); +} + +void EmitterEvent::SetType(const std::string &type) { + _type = type; +} diff --git a/src/event/emitterevent.h b/src/event/emitterevent.h new file mode 100644 index 0000000..acc735a --- /dev/null +++ b/src/event/emitterevent.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_EMITTEREVENT_H_ +#define EVENT_EMITTEREVENT_H_ + +#include "event.h" +#include "eventemitter.h" + +using namespace v8; + +class EmitterEvent : public Event { + public: + explicit EmitterEvent(EventEmitter *eventEmitter); + + void Handle(); + void SetType(const std::string& type); + + protected: + EventEmitter *_eventEmitter; + std::string _type; +}; + +#endif // EVENT_EMITTEREVENT_H_ diff --git a/src/event/mediastreamevent.cc b/src/event/mediastreamevent.cc new file mode 100644 index 0000000..c6c3548 --- /dev/null +++ b/src/event/mediastreamevent.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "mediastreamevent.h" +#include "eventemitter.h" +#include "rtcmediastream.h" + +using namespace v8; + +static const char kStream[] = "stream"; + +MediaStreamEvent::MediaStreamEvent(EventEmitter *eventEmitter, + const rtc::scoped_refptr &mediastream) + : EmitterEvent(eventEmitter), _mediastream(mediastream) { +} + +void MediaStreamEvent::Handle() { + Nan::HandleScope scope; + + // FIXME: make proper MediaStreamEvent ? + Local e = Nan::New(); + // FIXME: move to observer ? + Nan::Persistent mediastream(RTCMediaStream::Create(_mediastream)); + e->Set(LOCAL_STRING(kStream), Nan::New(mediastream)); + + _eventEmitter->EmitData(LOCAL_STRING(_type), e); +} diff --git a/src/event/mediastreamevent.h b/src/event/mediastreamevent.h new file mode 100644 index 0000000..155af71 --- /dev/null +++ b/src/event/mediastreamevent.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_MEDIASTREAMEVENT_H_ +#define EVENT_MEDIASTREAMEVENT_H_ + +#include "emitterevent.h" +#include "eventemitter.h" +#include +#include + +using namespace v8; + +class MediaStreamEvent : public EmitterEvent { + public: + explicit MediaStreamEvent(EventEmitter *eventEmitter, + const rtc::scoped_refptr& datachannel); + + void Handle(); + + private: + const rtc::scoped_refptr _mediastream; +}; + +#endif // EVENT_MEDIASTREAMEVENT_H_ diff --git a/src/event/messageevent.cc b/src/event/messageevent.cc new file mode 100644 index 0000000..16f88fe --- /dev/null +++ b/src/event/messageevent.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "messageevent.h" +#include "eventemitter.h" + +using namespace v8; + +static const char kData[] = "data"; + +MessageEvent::MessageEvent(EventEmitter *eventEmitter) + : EmitterEvent(eventEmitter) {} + +void MessageEvent::Handle() { + Nan::HandleScope scope; + + // FIXME: make proper MessageData ? + Local e = Nan::New(); + e->Set(LOCAL_STRING(kData), LOCAL_STRING(_data)); + _eventEmitter->EmitData(LOCAL_STRING(_type), e); +} + +void MessageEvent::SetData(const std::string& data) { + _data = data; +} diff --git a/src/event/messageevent.h b/src/event/messageevent.h new file mode 100644 index 0000000..f8f1d04 --- /dev/null +++ b/src/event/messageevent.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_MESSAGEEVENT_H_ +#define EVENT_MESSAGEEVENT_H_ + +#include "emitterevent.h" +#include "eventemitter.h" +#include + +using namespace v8; + +class MessageEvent : public EmitterEvent { + public: + explicit MessageEvent(EventEmitter *eventEmitter); + + void Handle(); + void SetData(const std::string& data); + + private: + std::string _data; +}; + +#endif // EVENT_MESSAGEEVENT_H_ diff --git a/src/event/peerconnectioniceevent.cc b/src/event/peerconnectioniceevent.cc new file mode 100644 index 0000000..db3e2ac --- /dev/null +++ b/src/event/peerconnectioniceevent.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "peerconnectioniceevent.h" +#include "eventemitter.h" +#include "rtcicecandidate.h" + +#include +#include + +using namespace v8; + +static const char kCandidate[] = "candidate"; + +PeerConnectionIceEvent::PeerConnectionIceEvent(EventEmitter *eventEmitter) + : EmitterEvent(eventEmitter) { +} + +void PeerConnectionIceEvent::Handle() { + Nan::HandleScope scope; + + // FIXME: make proper PeerConnectionIceEvent ? + Local e = Nan::New(); + + if (_candidate.length() && _sdpMid.length()) { + e->Set(LOCAL_STRING(kCandidate), + RTCIceCandidate::Create(_sdpMid, _sdpMLineIndex, _candidate)); + } else { + e->Set(LOCAL_STRING(kCandidate), Nan::Null()); + } + + _eventEmitter->EmitData(LOCAL_STRING(_type), e); +} + +void PeerConnectionIceEvent::SetCandidate( + const webrtc::IceCandidateInterface* candidate) { + candidate->ToString(&_candidate); + _sdpMid = candidate->sdp_mid(); + _sdpMLineIndex = candidate->sdp_mline_index(); +} diff --git a/src/event/peerconnectioniceevent.h b/src/event/peerconnectioniceevent.h new file mode 100644 index 0000000..72c134c --- /dev/null +++ b/src/event/peerconnectioniceevent.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_PEERCONNECTIONICEEVENT_H_ +#define EVENT_PEERCONNECTIONICEEVENT_H_ + +#include "emitterevent.h" +#include "eventemitter.h" +#include + +using namespace v8; + +class PeerConnectionIceEvent : public EmitterEvent { + public: + explicit PeerConnectionIceEvent(EventEmitter *eventEmitter); + + void Handle(); + void SetCandidate(const webrtc::IceCandidateInterface* candidate); + + private: + std::string _candidate; + std::string _sdpMid; + int _sdpMLineIndex = 0; +}; + +#endif // EVENT_PEERCONNECTIONICEEVENT_H_ diff --git a/src/event/setsessiondescriptionevent.cc b/src/event/setsessiondescriptionevent.cc new file mode 100644 index 0000000..4c909ed --- /dev/null +++ b/src/event/setsessiondescriptionevent.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "setsessiondescriptionevent.h" +#include "rtcsessiondescription.h" +#include + +using namespace v8; + +SetSessionDescriptionEvent::SetSessionDescriptionEvent( + Persistent *successCallback, + Persistent *failureCallback) : + _resolver(NULL), + _successCallback(successCallback), + _failureCallback(failureCallback) { +} + +SetSessionDescriptionEvent::SetSessionDescriptionEvent( + Persistent *resolver) : + _resolver(resolver), + _successCallback(NULL), + _failureCallback(NULL) { +} + +void SetSessionDescriptionEvent::Handle() { + Nan::HandleScope scope; + + if (_resolver) { + Local resolver = Nan::New(*_resolver); + + if (_succeeded) { + resolver->Resolve(Nan::Undefined()); + } else { + resolver->Reject(Nan::Error(_errorMessage.c_str())); + } + + Isolate::GetCurrent()->RunMicrotasks(); + _resolver->Reset(); + delete _resolver; + return; + } + + if (_succeeded && _successCallback) { + Local successCallback = Nan::New(*_successCallback); + Nan::Callback cb(successCallback); + + const int argc = 0; + Local argv[0] = {}; + + cb.Call(argc, argv); + } else if (!_succeeded && _failureCallback) { + Local failureCallback = Nan::New(*_failureCallback); + Nan::Callback cb(failureCallback); + + const int argc = 1; + Local argv[1] = { Nan::Error(_errorMessage.c_str()) }; + + cb.Call(argc, argv); + } + + _successCallback->Reset(); + _failureCallback->Reset(); + + delete _successCallback; + delete _failureCallback; +} + +void SetSessionDescriptionEvent::SetSucceeded(bool succeeded) { + _succeeded = succeeded; +} + +void SetSessionDescriptionEvent::SetErrorMessage( + const std::string &errorMessage) { + _errorMessage = errorMessage; +} diff --git a/src/event/setsessiondescriptionevent.h b/src/event/setsessiondescriptionevent.h new file mode 100644 index 0000000..88fb615 --- /dev/null +++ b/src/event/setsessiondescriptionevent.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_SETSESSIONDESCRIPTIONEVENT_H_ +#define EVENT_SETSESSIONDESCRIPTIONEVENT_H_ + +#include +#include +#include "event.h" + +using namespace v8; + +class SetSessionDescriptionEvent : public Event { + public: + explicit SetSessionDescriptionEvent( + Persistent *resolver); + SetSessionDescriptionEvent(Persistent *successCallback, + Persistent *failureCallback); + + void Handle(); + void SetSucceeded(bool succeeded); + void SetErrorMessage(const std::string& errorMessage); + + private: + Persistent *_resolver; + Persistent *_successCallback; + Persistent *_failureCallback; + bool _succeeded; + std::string _errorMessage; +}; + +#endif // EVENT_SETSESSIONDESCRIPTIONEVENT_H_ diff --git a/src/eventemitter.cc b/src/eventemitter.cc new file mode 100644 index 0000000..1e462e4 --- /dev/null +++ b/src/eventemitter.cc @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "eventemitter.h" +#include "common.h" + +using namespace v8; + +static const char kEmit[] = "emit"; + +EventEmitter::EventEmitter() {} + +void EventEmitter::Wrap(Local obj) { + Nan::ObjectWrap::Wrap(obj); + _emit = new Nan::Persistent( + Local::Cast(handle()->Get(Nan::New(kEmit).ToLocalChecked()))); +} + +void EventEmitter::Emit(Local type) { + Nan::HandleScope scope; + Local argv[] = { type }; + Local emit = Nan::New(*_emit); + emit->Call(handle(), 1, argv); +} + +void EventEmitter::EmitData(Local type, Local data) { + Nan::HandleScope scope; + Local argv[] = { type, data }; + Local emit = Nan::New(*_emit); + emit->Call(handle(), 2, argv); +} + + diff --git a/src/eventemitter.h b/src/eventemitter.h new file mode 100644 index 0000000..174c84c --- /dev/null +++ b/src/eventemitter.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENTEMITTER_H_ +#define EVENTEMITTER_H_ + +#include +#include + +using namespace v8; + +class EventEmitter : public Nan::ObjectWrap { + public: + EventEmitter(); + Persistent* _emit = nullptr; + + void Wrap(Local obj); + void Emit(Local type); + void EmitData(Local type, Local data); +}; + +#endif // EVENTEMITTER_H_ diff --git a/src/module.cc b/src/module.cc index 1526da6..711cd92 100644 --- a/src/module.cc +++ b/src/module.cc @@ -21,16 +21,24 @@ #include "rtcicecandidate.h" #include "rtcpeerconnection.h" #include "rtcsessiondescription.h" +#include "rtcdatachannel.h" +#include "rtcmediastream.h" +#include NAN_MODULE_INIT(Init) { if (!Globals::Init()) { return; } + rtc::LogMessage::LogToDebug(rtc::LS_NONE); + // rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE); + RTCCertificate::Init(target); RTCIceCandidate::Init(target); RTCPeerConnection::Init(target); RTCSessionDescription::Init(target); + RTCDataChannel::Init(target); + RTCMediaStream::Init(target); node::AtExit(Globals::Cleanup); } diff --git a/src/observer/datachannelobserver.cc b/src/observer/datachannelobserver.cc new file mode 100644 index 0000000..011977d --- /dev/null +++ b/src/observer/datachannelobserver.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "datachannelobserver.h" +#include "event/messageevent.h" +#include "globals.h" + +static const char kMessage[] = "message"; + +DataChannelObserver::DataChannelObserver() { +} + +DataChannelObserver::~DataChannelObserver() { + _eventEmitter = NULL; +} + +void DataChannelObserver::OnStateChange() { + std::cout << "DataChannel OnStateChange" << std::endl; +} + +void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) { + MessageEvent* _event = new MessageEvent(_eventEmitter); + _event->SetType(kMessage); + + rtc::CopyOnWriteBuffer data = buffer.data; + std::string _data(reinterpret_cast(data.data()), data.size());; + + _event->SetData(_data); + Globals::GetEventQueue()->PushEvent(_event); +} + +void DataChannelObserver::OnBufferedAmountChange(uint64_t previous_amount) { + std::cout << "DataChannel OnBufferedAmountChange" << std::endl; +} + +DataChannelObserver *DataChannelObserver::Create() { + return new rtc::RefCountedObject(); +} + +void DataChannelObserver::SetEventEmitter( + EventEmitter* eventEmitter) { + _eventEmitter = eventEmitter; +} diff --git a/src/observer/datachannelobserver.h b/src/observer/datachannelobserver.h new file mode 100644 index 0000000..8a4df6d --- /dev/null +++ b/src/observer/datachannelobserver.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBSERVER_DATACHANNELOBSERVER_H_ +#define OBSERVER_DATACHANNELOBSERVER_H_ + +#include +#include "event/emitterevent.h" + +using namespace v8; + +class DataChannelObserver : public rtc::RefCountInterface, + public webrtc::DataChannelObserver { + public: + static DataChannelObserver *Create(); + + void SetEventEmitter( + EventEmitter* eventEmitter); + + // The data channel state have changed. + void OnStateChange(); + + // A data buffer was successfully received. + void OnMessage(const webrtc::DataBuffer& buffer); + + // The data channel's buffered_amount has changed. + void OnBufferedAmountChange(uint64_t previous_amount); + + private: + EventEmitter* _eventEmitter; + + protected: + DataChannelObserver(); + ~DataChannelObserver(); +}; + +#endif // OBSERVER_DATACHANNELOBSERVER_H_ diff --git a/src/observer/peerconnectionobserver.cc b/src/observer/peerconnectionobserver.cc index 7d52f7a..53ebddd 100644 --- a/src/observer/peerconnectionobserver.cc +++ b/src/observer/peerconnectionobserver.cc @@ -16,51 +16,103 @@ #include #include "peerconnectionobserver.h" +#include "globals.h" + +static const char kSignalingStateChange[] = "signalingstatechange"; +static const char kIceConnectionStateChange[] = "iceconnectionstatechange"; +static const char kIceGatheringStateChange[] = "icegatheringstatechange"; +static const char kNegociationNeeded[] = "negotiationneeded"; +static const char kDataChannel[] = "datachannel"; +static const char kIceCandidate[] = "icecandidate"; +// FIXME: not part of W3C spec ? +static const char kAddStream[] = "addstream"; +static const char kRemoveStream[] = "removestream"; PeerConnectionObserver::PeerConnectionObserver() { } PeerConnectionObserver::~PeerConnectionObserver() { - _peerConnection = NULL; + _eventEmitter = NULL; } void PeerConnectionObserver::OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState new_state) { - std::cout << "OnSignalingChange" << std::endl; + webrtc::PeerConnectionInterface::SignalingState new_state) { + EmitterEvent* _event = new EmitterEvent(_eventEmitter); + _event->SetType(kSignalingStateChange); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnAddStream( rtc::scoped_refptr stream) { std::cout << "OnAddStream" << std::endl; + MediaStreamEvent* _event = new MediaStreamEvent(_eventEmitter, stream); + _event->SetType(kAddStream); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnRemoveStream( rtc::scoped_refptr stream) { std::cout << "OnRemoveStream" << std::endl; + MediaStreamEvent* _event = new MediaStreamEvent(_eventEmitter, stream); + _event->SetType(kRemoveStream); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnDataChannel( rtc::scoped_refptr data_channel) { - std::cout << "OnDataChannel" << std::endl; + DataChannelEvent* _event = new DataChannelEvent(_eventEmitter, data_channel); + _event->SetType(kDataChannel); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnRenegotiationNeeded() { - std::cout << "OnRenegotiationNeeded" << std::endl; + EmitterEvent* _event = new EmitterEvent(_eventEmitter); + _event->SetType(kNegociationNeeded); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnIceConnectionChange( webrtc::PeerConnectionInterface::IceConnectionState new_state) { - std::cout << "OnIceConnectionChange" << std::endl; + EmitterEvent* _event = new EmitterEvent(_eventEmitter); + _event->SetType(kIceConnectionStateChange); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnIceGatheringChange( webrtc::PeerConnectionInterface::IceGatheringState new_state) { - std::cout << "OnIceGatheringChange" << std::endl; + switch (new_state) { + case webrtc::PeerConnectionInterface::kIceGatheringNew: + break; + + case webrtc::PeerConnectionInterface::kIceGatheringGathering: + break; + + case webrtc::PeerConnectionInterface::kIceGatheringComplete: + { + // emit null ice candidate + // as per https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectioniceevent + PeerConnectionIceEvent* _iceEvent + = new PeerConnectionIceEvent(_eventEmitter); + _iceEvent->SetType(kIceCandidate); + Globals::GetEventQueue()->PushEvent(_iceEvent); + break; + } + + default: + break; + } + + EmitterEvent* _event = new EmitterEvent(_eventEmitter); + _event->SetType(kIceGatheringStateChange); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnIceCandidate( const webrtc::IceCandidateInterface *candidate) { - std::cout << "OnIceCandidate" << std::endl; + PeerConnectionIceEvent* _event = new PeerConnectionIceEvent(_eventEmitter); + _event->SetType(kIceCandidate); + _event->SetCandidate(candidate); + Globals::GetEventQueue()->PushEvent(_event); } void PeerConnectionObserver::OnIceCandidatesRemoved( @@ -76,7 +128,7 @@ PeerConnectionObserver *PeerConnectionObserver::Create() { return new rtc::RefCountedObject(); } -void PeerConnectionObserver::SetPeerConnection( - rtc::scoped_refptr peerConnection) { - _peerConnection = peerConnection; +void PeerConnectionObserver::SetEventEmitter( + EventEmitter* eventEmitter) { + _eventEmitter = eventEmitter; } diff --git a/src/observer/peerconnectionobserver.h b/src/observer/peerconnectionobserver.h index 8d52e59..0af3771 100644 --- a/src/observer/peerconnectionobserver.h +++ b/src/observer/peerconnectionobserver.h @@ -18,14 +18,20 @@ #define OBSERVER_PEERCONNECTIONOBSERVER_H_ #include +#include "event/emitterevent.h" +#include "event/peerconnectioniceevent.h" +#include "event/datachannelevent.h" +#include "event/mediastreamevent.h" + +using namespace v8; class PeerConnectionObserver : public rtc::RefCountInterface, public webrtc::PeerConnectionObserver { public: static PeerConnectionObserver *Create(); - void SetPeerConnection( - rtc::scoped_refptr peerConnection); + void SetEventEmitter( + EventEmitter* eventEmitter); // Triggered when the SignalingState changed. void OnSignalingChange( @@ -66,7 +72,7 @@ class PeerConnectionObserver : public rtc::RefCountInterface, void OnIceConnectionReceivingChange(bool receiving); private: - rtc::scoped_refptr _peerConnection; + EventEmitter* _eventEmitter; protected: PeerConnectionObserver(); diff --git a/src/observer/setsessiondescriptionobserver.cc b/src/observer/setsessiondescriptionobserver.cc new file mode 100644 index 0000000..b3fb589 --- /dev/null +++ b/src/observer/setsessiondescriptionobserver.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "common.h" +#include "setsessiondescriptionobserver.h" +#include "event/setsessiondescriptionevent.h" +#include "globals.h" + +using namespace v8; + +SetSessionDescriptionObserver::SetSessionDescriptionObserver( + Persistent *resolver) { + _event = new SetSessionDescriptionEvent(resolver); +} + +SetSessionDescriptionObserver::SetSessionDescriptionObserver( + Persistent *successCallback, + Persistent *failureCallback) { + _event = new SetSessionDescriptionEvent(successCallback, failureCallback); +} + +SetSessionDescriptionObserver *SetSessionDescriptionObserver:: + Create(Persistent *successCallback, + Persistent *failureCallback) { + return new rtc::RefCountedObject + (successCallback, failureCallback); +} + +SetSessionDescriptionObserver *SetSessionDescriptionObserver:: + Create(Persistent *resolver) { + return new rtc::RefCountedObject + (resolver); +} + +void SetSessionDescriptionObserver::OnSuccess() { + _event->SetSucceeded(true); + Globals::GetEventQueue()->PushEvent(_event); +} + +void SetSessionDescriptionObserver::OnFailure(const std::string &error) { + _event->SetSucceeded(false); + _event->SetErrorMessage(error); + Globals::GetEventQueue()->PushEvent(_event); +} diff --git a/src/observer/setsessiondescriptionobserver.h b/src/observer/setsessiondescriptionobserver.h new file mode 100644 index 0000000..75a53af --- /dev/null +++ b/src/observer/setsessiondescriptionobserver.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBSERVER_SETSESSIONDESCRIPTIONOBSERVER_H_ +#define OBSERVER_SETSESSIONDESCRIPTIONOBSERVER_H_ + +#include +#include +#include + +using namespace v8; + +class SetSessionDescriptionEvent; +class SetSessionDescriptionObserver : + public webrtc::SetSessionDescriptionObserver { + public: + static SetSessionDescriptionObserver *Create( + Persistent *resolver); + static SetSessionDescriptionObserver *Create( + Persistent *successCallback, + Persistent *failureCallback); + + void OnSuccess(); + void OnFailure(const std::string& error); + + private: + SetSessionDescriptionEvent *_event; + + protected: + explicit SetSessionDescriptionObserver( + Persistent *resolver); + + SetSessionDescriptionObserver(Persistent *successCallback, + Persistent *failureCallback); +}; + +#endif // OBSERVER_SETSESSIONDESCRIPTIONOBSERVER_H_ diff --git a/src/rtccertificate.h b/src/rtccertificate.h index eacfd84..8197475 100644 --- a/src/rtccertificate.h +++ b/src/rtccertificate.h @@ -41,15 +41,14 @@ class RTCCertificate : public Nan::ObjectWrap { return _constructor; } + const rtc::scoped_refptr _certificate; + private: explicit RTCCertificate( - const rtc::scoped_refptr& certificate); + const rtc::scoped_refptr& certificate); ~RTCCertificate(); static NAN_METHOD(New); - - protected: - const rtc::scoped_refptr _certificate; }; #endif // RTCCERTIFICATE_H_ diff --git a/src/rtcdatachannel.cc b/src/rtcdatachannel.cc new file mode 100644 index 0000000..e0df156 --- /dev/null +++ b/src/rtcdatachannel.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "observer/datachannelobserver.h" +#include "rtcdatachannel.h" +#include "common.h" + +static const char sRTCDataChannel[] = "RTCDataChannel"; + +static const char kLabel[] = "label"; +static const char kOrdered[] = "ordered"; +static const char kReadyState[] = "readyState"; +static const char kSend[] = "send"; +static const char kClose[] = "close"; + +NAN_MODULE_INIT(RTCDataChannel::Init) { + Local ctor = Nan::New(New); + ctor->SetClassName(LOCAL_STRING(sRTCDataChannel)); + ctor->InstanceTemplate()->SetInternalFieldCount(1); + + Local prototype = ctor->PrototypeTemplate(); + Nan::SetAccessor(prototype, LOCAL_STRING(kLabel), GetLabel); + Nan::SetAccessor(prototype, LOCAL_STRING(kOrdered), GetOrdered); + Nan::SetAccessor(prototype, LOCAL_STRING(kReadyState), GetReadyState); + + Nan::SetMethod(prototype, kSend, Send); + Nan::SetMethod(prototype, kClose, Close); + + constructor().Reset(Nan::GetFunction(ctor).ToLocalChecked()); + + Nan::Set(target, LOCAL_STRING(sRTCDataChannel), ctor->GetFunction()); +} + +RTCDataChannel::RTCDataChannel( + const rtc::scoped_refptr &datachannel) + : _datachannel(datachannel) { + + _datachannelObserver = DataChannelObserver::Create(); + _datachannel->RegisterObserver(_datachannelObserver); +} + +RTCDataChannel::~RTCDataChannel() { + _datachannel->UnregisterObserver(); + _datachannelObserver = NULL; +} + +Local RTCDataChannel::Create( + const rtc::scoped_refptr &datachannel) { + Local cons = Nan::New(RTCDataChannel::constructor()); + Local instance = Nan::NewInstance(cons, 0, NULL).ToLocalChecked(); + + RTCDataChannel *_datachannel = new RTCDataChannel(datachannel); + _datachannel->Wrap(instance); + _datachannel->_datachannelObserver->SetEventEmitter(_datachannel); + + return instance; +} + +NAN_METHOD(RTCDataChannel::New) { +} + +NAN_GETTER(RTCDataChannel::GetLabel) { + UNWRAP_OBJECT(RTCDataChannel, object); + info.GetReturnValue().Set(LOCAL_STRING(object->_datachannel->label())); +} + +NAN_GETTER(RTCDataChannel::GetOrdered) { + UNWRAP_OBJECT(RTCDataChannel, object); + info.GetReturnValue().Set(object->_datachannel->ordered()); +} + +NAN_GETTER(RTCDataChannel::GetReadyState) { + UNWRAP_OBJECT(RTCDataChannel, object); + const char* readyState = webrtc::DataChannelInterface::DataStateString( + object->_datachannel->state()); + info.GetReturnValue().Set(LOCAL_STRING(readyState)); +} + +NAN_METHOD(RTCDataChannel::Send) { + METHOD_HEADER("RTCDataChannel", "send"); + UNWRAP_OBJECT(RTCDataChannel, object); + + // FIXME: implement for ArrayBuffer, others? + ASSERT_STRING_ARGUMENT(0, data); + + webrtc::DataBuffer _buffer(*data); + object->_datachannel->Send(_buffer); + + info.GetReturnValue().Set(Nan::Null()); +} + +NAN_METHOD(RTCDataChannel::Close) { + METHOD_HEADER("RTCDataChannel", "close"); + UNWRAP_OBJECT(RTCDataChannel, object); + + object->_datachannel->Close(); + + info.GetReturnValue().Set(Nan::Null()); +} diff --git a/src/rtcdatachannel.h b/src/rtcdatachannel.h new file mode 100644 index 0000000..2ee4330 --- /dev/null +++ b/src/rtcdatachannel.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RTCDATACHANNEL_H_ +#define RTCDATACHANNEL_H_ + +#include +#include +#include +#include "eventemitter.h" +#include "observer/datachannelobserver.h" + +using namespace v8; + +class RTCDataChannel : public EventEmitter { + public: + static NAN_MODULE_INIT(Init); + + static NAN_GETTER(GetLabel); + static NAN_GETTER(GetOrdered); + static NAN_GETTER(GetReadyState); + + static NAN_METHOD(Send); + static NAN_METHOD(Close); + + static Local Create( + const rtc::scoped_refptr& datachannel); + + static inline Nan::Persistent& constructor() { + static Nan::Persistent _constructor; + return _constructor; + } + + const rtc::scoped_refptr _datachannel; + + private: + explicit RTCDataChannel( + const rtc::scoped_refptr& datachannel); + ~RTCDataChannel(); + + static NAN_METHOD(New); + + protected: + rtc::scoped_refptr _datachannelObserver; +}; + +#endif // RTCDATACHANNEL_H_ diff --git a/src/rtcicecandidate.cc b/src/rtcicecandidate.cc index c60a1e5..136fb56 100644 --- a/src/rtcicecandidate.cc +++ b/src/rtcicecandidate.cc @@ -23,8 +23,6 @@ #include "common.h" #include "rtcicecandidate.h" -Nan::Persistent RTCIceCandidate::constructor; - static const char sRTCIceCandidate[] = "RTCIceCandidate"; static const char kCandidate[] = "candidate"; @@ -67,19 +65,40 @@ NAN_MODULE_INIT(RTCIceCandidate::Init) { Nan::SetAccessor(prototype, LOCAL_STRING(kRelatedAddress), GetRelatedAddress); Nan::SetAccessor(prototype, LOCAL_STRING(kRelatedPort), GetRelatedPort); - constructor.Reset(ctor); + constructor().Reset(Nan::GetFunction(ctor).ToLocalChecked()); + Nan::Set(target, LOCAL_STRING(sRTCIceCandidate), ctor->GetFunction()); } -RTCIceCandidate::RTCIceCandidate(webrtc::IceCandidateInterface *iceCandidate) - : _iceCandidate(iceCandidate) { +RTCIceCandidate::RTCIceCandidate( + const webrtc::IceCandidateInterface *iceCandidate) + : _iceCandidate(iceCandidate) { } RTCIceCandidate::~RTCIceCandidate() { delete _iceCandidate; } +Local RTCIceCandidate::Create( + std::string sdpMid, int sdpMLineIndex, std::string candidate) { + Local cons = Nan::New(RTCIceCandidate::constructor()); + + Local candidateInitDict = Nan::New(); + candidateInitDict->Set(LOCAL_STRING(kSdpMid), + LOCAL_STRING(sdpMid)); + candidateInitDict->Set(LOCAL_STRING(kSdpMLineIndex), + Nan::New(sdpMLineIndex)); + candidateInitDict->Set(LOCAL_STRING(kCandidate), + LOCAL_STRING(candidate)); + + const int argc = 1; + Local argv[1] = { candidateInitDict }; + Local instance = Nan::NewInstance(cons, argc, argv).ToLocalChecked(); + + return instance; +} + NAN_METHOD(RTCIceCandidate::New) { CONSTRUCTOR_HEADER("RTCIceCandidate") diff --git a/src/rtcicecandidate.h b/src/rtcicecandidate.h index 907195f..c113c15 100644 --- a/src/rtcicecandidate.h +++ b/src/rtcicecandidate.h @@ -25,9 +25,12 @@ using namespace v8; class RTCIceCandidate : public Nan::ObjectWrap { public: static NAN_MODULE_INIT(Init); + static Local Create(std::string sdpMid, + int sdpMLineIndex, + std::string candidate); private: - explicit RTCIceCandidate(webrtc::IceCandidateInterface *iceCandidate); + explicit RTCIceCandidate(const webrtc::IceCandidateInterface *iceCandidate); ~RTCIceCandidate(); static NAN_METHOD(New); @@ -46,10 +49,13 @@ class RTCIceCandidate : public Nan::ObjectWrap { static NAN_GETTER(GetRelatedAddress); static NAN_GETTER(GetRelatedPort); - static Nan::Persistent constructor; + static inline Nan::Persistent& constructor() { + static Nan::Persistent _constructor; + return _constructor; + } protected: - webrtc::IceCandidateInterface *_iceCandidate; + const webrtc::IceCandidateInterface *_iceCandidate; }; #endif // RTCICECANDIDATE_H_ diff --git a/src/rtcmediastream.cc b/src/rtcmediastream.cc new file mode 100644 index 0000000..0d18c7f --- /dev/null +++ b/src/rtcmediastream.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "rtcmediastream.h" +#include "common.h" + +static const char sRTCMediaStream[] = "RTCMediaStream"; + +NAN_MODULE_INIT(RTCMediaStream::Init) { + Local ctor = Nan::New(New); + ctor->SetClassName(LOCAL_STRING(sRTCMediaStream)); + ctor->InstanceTemplate()->SetInternalFieldCount(1); + + // Local prototype = ctor->PrototypeTemplate(); + // Nan::SetAccessor(prototype, LOCAL_STRING(kLabel), GetLabel); + // Nan::SetAccessor(prototype, LOCAL_STRING(kOrdered), GetOrdered); + // Nan::SetAccessor(prototype, LOCAL_STRING(kReadyState), GetReadyState); + + // Nan::SetMethod(prototype, kSend, Send); + // Nan::SetMethod(prototype, kClose, Close); + + constructor().Reset(Nan::GetFunction(ctor).ToLocalChecked()); + + Nan::Set(target, LOCAL_STRING(sRTCMediaStream), ctor->GetFunction()); +} + +RTCMediaStream::RTCMediaStream( + const rtc::scoped_refptr &mediastream) + : _mediastream(mediastream) { + + //_mediastreamObserver = DataChannelObserver::Create(); + //_mediastream->RegisterObserver(_mediastreamObserver); +} + +RTCMediaStream::~RTCMediaStream() { + //_mediastream->UnregisterObserver(); + //_mediastreamObserver = NULL; +} + +Local RTCMediaStream::Create( + const rtc::scoped_refptr &mediastream) { + + Local cons = Nan::New(RTCMediaStream::constructor()); + Local instance = Nan::NewInstance(cons, 0, NULL).ToLocalChecked(); + + RTCMediaStream *_mediastream = new RTCMediaStream(mediastream); + _mediastream->Wrap(instance); + //_mediastream->_mediastreamObserver->SetEventEmitter(_mediastream); + + return instance; +} + +NAN_METHOD(RTCMediaStream::New) { +} diff --git a/src/rtcmediastream.h b/src/rtcmediastream.h new file mode 100644 index 0000000..a5850a6 --- /dev/null +++ b/src/rtcmediastream.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Axel Isouard + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RTCMEDIASTREAM_H_ +#define RTCMEDIASTREAM_H_ + +#include +#include +#include +#include "eventemitter.h" + +using namespace v8; + +class RTCMediaStream : public EventEmitter { + public: + static NAN_MODULE_INIT(Init); + + // static NAN_GETTER(GetLabel); + // static NAN_GETTER(GetOrdered); + // static NAN_GETTER(GetReadyState); + + // static NAN_METHOD(Send); + // static NAN_METHOD(Close); + + static Local Create( + const rtc::scoped_refptr& mediastream); + + static inline Nan::Persistent& constructor() { + static Nan::Persistent _constructor; + return _constructor; + } + + const rtc::scoped_refptr _mediastream; + + private: + explicit RTCMediaStream( + const rtc::scoped_refptr& mediastream); + ~RTCMediaStream(); + + static NAN_METHOD(New); + + protected: + // rtc::scoped_refptr _datachannelObserver; +}; + +#endif // RTCMEDIASTREAM_H_ diff --git a/src/rtcpeerconnection.cc b/src/rtcpeerconnection.cc index 2209cf4..1c646fa 100644 --- a/src/rtcpeerconnection.cc +++ b/src/rtcpeerconnection.cc @@ -21,17 +21,30 @@ #include "common.h" #include "globals.h" #include "observer/createsessiondescriptionobserver.h" +#include "observer/setsessiondescriptionobserver.h" #include "observer/peerconnectionobserver.h" #include "rtccertificate.h" #include "rtcpeerconnection.h" +#include "rtcsessiondescription.h" +#include "rtcdatachannel.h" +#include "rtcmediastream.h" Nan::Persistent RTCPeerConnection::constructor; static const char sRTCPeerConnection[] = "RTCPeerConnection"; static const char kCreateOffer[] = "createOffer"; +static const char kCreateAnswer[] = "createAnswer"; +static const char kSetLocalDescription[] = "setLocalDescription"; +static const char kSetRemoteDescription[] = "setRemoteDescription"; +static const char kCreateDataChannel[] = "createDataChannel"; +static const char kAddStream[] = "addStream"; static const char kGenerateCertificate[] = "generateCertificate"; +static const char kIceServers[] = "iceServers"; +static const char kIceServerUrls[] = "urls"; +static const char kCertificates[] = "certificates"; + static const char kName[] = "name"; static const char kRSA[] = "RSASSA-PKCS1-v1_5"; static const char kHash[] = "hash"; @@ -45,6 +58,8 @@ static const char kP256[] = "P-256"; static const char kConnectionState[] = "connectionState"; static const char kCurrentLocalDescription[] = "currentLocalDescription"; static const char kCurrentRemoteDescription[] = "currentRemoteDescription"; +static const char kLocalDescription[] = "localDescription"; +static const char kRemoteDescription[] = "remoteDescription"; static const char kIceConnectionState[] = "iceConnectionState"; static const char kIceGatheringState[] = "iceGatheringState"; static const char kPendingLocalDescription[] = "pendingLocalDescription"; @@ -75,7 +90,10 @@ static const char eUnsupported[] = "The 1st argument provided is an " "AlgorithmIdentifier with a supported algorithm name, but the parameters " "are not supported."; -static const char eFailure[] = "Failed to generate the certificate."; +static const char eGenerateCertificateFailure[] = + "Failed to generate the certificate."; +static const char eConstructorFailure[] = + "Failed to construct 'RTCPeerConnection'"; NAN_MODULE_INIT(RTCPeerConnection::Init) { Local ctor = Nan::New(New); @@ -86,6 +104,11 @@ NAN_MODULE_INIT(RTCPeerConnection::Init) { Local prototype = ctor->InstanceTemplate(); Nan::SetMethod(prototype, kCreateOffer, CreateOffer); + Nan::SetMethod(prototype, kCreateAnswer, CreateAnswer); + Nan::SetMethod(prototype, kSetLocalDescription, SetLocalDescription); + Nan::SetMethod(prototype, kSetRemoteDescription, SetRemoteDescription); + Nan::SetMethod(prototype, kCreateDataChannel, CreateDataChannel); + Nan::SetMethod(prototype, kAddStream, AddStream); Local tpl = ctor->InstanceTemplate(); Nan::SetAccessor(tpl, LOCAL_STRING(kConnectionState), @@ -94,6 +117,10 @@ NAN_MODULE_INIT(RTCPeerConnection::Init) { GetCurrentLocalDescription); Nan::SetAccessor(tpl, LOCAL_STRING(kCurrentRemoteDescription), GetCurrentRemoteDescription); + Nan::SetAccessor(tpl, LOCAL_STRING(kLocalDescription), + GetLocalDescription); + Nan::SetAccessor(tpl, LOCAL_STRING(kRemoteDescription), + GetRemoteDescription); Nan::SetAccessor(tpl, LOCAL_STRING(kIceConnectionState), GetIceConnectionState); Nan::SetAccessor(tpl, LOCAL_STRING(kIceGatheringState), @@ -110,8 +137,7 @@ NAN_MODULE_INIT(RTCPeerConnection::Init) { } RTCPeerConnection::RTCPeerConnection( - const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const webrtc::MediaConstraintsInterface& constraints) { + const webrtc::PeerConnectionInterface::RTCConfiguration& config) { _peerConnectionFactory = webrtc::CreatePeerConnectionFactory( Globals::GetSignalingThread(), Globals::GetWorkerThread(), @@ -119,8 +145,7 @@ RTCPeerConnection::RTCPeerConnection( _peerConnectionObserver = PeerConnectionObserver::Create(); _peerConnection = _peerConnectionFactory->CreatePeerConnection( - config, &constraints, NULL, NULL, _peerConnectionObserver); - _peerConnectionObserver->SetPeerConnection(_peerConnection); + config, NULL, NULL, _peerConnectionObserver); } RTCPeerConnection::~RTCPeerConnection() { @@ -130,19 +155,66 @@ RTCPeerConnection::~RTCPeerConnection() { } NAN_METHOD(RTCPeerConnection::New) { + CONSTRUCTOR_HEADER("RTCPeerConnection") webrtc::FakeConstraints constraints; - webrtc::PeerConnectionInterface::RTCConfiguration config; - webrtc::PeerConnectionInterface::IceServer server; - server.uri = "stun:stun.l.google.com:19302"; - config.servers.push_back(server); + webrtc::PeerConnectionInterface::RTCConfiguration _config; + + if (info.Length() > 0) { + ASSERT_OBJECT_ARGUMENT(0, config); + + DECLARE_OBJECT_PROPERTY(config, kIceServers, iceServersVal); + + if (!iceServersVal->IsNull() && !iceServersVal->IsUndefined()) { + ASSERT_PROPERTY_ARRAY(kIceServers, iceServersVal, iceServers); + + for (unsigned int i = 0; i < iceServers->Length(); i = i + 1) { + Local iceServerVal = iceServers->Get(i); + ASSERT_PROPERTY_OBJECT(kIceServers, iceServerVal, iceServer); + + webrtc::PeerConnectionInterface::IceServer server; + + DECLARE_OBJECT_PROPERTY(iceServer, kIceServerUrls, iceServerUrlsVal); + ASSERT_PROPERTY_ARRAY(kIceServerUrls, iceServerUrlsVal, iceServerUrls); + + for (unsigned int j = 0; j < iceServerUrls->Length(); j = j + 1) { + Local iceServerUrlVal = iceServerUrls->Get(j); + // FIXME: validate URL + ASSERT_PROPERTY_STRING(kIceServerUrls, iceServerUrlVal, iceServerUrl); + server.urls.push_back(*iceServerUrl); + // FIXME: add username / password for TURN servers + } + _config.servers.push_back(server); + } + } + + DECLARE_OBJECT_PROPERTY(config, kCertificates, certificatesVal); + + if (!certificatesVal->IsNull() && !certificatesVal->IsUndefined()) { + ASSERT_PROPERTY_ARRAY(kCertificates, certificatesVal, certificates); + + for (unsigned int i = 0; i < certificates->Length(); i = i + 1) { + Local certificateVal = certificates->Get(i); + ASSERT_PROPERTY_OBJECT(kCertificates, certificateVal, certificate); + // FIXME: validate it's a RTCCertificate object + RTCCertificate* _certificate + = Nan::ObjectWrap::Unwrap(certificate); - constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, - "true"); + _config.certificates.push_back(_certificate->_certificate); + } + } + } + + RTCPeerConnection *rtcPeerConnection = new RTCPeerConnection(_config); + + if (!rtcPeerConnection->_peerConnection) { + return Nan::ThrowError(eConstructorFailure); + } - RTCPeerConnection *rtcPeerConnection = new RTCPeerConnection(config, - constraints); rtcPeerConnection->Wrap(info.This()); + rtcPeerConnection->_peerConnectionObserver->SetEventEmitter( + rtcPeerConnection); + info.GetReturnValue().Set(info.This()); } @@ -189,7 +261,121 @@ NAN_METHOD(RTCPeerConnection::CreateOffer) { object->_peerConnection->CreateOffer(observer, &constraints); } +NAN_METHOD(RTCPeerConnection::CreateAnswer) { + METHOD_HEADER("RTCPeerConnection", "createAnswer"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + unsigned char start = 0; + rtc::scoped_refptr observer; + + if (info.Length() < 2) { + DECLARE_PROMISE_RESOLVER; + + observer = CreateSessionDescriptionObserver::Create( + new Nan::Persistent(resolver)); + } else if (info.Length() > 1) { + if (info.Length() > 2) { + start = 1; + } + + ASSERT_FUNCTION_ARGUMENT(start, successCallback); + ASSERT_FUNCTION_ARGUMENT(start + 1, failureCallback); + + observer = CreateSessionDescriptionObserver::Create( + new Nan::Persistent(successCallback), + new Nan::Persistent(failureCallback)); + } + + webrtc::FakeConstraints constraints; + object->_peerConnection->CreateAnswer(observer, &constraints); +} + +// FIXME: factorize SetLocalDescription and SetRemoteDescription + +NAN_METHOD(RTCPeerConnection::SetLocalDescription) { + METHOD_HEADER("RTCPeerConnection", "setLocalDescription"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + rtc::scoped_refptr observer; + + // FIXME: Promise implementation only + DECLARE_PROMISE_RESOLVER; + ASSERT_REJECT_OBJECT_ARGUMENT(0, sessionDescription); + + // FIXME: validate it's a RTCSessionDescription object + RTCSessionDescription* _sessionDescription; + _sessionDescription = Nan::ObjectWrap::Unwrap( + sessionDescription); + + observer = SetSessionDescriptionObserver::Create( + new Nan::Persistent(resolver)); + + object->_peerConnection->SetLocalDescription(observer, + _sessionDescription->session_description()); +} + +NAN_METHOD(RTCPeerConnection::SetRemoteDescription) { + METHOD_HEADER("RTCPeerConnection", "setRemoteDescription"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + rtc::scoped_refptr observer; + + // FIXME: Promise implementation only + DECLARE_PROMISE_RESOLVER; + ASSERT_REJECT_OBJECT_ARGUMENT(0, sessionDescription); + + // FIXME: validate it's a RTCSessionDescription object + RTCSessionDescription* _sessionDescription; + _sessionDescription = Nan::ObjectWrap::Unwrap( + sessionDescription); + + observer = SetSessionDescriptionObserver::Create( + new Nan::Persistent(resolver)); + + object->_peerConnection->SetRemoteDescription(observer, + _sessionDescription->session_description()); +} + +NAN_METHOD(RTCPeerConnection::CreateDataChannel) { + METHOD_HEADER("RTCPeerConnection", "createDataChannel"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + ASSERT_STRING_ARGUMENT(0, name); + + // FIXME: add init options + const webrtc::DataChannelInit init; + + rtc::scoped_refptr _channel + = object->_peerConnection->CreateDataChannel(*name, &init); + + Local datachannel = RTCDataChannel::Create(_channel); + + info.GetReturnValue().Set(datachannel); +} + +NAN_METHOD(RTCPeerConnection::AddStream) { + METHOD_HEADER("RTCPeerConnection", "addStream"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + ASSERT_OBJECT_ARGUMENT(0, stream); + + // FIXME: validate... + RTCMediaStream* _stream = Nan::ObjectWrap::Unwrap( + stream); + + std::cout << "ADDING STREAM" << std::endl; + bool ok = object->_peerConnection->AddStream(_stream->_mediastream); + if(ok) { + std::cout << "stream added" << std::endl; + }else{ + std::cout << "stream NOT added" << std::endl; + } + + info.GetReturnValue().Set(Nan::Null()); +} + NAN_GETTER(RTCPeerConnection::GetConnectionState) { + // FIXME: implement info.GetReturnValue().Set(LOCAL_STRING("new")); } @@ -197,6 +383,38 @@ NAN_GETTER(RTCPeerConnection::GetCurrentLocalDescription) { info.GetReturnValue().Set(Nan::Null()); } +NAN_GETTER(RTCPeerConnection::GetLocalDescription) { + METHOD_HEADER("RTCPeerConnection", "getLocalDescription"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + const webrtc::SessionDescriptionInterface *session + = object->_peerConnection->local_description(); + + const std::string type = session->type(); + std::string sdp; + session->ToString(&sdp); + + Local desc = RTCSessionDescription::Create(type, sdp); + + info.GetReturnValue().Set(desc); +} + +NAN_GETTER(RTCPeerConnection::GetRemoteDescription) { + METHOD_HEADER("RTCPeerConnection", "getRemoteDescription"); + UNWRAP_OBJECT(RTCPeerConnection, object); + + const webrtc::SessionDescriptionInterface *session + = object->_peerConnection->remote_description(); + + const std::string type = session->type(); + std::string sdp; + session->ToString(&sdp); + + Local desc = RTCSessionDescription::Create(type, sdp); + + info.GetReturnValue().Set(desc); +} + NAN_GETTER(RTCPeerConnection::GetCurrentRemoteDescription) { info.GetReturnValue().Set(Nan::Null()); } @@ -336,7 +554,7 @@ void RTCPeerConnection::GenerateCertificateWorker::WorkComplete() { if (!_certificate.get()) { std::stringstream errorStream; - errorStream << eFailure; + errorStream << eGenerateCertificateFailure; resolver->Reject(Nan::TypeError(errorStream.str().c_str())); } else { resolver->Resolve(RTCCertificate::Create(_certificate)); diff --git a/src/rtcpeerconnection.h b/src/rtcpeerconnection.h index 9bb302e..60236cf 100644 --- a/src/rtcpeerconnection.h +++ b/src/rtcpeerconnection.h @@ -20,27 +20,34 @@ #include #include #include +#include "eventemitter.h" using namespace v8; class PeerConnectionObserver; -class RTCPeerConnection : public Nan::ObjectWrap { +class RTCPeerConnection : public EventEmitter { public: static NAN_MODULE_INIT(Init); private: explicit RTCPeerConnection( - const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const webrtc::MediaConstraintsInterface& constraints); + const webrtc::PeerConnectionInterface::RTCConfiguration& config); ~RTCPeerConnection(); static NAN_METHOD(New); static NAN_METHOD(CreateOffer); + static NAN_METHOD(CreateAnswer); + static NAN_METHOD(SetLocalDescription); + static NAN_METHOD(SetRemoteDescription); + static NAN_METHOD(CreateDataChannel); static NAN_METHOD(GenerateCertificate); + static NAN_METHOD(AddStream); static NAN_GETTER(GetConnectionState); static NAN_GETTER(GetCurrentLocalDescription); static NAN_GETTER(GetCurrentRemoteDescription); + static NAN_GETTER(GetLocalDescription); + static NAN_GETTER(GetRemoteDescription); static NAN_GETTER(GetIceConnectionState); static NAN_GETTER(GetIceGatheringState); static NAN_GETTER(GetPendingLocalDescription); diff --git a/src/rtcsessiondescription.cc b/src/rtcsessiondescription.cc index d0bb36f..c5eeaaa 100644 --- a/src/rtcsessiondescription.cc +++ b/src/rtcsessiondescription.cc @@ -52,6 +52,17 @@ RTCSessionDescription::~RTCSessionDescription() { delete _sessionDescription; } +webrtc::SessionDescriptionInterface* +RTCSessionDescription::session_description() { + std::string sdp; + _sessionDescription->ToString(&sdp); + std::string type = _sessionDescription->type(); + + // FIXME: handle error... + webrtc::SdpParseError error; + return webrtc::CreateSessionDescription(type, sdp, &error); +} + Local RTCSessionDescription::Create(const std::string &type, const std::string &sdp) { Local cons = Nan::New(RTCSessionDescription::constructor()); diff --git a/src/rtcsessiondescription.h b/src/rtcsessiondescription.h index c941a5a..98b8b03 100644 --- a/src/rtcsessiondescription.h +++ b/src/rtcsessiondescription.h @@ -42,18 +42,19 @@ class RTCSessionDescription : public Nan::ObjectWrap { static const char kPranswer[]; static const char kRollback[]; + webrtc::SessionDescriptionInterface* session_description(); + private: explicit RTCSessionDescription( - webrtc::SessionDescriptionInterface *sessionDescription); + webrtc::SessionDescriptionInterface* sessionDescription); ~RTCSessionDescription(); + webrtc::SessionDescriptionInterface* _sessionDescription; + static NAN_METHOD(New); static NAN_GETTER(GetType); static NAN_GETTER(GetSdp); - - protected: - webrtc::SessionDescriptionInterface *_sessionDescription; }; #endif // RTCSESSIONDESCRIPTION_H_