From 92aa5a8ad481e803c5c11ecc312d0da327306bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cervi=C3=B1o?= Date: Fri, 22 Dec 2017 14:55:54 +0100 Subject: [PATCH 1/4] Generate SDPInfo in ErizoJS --- erizo/src/erizo/SdpInfo.cpp | 6 +- erizo/src/erizo/SdpInfo.h | 17 +- erizo/src/erizo/WebRtcConnection.cpp | 17 +- erizo/src/erizo/WebRtcConnection.h | 4 +- erizoAPI/ConnectionDescription.cc | 415 ++++++++++++++++++ erizoAPI/ConnectionDescription.h | 64 +++ erizoAPI/WebRtcConnection.cc | 13 + erizoAPI/WebRtcConnection.h | 7 + erizoAPI/addon.cc | 2 + erizoAPI/binding.gyp | 2 +- .../erizoAgent/log4cxx.properties | 2 +- erizo_controller/erizoJS/erizoJSController.js | 21 +- erizo_controller/erizoJS/models/Helpers.js | 18 + erizo_controller/erizoJS/models/Publisher.js | 26 +- .../erizoJS/models/SessionDescription.js | 129 ++++++ 15 files changed, 703 insertions(+), 40 deletions(-) create mode 100644 erizoAPI/ConnectionDescription.cc create mode 100644 erizoAPI/ConnectionDescription.h create mode 100644 erizo_controller/erizoJS/models/Helpers.js create mode 100644 erizo_controller/erizoJS/models/SessionDescription.js diff --git a/erizo/src/erizo/SdpInfo.cpp b/erizo/src/erizo/SdpInfo.cpp index 706c9d81cd..43bc4c2d94 100644 --- a/erizo/src/erizo/SdpInfo.cpp +++ b/erizo/src/erizo/SdpInfo.cpp @@ -837,7 +837,7 @@ namespace erizo { } else { ELOG_DEBUG("invalid rid syntax: missing delimiter"); } - } else if ((mtype == AUDIO_TYPE)) { + } else if (mtype == AUDIO_TYPE) { ELOG_DEBUG("audio shouldn't have simulcast rid! - ignoring this sdp line"); } } @@ -943,6 +943,10 @@ namespace erizo { } } // sdp lines loop + return postProcessInfo(); + } + + bool SdpInfo::postProcessInfo() { // If there is no video or audio credentials we use the ones we have if (iceVideoUsername_.empty() && iceAudioUsername_.empty()) { ELOG_ERROR("No valid credentials for ICE") diff --git a/erizo/src/erizo/SdpInfo.h b/erizo/src/erizo/SdpInfo.h index d29cdf26c0..2367ef3928 100644 --- a/erizo/src/erizo/SdpInfo.h +++ b/erizo/src/erizo/SdpInfo.h @@ -246,6 +246,8 @@ class SdpInfo { void updateSupportedExtensionMap(const std::vector &ext_map); bool isValidExtension(std::string uri); + bool postProcessInfo(); + /** * @return A vector containing the simulcast RID informations */ @@ -314,13 +316,6 @@ class SdpInfo { int audioSdpMLine; int videoCodecs, audioCodecs; unsigned int videoBandwidth; - - private: - bool processSdp(const std::string& sdp, const std::string& media); - bool processCandidate(const std::vector& pieces, MediaType mediaType, std::string sdp); - std::string stringifyCandidate(const CandidateInfo & candidate); - void gen_random(char* s, int len); - void maybeAddSsrcToList(uint32_t ssrc); std::vector candidateVector_; std::vector cryptoVector_; std::vector internalPayloadVector_; @@ -329,6 +324,14 @@ class SdpInfo { std::map payload_parsed_map_; std::vector supported_ext_map_; std::vector rids_; + + private: + bool processSdp(const std::string& sdp, const std::string& media); + bool processCandidate(const std::vector& pieces, MediaType mediaType, std::string sdp); + std::string stringifyCandidate(const CandidateInfo & candidate); + void gen_random(char* s, int len); + void maybeAddSsrcToList(uint32_t ssrc); + }; } // namespace erizo #endif // ERIZO_SRC_ERIZO_SDPINFO_H_ diff --git a/erizo/src/erizo/WebRtcConnection.cpp b/erizo/src/erizo/WebRtcConnection.cpp index fe996577dc..de21ffdc55 100644 --- a/erizo/src/erizo/WebRtcConnection.cpp +++ b/erizo/src/erizo/WebRtcConnection.cpp @@ -164,6 +164,17 @@ void WebRtcConnection::removeMediaStream(const std::string& stream_id) { media_stream_.reset(); } +bool WebRtcConnection::setRemoteSdpInfo(std::shared_ptr sdp) { + ELOG_DEBUG("%s message: setting remote SDPInfo", toLog()); + + if (!sending_) { + return true; + } + + remote_sdp_ = sdp; + return processRemoteSdp(); +} + bool WebRtcConnection::setRemoteSdp(const std::string &sdp) { ELOG_DEBUG("%s message: setting remote SDP", toLog()); @@ -172,7 +183,11 @@ bool WebRtcConnection::setRemoteSdp(const std::string &sdp) { } remote_sdp_->initWithSdp(sdp, ""); + return processRemoteSdp(); +} +bool WebRtcConnection::processRemoteSdp() { + ELOG_DEBUG("%s message: processing remote SDP", toLog()); bundle_ = remote_sdp_->isBundle; local_sdp_->setOfferSdp(remote_sdp_); extension_processor_.setSdpInfo(local_sdp_); @@ -238,10 +253,10 @@ bool WebRtcConnection::setRemoteSdp(const std::string &sdp) { } } } - if (trickle_enabled_) { std::string object = this->getLocalSdp(); if (conn_event_listener_) { + ELOG_DEBUG("%s message: Sending SDP", toLog()); conn_event_listener_->notifyEvent(CONN_SDP, object); } } diff --git a/erizo/src/erizo/WebRtcConnection.h b/erizo/src/erizo/WebRtcConnection.h index e6c2a7f671..151cdda4dd 100644 --- a/erizo/src/erizo/WebRtcConnection.h +++ b/erizo/src/erizo/WebRtcConnection.h @@ -80,6 +80,8 @@ class WebRtcConnection: public TransportListener, public LogContext, bool init(); void close(); void syncClose(); + + bool setRemoteSdpInfo(std::shared_ptr sdp); /** * Sets the SDP of the remote peer. * @param sdp The SDP. @@ -148,7 +150,7 @@ class WebRtcConnection: public TransportListener, public LogContext, } private: - // Utils + bool processRemoteSdp(); std::string getJSONCandidate(const std::string& mid, const std::string& sdp); void trackTransportInfo(); diff --git a/erizoAPI/ConnectionDescription.cc b/erizoAPI/ConnectionDescription.cc new file mode 100644 index 0000000000..dab20357d0 --- /dev/null +++ b/erizoAPI/ConnectionDescription.cc @@ -0,0 +1,415 @@ +#ifndef BUILDING_NODE_EXTENSION +#define BUILDING_NODE_EXTENSION +#endif + +#include "ConnectionDescription.h" + +#include "lib/json.hpp" + +using v8::HandleScope; +using v8::Function; +using v8::FunctionTemplate; +using v8::Local; +using v8::Persistent; +using v8::Exception; +using v8::Value; +using json = nlohmann::json; + +#define GET_SDP() \ + ConnectionDescription* obj = ObjectWrap::Unwrap(info.Holder()); \ + std::shared_ptr sdp = obj->me; + +std::string getString(v8::Local value) { + v8::String::Utf8Value value_str(Nan::To(value).ToLocalChecked()); \ + return std::string(*value_str); +} + +erizo::MediaType getMediaType(std::string media_type) { + erizo::MediaType media_type_value; + if (media_type == "audio") { + media_type_value = erizo::AUDIO_TYPE; + } else if (media_type == "video") { + media_type_value = erizo::VIDEO_TYPE; + } else { + media_type_value = erizo::OTHER; + } + return media_type_value; +} + +Nan::Persistent ConnectionDescription::constructor; + +ConnectionDescription::ConnectionDescription() { +} + +ConnectionDescription::~ConnectionDescription() { +} + +NAN_MODULE_INIT(ConnectionDescription::Init) { + // Prepare constructor template + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("ConnectionDescription").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + // Prototype + Nan::SetPrototypeMethod(tpl, "close", close); + + Nan::SetPrototypeMethod(tpl, "setProfile", setProfile); + Nan::SetPrototypeMethod(tpl, "setBundle", setBundle); + Nan::SetPrototypeMethod(tpl, "addBundleTag", addBundleTag); + Nan::SetPrototypeMethod(tpl, "setRtcpMux", setRtcpMux); + Nan::SetPrototypeMethod(tpl, "setAudioAndVideo", setAudioAndVideo); + + Nan::SetPrototypeMethod(tpl, "setAudioSsrc", setAudioSsrc); + Nan::SetPrototypeMethod(tpl, "setVideoSsrcList", setVideoSsrcList); + + Nan::SetPrototypeMethod(tpl, "setDirection", setDirection); + + Nan::SetPrototypeMethod(tpl, "setFingerprint", setFingerprint); + Nan::SetPrototypeMethod(tpl, "setDtlsRole", setDtlsRole); + + Nan::SetPrototypeMethod(tpl, "setVideoBandwidth", setVideoBandwidth); + + Nan::SetPrototypeMethod(tpl, "addCandidate", addCandidate); + Nan::SetPrototypeMethod(tpl, "addCryptoInfo", addCryptoInfo); + Nan::SetPrototypeMethod(tpl, "setICECredentials", setICECredentials); + + Nan::SetPrototypeMethod(tpl, "addRid", addRid); + Nan::SetPrototypeMethod(tpl, "addPt", addPt); + Nan::SetPrototypeMethod(tpl, "addExtension", addExtension); + Nan::SetPrototypeMethod(tpl, "addFeedback", addFeedback); + Nan::SetPrototypeMethod(tpl, "addParameter", addParameter); + + Nan::SetPrototypeMethod(tpl, "postProcessInfo", postProcessInfo); + + constructor.Reset(tpl->GetFunction()); + Nan::Set(target, Nan::New("ConnectionDescription").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); +} + + +NAN_METHOD(ConnectionDescription::New) { + if (info.Length() != 1) { + Nan::ThrowError("Wrong number of arguments"); + } + + if (info.IsConstructCall()) { + // Invoked as a constructor with 'new ConnectionDescription()' + ConnectionDescription* obj = new ConnectionDescription(); + + v8::String::Utf8Value json_param(Nan::To(info[0]).ToLocalChecked()); + std::string media_config_string = std::string(*json_param); + json media_config = json::parse(media_config_string); + + std::vector rtp_mappings; + if (media_config.find("rtpMappings") != media_config.end()) { + json rtp_map_json = media_config["rtpMappings"]; + for (json::iterator it = rtp_map_json.begin(); it != rtp_map_json.end(); ++it) { + erizo::RtpMap rtp_map; + if (it.value()["payloadType"].is_number()) { + rtp_map.payload_type = it.value()["payloadType"]; + } else { + continue; + } + if (it.value()["encodingName"].is_string()) { + rtp_map.encoding_name = it.value()["encodingName"]; + } else { + continue; + } + if (it.value()["mediaType"].is_string()) { + if (it.value()["mediaType"] == "video") { + rtp_map.media_type = erizo::VIDEO_TYPE; + } else if (it.value()["mediaType"] == "audio") { + rtp_map.media_type = erizo::AUDIO_TYPE; + } else { + continue; + } + } else { + continue; + } + if (it.value()["clockRate"].is_number()) { + rtp_map.clock_rate = it.value()["clockRate"]; + } + if (rtp_map.media_type == erizo::AUDIO_TYPE) { + if (it.value()["channels"].is_number()) { + rtp_map.channels = it.value()["channels"]; + } + } + if (it.value()["formatParameters"].is_object()) { + json format_params_json = it.value()["formatParameters"]; + for (json::iterator params_it = format_params_json.begin(); + params_it != format_params_json.end(); ++params_it) { + std::string value = params_it.value(); + std::string key = params_it.key(); + rtp_map.format_parameters.insert(rtp_map.format_parameters.begin(), + std::pair (key, value)); + } + } + if (it.value()["feedbackTypes"].is_array()) { + json feedback_types_json = it.value()["feedbackTypes"]; + for (json::iterator feedback_it = feedback_types_json.begin(); + feedback_it != feedback_types_json.end(); ++feedback_it) { + rtp_map.feedback_types.push_back(*feedback_it); + } + } + rtp_mappings.push_back(rtp_map); + } + } + + obj->me = std::make_shared(rtp_mappings); + obj->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); + } else { + // TODO(pedro) Check what happens here + } +} + +NAN_METHOD(ConnectionDescription::setProfile) { + GET_SDP(); + std::string profile = getString(info[0]); + + if (profile == "SAVPF") { + sdp->profile = erizo::SAVPF; + } else if (profile == "AVPF") { + sdp->profile = erizo::AVPF; + } +} + +NAN_METHOD(ConnectionDescription::setBundle) { + GET_SDP(); + sdp->isBundle = info[0]->BooleanValue(); +} + +NAN_METHOD(ConnectionDescription::addBundleTag) { + GET_SDP(); + std::string id = getString(info[0]); + std::string media_type = getString(info[1]); + sdp->bundleTags.push_back({id, getMediaType(media_type)}); +} + +NAN_METHOD(ConnectionDescription::setRtcpMux) { + GET_SDP(); + sdp->isRtcpMux = info[0]->BooleanValue(); +} + +NAN_METHOD(ConnectionDescription::setAudioAndVideo) { + GET_SDP(); + sdp->hasAudio = info[0]->BooleanValue(); + sdp->hasVideo = info[1]->BooleanValue(); +} + +NAN_METHOD(ConnectionDescription::setAudioSsrc) { + GET_SDP(); + sdp->audio_ssrc = info[0]->IntegerValue(); +} + +NAN_METHOD(ConnectionDescription::setVideoSsrcList) { + GET_SDP(); + v8::Local video_ssrc_array = v8::Local::Cast(info[0]); + std::vector video_ssrc_list; + + for (unsigned int i = 0; i < video_ssrc_array->Length(); i++) { + v8::Handle val = video_ssrc_array->Get(i); + unsigned int numVal = val->IntegerValue(); + video_ssrc_list.push_back(numVal); + } + + sdp->video_ssrc_list = video_ssrc_list; +} + +NAN_METHOD(ConnectionDescription::setDirection) { + GET_SDP(); + std::string direction = getString(info[0]); + + if (direction == "sendonly") { + sdp->audioDirection = erizo::SENDONLY; + sdp->videoDirection = erizo::SENDONLY; + } else if (direction == "sendrecv") { + sdp->audioDirection = erizo::SENDRECV; + sdp->videoDirection = erizo::SENDRECV; + } else if (direction == "recvonly") { + sdp->audioDirection = erizo::RECVONLY; + sdp->videoDirection = erizo::RECVONLY; + } +} + +NAN_METHOD(ConnectionDescription::setFingerprint) { + GET_SDP(); + sdp->fingerprint = getString(info[0]); + sdp->isFingerprint = true; +} + +NAN_METHOD(ConnectionDescription::setDtlsRole) { + GET_SDP(); + std::string dtls_role = getString(info[0]); + + if (dtls_role == "actpass") { + sdp->dtlsRole = erizo::ACTPASS; + } else if (dtls_role == "passive") { + sdp->dtlsRole = erizo::PASSIVE; + } else if (dtls_role == "active") { + sdp->dtlsRole = erizo::ACTIVE; + } +} + +NAN_METHOD(ConnectionDescription::setVideoBandwidth) { + GET_SDP(); + sdp->videoBandwidth = info[0]->IntegerValue(); +} + +NAN_METHOD(ConnectionDescription::addCandidate) { + GET_SDP(); + erizo::CandidateInfo cand; + + cand.mediaType = getMediaType(getString(info[0])); + cand.foundation = getString(info[1]); + cand.componentId = info[2]->IntegerValue(); + cand.netProtocol = getString(info[3]); + cand.priority = info[4]->IntegerValue(); + cand.hostAddress = getString(info[5]); + cand.hostPort = info[6]->IntegerValue(); + + // libnice does not support tcp candidates, we ignore them + if (cand.netProtocol.compare("UDP") && cand.netProtocol.compare("udp")) { + info.GetReturnValue().Set(Nan::New(false)); + return; + } + + std::string type = getString(info[7]); + if (type == "host") { + cand.hostType = erizo::HOST; + } else if (type == "srflx") { + cand.hostType = erizo::SRFLX; + } else if (type == "prflx") { + cand.hostType = erizo::PRFLX; + } else if (type == "relay") { + cand.hostType = erizo::RELAY; + } else { + cand.hostType = erizo::HOST; + } + + if (cand.hostType == erizo::SRFLX || cand.hostType == erizo::RELAY) { + cand.rAddress = getString(info[8]); + cand.rPort = info[9]->IntegerValue(); + } + + cand.sdp = getString(info[10]); + + sdp->candidateVector_.push_back(cand); + info.GetReturnValue().Set(Nan::New(true)); +} + +NAN_METHOD(ConnectionDescription::addCryptoInfo) { + GET_SDP(); + erizo::CryptoInfo crypinfo; + + crypinfo.cipherSuite = getString(info[0]); + crypinfo.keyParams = getString(info[1]);; + crypinfo.mediaType = getMediaType(getString(info[2])); + sdp->cryptoVector_.push_back(crypinfo); +} + +NAN_METHOD(ConnectionDescription::setICECredentials) { + GET_SDP(); + std::string username = getString(info[0]); + std::string password = getString(info[1]); + erizo::MediaType media = getMediaType(getString(info[2])); + switch (media) { + case(erizo::VIDEO_TYPE): + sdp->iceVideoUsername_ = std::string(username); + sdp->iceVideoPassword_ = std::string(password); + break; + case(erizo::AUDIO_TYPE): + sdp->iceAudioUsername_ = std::string(username); + sdp->iceAudioPassword_ = std::string(password); + break; + default: + sdp->iceVideoUsername_ = std::string(username); + sdp->iceVideoPassword_ = std::string(password); + sdp->iceAudioUsername_ = std::string(username); + sdp->iceAudioPassword_ = std::string(password); + break; + } +} + +NAN_METHOD(ConnectionDescription::addRid) { + GET_SDP(); + + std::string id = getString(info[0]); + std::string direction = getString(info[1]); + + erizo::RidDirection rid_direction = erizo::RidDirection::SEND; + + if (direction == "send") { + rid_direction = erizo::RidDirection::SEND; + } else if (direction == "recv") { + rid_direction = erizo::RidDirection::RECV; + } + + sdp->rids_.push_back({id, rid_direction}); +} + +NAN_METHOD(ConnectionDescription::addPt) { + GET_SDP(); + unsigned int pt = info[0]->IntegerValue(); + std::string codec_name = getString(info[1]); + unsigned int parsed_clock = info[2]->IntegerValue(); + erizo::MediaType media = getMediaType(getString(info[3])); + + erizo::RtpMap new_mapping; + new_mapping.payload_type = pt; + new_mapping.encoding_name = codec_name; + new_mapping.clock_rate = parsed_clock; + new_mapping.media_type = media; + sdp->payload_parsed_map_[pt] = new_mapping; +} + +NAN_METHOD(ConnectionDescription::addExtension) { + GET_SDP(); + unsigned int id = info[0]->IntegerValue(); + std::string uri = getString(info[1]); + erizo::MediaType media = getMediaType(getString(info[2])); + erizo::ExtMap anExt(id, uri); + anExt.mediaType = media; + sdp->extMapVector.push_back(anExt); +} + +NAN_METHOD(ConnectionDescription::addFeedback) { + GET_SDP(); + unsigned int pt = info[0]->IntegerValue(); + std::string feedback = getString(info[1]); + auto map_element = sdp->payload_parsed_map_.find(pt); + if (map_element != sdp->payload_parsed_map_.end()) { + map_element->second.feedback_types.push_back(feedback); + } else { + erizo::RtpMap new_map; + new_map.payload_type = pt; + new_map.feedback_types.push_back(feedback); + sdp->payload_parsed_map_[pt] = new_map; + } +} + +NAN_METHOD(ConnectionDescription::addParameter) { + GET_SDP(); + unsigned int pt = info[0]->IntegerValue(); + std::string option = getString(info[1]); + std::string value = getString(info[2]); + auto map_element = sdp->payload_parsed_map_.find(pt); + if (map_element != sdp->payload_parsed_map_.end()) { + map_element->second.format_parameters[option] = value; + } else { + erizo::RtpMap new_map; + new_map.payload_type = pt; + new_map.format_parameters[option] = value; + sdp->payload_parsed_map_[pt] = new_map; + } +} + +NAN_METHOD(ConnectionDescription::postProcessInfo) { + GET_SDP(); + bool success = sdp->postProcessInfo(); + info.GetReturnValue().Set(Nan::New(success)); +} + +NAN_METHOD(ConnectionDescription::close) { + GET_SDP(); + obj->me.reset(); +} diff --git a/erizoAPI/ConnectionDescription.h b/erizoAPI/ConnectionDescription.h new file mode 100644 index 0000000000..95d6de400a --- /dev/null +++ b/erizoAPI/ConnectionDescription.h @@ -0,0 +1,64 @@ + +#ifndef ERIZOAPI_CONNECTIONDESCRIPTION_H_ +#define ERIZOAPI_CONNECTIONDESCRIPTION_H_ + +#include +#include + +/* + * Wrapper class of erizo::SdpInfo + * + */ +class ConnectionDescription : public Nan::ObjectWrap { + public: + static NAN_MODULE_INIT(Init); + + std::shared_ptr me; + + private: + ConnectionDescription(); + ~ConnectionDescription(); + + /* + * Constructor. + * Constructs an empty SdpInfo without any information. + */ + static NAN_METHOD(New); + /* + * Closes the SdpInfo. + * The object cannot be used after this call. + */ + static NAN_METHOD(close); + + static NAN_METHOD(setProfile); + static NAN_METHOD(setBundle); + static NAN_METHOD(addBundleTag); + static NAN_METHOD(setRtcpMux); + static NAN_METHOD(setAudioAndVideo); + + static NAN_METHOD(setAudioSsrc); + static NAN_METHOD(setVideoSsrcList); + + static NAN_METHOD(setDirection); + + static NAN_METHOD(setFingerprint); + static NAN_METHOD(setDtlsRole); + + static NAN_METHOD(setVideoBandwidth); + + static NAN_METHOD(addCandidate); + static NAN_METHOD(addCryptoInfo); + static NAN_METHOD(setICECredentials); + + static NAN_METHOD(addRid); + static NAN_METHOD(addPt); + static NAN_METHOD(addExtension); + static NAN_METHOD(addFeedback); + static NAN_METHOD(addParameter); + + static NAN_METHOD(postProcessInfo); + + static Nan::Persistent constructor; +}; + +#endif // ERIZOAPI_CONNECTIONDESCRIPTION_H_ diff --git a/erizoAPI/WebRtcConnection.cc b/erizoAPI/WebRtcConnection.cc index e07e278a24..81ec27b83d 100644 --- a/erizoAPI/WebRtcConnection.cc +++ b/erizoAPI/WebRtcConnection.cc @@ -40,6 +40,7 @@ NAN_MODULE_INIT(WebRtcConnection::Init) { // Prototype Nan::SetPrototypeMethod(tpl, "close", close); Nan::SetPrototypeMethod(tpl, "init", init); + Nan::SetPrototypeMethod(tpl, "setRemoteDescription", setRemoteDescription); Nan::SetPrototypeMethod(tpl, "setRemoteSdp", setRemoteSdp); Nan::SetPrototypeMethod(tpl, "addRemoteCandidate", addRemoteCandidate); Nan::SetPrototypeMethod(tpl, "getLocalSdp", getLocalSdp); @@ -251,6 +252,18 @@ NAN_METHOD(WebRtcConnection::setRemoteSdp) { info.GetReturnValue().Set(Nan::New(r)); } +NAN_METHOD(WebRtcConnection::setRemoteDescription) { + WebRtcConnection* obj = Nan::ObjectWrap::Unwrap(info.Holder()); + std::shared_ptr me = obj->me; + + ConnectionDescription* param = + Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); + auto sdp = std::shared_ptr(param->me); + + bool r = me->setRemoteSdpInfo(sdp); + info.GetReturnValue().Set(Nan::New(r)); +} + NAN_METHOD(WebRtcConnection::addRemoteCandidate) { WebRtcConnection* obj = Nan::ObjectWrap::Unwrap(info.Holder()); std::shared_ptr me = obj->me; diff --git a/erizoAPI/WebRtcConnection.h b/erizoAPI/WebRtcConnection.h index f11fbe7d67..55beb04f99 100644 --- a/erizoAPI/WebRtcConnection.h +++ b/erizoAPI/WebRtcConnection.h @@ -5,6 +5,7 @@ #include #include "MediaDefinitions.h" #include "OneToManyProcessor.h" +#include "ConnectionDescription.h" #include #include @@ -57,6 +58,12 @@ class WebRtcConnection : public erizo::WebRtcConnectionEventListener, * Returns true if the process has started successfully. */ static NAN_METHOD(createOffer); + /* + * Sets the SDP of the remote peer. + * Param: the SDP. + * Returns true if the SDP was received correctly. + */ + static NAN_METHOD(setRemoteDescription); /* * Sets the SDP of the remote peer. * Param: the SDP. diff --git a/erizoAPI/addon.cc b/erizoAPI/addon.cc index fbdf7143aa..d4966f3f9f 100644 --- a/erizoAPI/addon.cc +++ b/erizoAPI/addon.cc @@ -9,6 +9,7 @@ #include "SyntheticInput.h" #include "ExternalInput.h" #include "ExternalOutput.h" +#include "ConnectionDescription.h" #include "ThreadPool.h" #include "IOThreadPool.h" @@ -21,6 +22,7 @@ NAN_MODULE_INIT(InitAll) { SyntheticInput::Init(target); ThreadPool::Init(target); IOThreadPool::Init(target); + ConnectionDescription::Init(target); } NODE_MODULE(addon, InitAll) diff --git a/erizoAPI/binding.gyp b/erizoAPI/binding.gyp index ef2ed0c3af..ec3c91d9ca 100644 --- a/erizoAPI/binding.gyp +++ b/erizoAPI/binding.gyp @@ -1,6 +1,6 @@ { 'variables' : { - 'common_sources': [ 'addon.cc', 'IOThreadPool.cc', 'ThreadPool.cc', 'MediaStream.cc', 'WebRtcConnection.cc', 'OneToManyProcessor.cc', 'ExternalInput.cc', 'ExternalOutput.cc', 'SyntheticInput.cc'], + 'common_sources': [ 'addon.cc', 'IOThreadPool.cc', 'ThreadPool.cc', 'MediaStream.cc', 'WebRtcConnection.cc', 'OneToManyProcessor.cc', 'ExternalInput.cc', 'ExternalOutput.cc', 'SyntheticInput.cc', 'ConnectionDescription.cc'], 'common_include_dirs' : [" { + if (global.mediaConfig && global.mediaConfig.codecConfigurations) { + if (global.mediaConfig.codecConfigurations[mediaConfiguration]) { + return JSON.stringify(global.mediaConfig.codecConfigurations[mediaConfiguration]); + } else if (global.mediaConfig.codecConfigurations.default) { + return JSON.stringify(global.mediaConfig.codecConfigurations.default); + } else { + log.warn('message: Bad media config file. You need to specify a default codecConfiguration.'); + return JSON.stringify({}); + } + } else { + log.warn('message: Bad media config file. You need to specify a default codecConfiguration.'); + return JSON.stringify({}); + } +} diff --git a/erizo_controller/erizoJS/models/Publisher.js b/erizo_controller/erizoJS/models/Publisher.js index e7c2183945..847df0e355 100644 --- a/erizo_controller/erizoJS/models/Publisher.js +++ b/erizo_controller/erizoJS/models/Publisher.js @@ -2,26 +2,11 @@ 'use strict'; var addon = require('./../../../erizoAPI/build/Release/addon'); var logger = require('./../../common/logger').logger; +var Helpers = require('./Helpers'); // Logger var log = logger.getLogger('Publisher'); -function getMediaConfiguration(mediaConfiguration = 'default') { - if (global.mediaConfig && global.mediaConfig.codecConfigurations) { - if (global.mediaConfig.codecConfigurations[mediaConfiguration]) { - return JSON.stringify(global.mediaConfig.codecConfigurations[mediaConfiguration]); - } else if (global.mediaConfig.codecConfigurations.default) { - return JSON.stringify(global.mediaConfig.codecConfigurations.default); - } else { - log.warn('message: Bad media config file. You need to specify a default codecConfiguration.'); - return JSON.stringify({}); - } - } else { - log.warn('message: Bad media config file. You need to specify a default codecConfiguration.'); - return JSON.stringify({}); - } -} - function createWrtc(id, threadPool, ioThreadPool, mediaConfiguration) { var wrtc = new addon.WebRtcConnection(threadPool, ioThreadPool, id, global.config.erizo.stunserver, @@ -29,14 +14,13 @@ function createWrtc(id, threadPool, ioThreadPool, mediaConfiguration) { global.config.erizo.minport, global.config.erizo.maxport, false, - getMediaConfiguration(mediaConfiguration), + Helpers.getMediaConfiguration(mediaConfiguration), global.config.erizo.useNicer, global.config.erizo.turnserver, global.config.erizo.turnport, global.config.erizo.turnusername, global.config.erizo.turnpass, global.config.erizo.networkinterface); - return wrtc; } @@ -68,6 +52,7 @@ class Source { ', ' + logger.objectToLog(options.metadata)); var wrtc = createWrtc(wrtcId, this.threadPool, this.ioThreadPool, options.mediaConfiguration); wrtc.wrtcId = wrtcId; + wrtc.mediaConfiguration = options.mediaConfiguration; wrtc.mediaStream = createMediaStream(wrtc, id); wrtc.addMediaStream(wrtc.mediaStream); this.subscribers[id] = wrtc; @@ -103,7 +88,7 @@ class Source { var eoId = url + '_' + this.id; log.info('message: Adding ExternalOutput, id: ' + eoId); var externalOutput = new addon.ExternalOutput(url, - getMediaConfiguration(options.mediaConfiguration)); + Helpers.getMediaConfiguration(mediaConfiguration)); externalOutput.wrtcId = eoId; externalOutput.init(); this.muxer.addExternalOutput(externalOutput, url); @@ -190,8 +175,9 @@ class Publisher extends Source { constructor(id, threadPool, ioThreadPool, options) { super(id, threadPool, ioThreadPool); this.mediaConfiguration = options.mediaConfiguration; - this.wrtc = createWrtc(this.id, this.threadPool, this.ioThreadPool, this.mediaConfiguration); + this.wrtc = createWrtc(this.id, this.threadPool, this.ioThreadPool, options.mediaConfiguration); this.wrtc.wrtcId = id; + this.wrtc.mediaConfiguration = options.mediaConfiguration; this.wrtc.mediaStream = createMediaStream(this.wrtc, this.wrtc.wrtcId); this.wrtc.addMediaStream(this.wrtc.mediaStream); diff --git a/erizo_controller/erizoJS/models/SessionDescription.js b/erizo_controller/erizoJS/models/SessionDescription.js new file mode 100644 index 0000000000..22fe959a24 --- /dev/null +++ b/erizo_controller/erizoJS/models/SessionDescription.js @@ -0,0 +1,129 @@ +/*global require, exports*/ +'use strict'; +var ConnectionDescription = require('./../../../erizoAPI/build/Release/addon').ConnectionDescription; +var Direction = require('./../../common/semanticSdp/Direction'); +var DirectionWay = require('./../../common/semanticSdp/DirectionWay'); +var Setup = require('./../../common/semanticSdp/Setup'); +var logger = require('./../../common/logger').logger; +var Helpers = require('./Helpers'); + +// Logger +var log = logger.getLogger('SessionDescription'); + +class SessionDescription { + constructor(sdp, mediaConfiguration) { + this.sdp = sdp; + this.mediaConfiguration = mediaConfiguration; + this.processSdp(); + } + + processSdp() { + const info = new ConnectionDescription(Helpers.getMediaConfiguration(this.mediaConfiguration)); + const sdp = this.sdp; + let audio; + let video; + + info.setRtcpMux(true); // TODO + + // we use the same field for both audio and video + info.setDirection(Direction.toString(sdp.medias[0].getDirection())); + info.setProfile('UDP/TLS/RTP/SAVPF'); // TODO + + info.setBundle(true); // TODO + + const dtls = sdp.getDTLS(); + if (dtls) { + info.setFingerprint(dtls.getFingerprint()); + info.setDtlsRole(Setup.toString(dtls.getSetup())); + } + + sdp.medias.forEach((media) => { + const dtls = media.getDTLS(); + if (dtls) { + info.setFingerprint(dtls.getFingerprint()); + info.setDtlsRole(Setup.toString(dtls.getSetup())); + } + if (media.getType() === 'audio') { + audio = media; + } else if (media.getType() === 'video') { + video = media; + } + info.addBundleTag(media.getId(), media.getType()); + + const candidates = media.getCandidates(); + candidates.forEach((candidate) => { + info.addCandidate(media.getType(), candidate.getFoundation(), candidate.getComponentId(), + candidate.getTransport(), candidate.getPriority(), candidate.getAddress(), + candidate.getPort(), candidate.getType(), candidate.getRelAddr(), candidate.getRelPort()); + }); + + let ice = media.getICE(); + if (ice && ice.getUfrag()) { + info.setICECredentials(ice.getUfrag(), ice.getPwd(), media.getType()); + } + + media.getRIDs().forEach((ridInfo) => { + info.addRid(ridInfo.getId(), DirectionWay.toString(ridInfo.getDirection())); + }); + + media.getCodecs().forEach((codec) => { + info.addPt(codec.getType(), codec.getCodec(), codec.getRate(), media.getType()); + + const params = codec.getParams(); + Object.keys(params).forEach((option) => { + info.addParameter(codec.getType(), option, params[option]); + }); + + codec.getFeedback().forEach((rtcpFb) => { + const feedback = rtcpFb.subtype ? rtcpFb.type + ' ' + rtcpFb.subtype : rtcpFb.type; + info.addFeedback(codec.getType(), feedback); + }); + }); + + if (media.getBitrate() > 0) { + info.setVideoBandwidth(media.getBitrate()); + } + + media.getExtensions().forEach((uri, value) => { + info.addExtension(value, uri, media.getType()); + }); + }); + info.setAudioAndVideo(audio !== undefined, video !== undefined); + + let ice = sdp.getICE(); + if (ice && ice.getUfrag()) { + info.setICECredentials(ice.getUfrag(), ice.getPwd(), 'audio'); + info.setICECredentials(ice.getUfrag(), ice.getPwd(), 'video'); + } + + let videoSsrcList = []; + let simulcastVideoSsrcList; + sdp.getStreams().forEach((stream) => { + stream.getTracks().forEach((track) => { + if (track.getMedia() === 'audio') { + info.setAudioSsrc(track.getSSRCs()[0].getSSRC()); + } else if (track.getMedia() === 'video') { + track.getSSRCs().forEach((ssrc) => { + videoSsrcList.push(ssrc.getSSRC()); + }); + } + + // Google's simulcast + track.getSourceGroups().forEach((group) => { + if (group.getSemantics().toUpperCase() === 'SIM') { + simulcastVideoSsrcList = group.getSSRCs(); + } + }); + }); + }); + + videoSsrcList = simulcastVideoSsrcList || videoSsrcList; + info.setVideoSsrcList(videoSsrcList); + + info.postProcessInfo(); + + this.connectionDescription = info; + } + +} +module.exports = SessionDescription; From 3135c34ecf0e3ca1a059289c6e3e298123dfea3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cervi=C3=B1o?= Date: Fri, 22 Dec 2017 15:01:34 +0100 Subject: [PATCH 2/4] Fix lint. --- erizo_controller/erizoJS/erizoJSController.js | 12 ++++++++---- erizo_controller/erizoJS/models/Helpers.js | 6 +++++- erizo_controller/erizoJS/models/Publisher.js | 2 +- .../erizoJS/models/SessionDescription.js | 9 +++------ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/erizo_controller/erizoJS/erizoJSController.js b/erizo_controller/erizoJS/erizoJSController.js index a4d32ed06d..3465fa11f2 100644 --- a/erizo_controller/erizoJS/erizoJSController.js +++ b/erizo_controller/erizoJS/erizoJSController.js @@ -261,7 +261,8 @@ exports.ErizoJSController = function (threadPool, ioThreadPool) { const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); subscriber.remoteDescription = new SessionDescription(sdp, subscriber.mediaConfiguration); - subscriber.setRemoteDescription(subscriber.remoteDescription.connectionDescription); + subscriber.setRemoteDescription( + subscriber.remoteDescription.connectionDescription); disableDefaultHandlers(subscriber); } else if (msg.type === 'candidate') { subscriber.addRemoteCandidate(msg.candidate.sdpMid, @@ -272,7 +273,8 @@ exports.ErizoJSController = function (threadPool, ioThreadPool) { const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); subscriber.remoteDescription = new SessionDescription(sdp, subscriber.mediaConfiguration); - subscriber.setRemoteDescription(subscriber.remoteDescription.connectionDescription); + subscriber.setRemoteDescription( + subscriber.remoteDescription.connectionDescription); } if (msg.config) { if (msg.config.slideShowMode !== undefined) { @@ -296,7 +298,8 @@ exports.ErizoJSController = function (threadPool, ioThreadPool) { const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); publisher.remoteDescription = new SessionDescription(sdp, publisher.mediaConfiguration); - publisher.wrtc.setRemoteDescription(publisher.remoteDescription.connectionDescription); + publisher.wrtc.setRemoteDescription( + publisher.remoteDescription.connectionDescription); disableDefaultHandlers(publisher.wrtc); } else if (msg.type === 'candidate') { publisher.wrtc.addRemoteCandidate(msg.candidate.sdpMid, @@ -307,7 +310,8 @@ exports.ErizoJSController = function (threadPool, ioThreadPool) { const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); publisher.remoteDescription = new SessionDescription(sdp, publisher.mediaConfiguration); - publisher.wrtc.setRemoteDescription(publisher.remoteDescription.connectionDescription); + publisher.wrtc.setRemoteDescription( + publisher.remoteDescription.connectionDescription); } if (msg.config) { if (msg.config.minVideoBW) { diff --git a/erizo_controller/erizoJS/models/Helpers.js b/erizo_controller/erizoJS/models/Helpers.js index e2cd123f08..2ec709e03a 100644 --- a/erizo_controller/erizoJS/models/Helpers.js +++ b/erizo_controller/erizoJS/models/Helpers.js @@ -1,5 +1,9 @@ /*global require, exports*/ 'use strict'; +var logger = require('./../../common/logger').logger; + +// Logger +var log = logger.getLogger('Helpers'); exports.getMediaConfiguration = (mediaConfiguration = 'default') => { if (global.mediaConfig && global.mediaConfig.codecConfigurations) { @@ -15,4 +19,4 @@ exports.getMediaConfiguration = (mediaConfiguration = 'default') => { log.warn('message: Bad media config file. You need to specify a default codecConfiguration.'); return JSON.stringify({}); } -} +}; diff --git a/erizo_controller/erizoJS/models/Publisher.js b/erizo_controller/erizoJS/models/Publisher.js index 847df0e355..4c6f0751ea 100644 --- a/erizo_controller/erizoJS/models/Publisher.js +++ b/erizo_controller/erizoJS/models/Publisher.js @@ -88,7 +88,7 @@ class Source { var eoId = url + '_' + this.id; log.info('message: Adding ExternalOutput, id: ' + eoId); var externalOutput = new addon.ExternalOutput(url, - Helpers.getMediaConfiguration(mediaConfiguration)); + Helpers.getMediaConfiguration(options.mediaConfiguration)); externalOutput.wrtcId = eoId; externalOutput.init(); this.muxer.addExternalOutput(externalOutput, url); diff --git a/erizo_controller/erizoJS/models/SessionDescription.js b/erizo_controller/erizoJS/models/SessionDescription.js index 22fe959a24..de3a2e35d5 100644 --- a/erizo_controller/erizoJS/models/SessionDescription.js +++ b/erizo_controller/erizoJS/models/SessionDescription.js @@ -1,15 +1,12 @@ -/*global require, exports*/ +/*global require*/ 'use strict'; -var ConnectionDescription = require('./../../../erizoAPI/build/Release/addon').ConnectionDescription; +var ConnectionDescription = require('./../../../erizoAPI/build/Release/addon') + .ConnectionDescription; var Direction = require('./../../common/semanticSdp/Direction'); var DirectionWay = require('./../../common/semanticSdp/DirectionWay'); var Setup = require('./../../common/semanticSdp/Setup'); -var logger = require('./../../common/logger').logger; var Helpers = require('./Helpers'); -// Logger -var log = logger.getLogger('SessionDescription'); - class SessionDescription { constructor(sdp, mediaConfiguration) { this.sdp = sdp; From 95c9117ea7c3d80eb8d42a5c149f2fbb26ccaadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cervi=C3=B1o?= Date: Fri, 22 Dec 2017 15:09:19 +0100 Subject: [PATCH 3/4] Update unit tests --- .../erizoJS/models/SessionDescription.js | 5 ++++- erizo_controller/test/erizoJS/erizoJSController.js | 8 ++++---- erizo_controller/test/utils.js | 12 ++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erizo_controller/erizoJS/models/SessionDescription.js b/erizo_controller/erizoJS/models/SessionDescription.js index de3a2e35d5..1d1359eb35 100644 --- a/erizo_controller/erizoJS/models/SessionDescription.js +++ b/erizo_controller/erizoJS/models/SessionDescription.js @@ -23,7 +23,10 @@ class SessionDescription { info.setRtcpMux(true); // TODO // we use the same field for both audio and video - info.setDirection(Direction.toString(sdp.medias[0].getDirection())); + if (sdp.medias && sdp.medias.length > 0) { + info.setDirection(Direction.toString(sdp.medias[0].getDirection())); + } + info.setProfile('UDP/TLS/RTP/SAVPF'); // TODO info.setBundle(true); // TODO diff --git a/erizo_controller/test/erizoJS/erizoJSController.js b/erizo_controller/test/erizoJS/erizoJSController.js index 8f561de750..9407b73edc 100644 --- a/erizo_controller/test/erizoJS/erizoJSController.js +++ b/erizo_controller/test/erizoJS/erizoJSController.js @@ -278,7 +278,7 @@ describe('Erizo JS Controller', function() { it('should set remote sdp when received', function() { controller.processSignaling(kArbitraryId, undefined, {type: 'offer', sdp: ''}); - expect(mocks.WebRtcConnection.setRemoteSdp.callCount).to.equal(1); + expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should set candidate when received', function() { @@ -294,7 +294,7 @@ describe('Erizo JS Controller', function() { type: 'updatestream', sdp: 'sdp'}); - expect(mocks.WebRtcConnection.setRemoteSdp.callCount).to.equal(1); + expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); }); @@ -351,7 +351,7 @@ describe('Erizo JS Controller', function() { controller.processSignaling(kArbitraryId, kArbitraryId2, {type: 'offer', sdp: ''}); - expect(mocks.WebRtcConnection.setRemoteSdp.callCount).to.equal(1); + expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should set candidate when received', function() { @@ -367,7 +367,7 @@ describe('Erizo JS Controller', function() { type: 'updatestream', sdp: 'aaa'}); - expect(mocks.WebRtcConnection.setRemoteSdp.callCount).to.equal(1); + expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should mute and unmute subscriber stream', function() { diff --git a/erizo_controller/test/utils.js b/erizo_controller/test/utils.js index 6de430cdb1..970976cf43 100644 --- a/erizo_controller/test/utils.js +++ b/erizo_controller/test/utils.js @@ -136,12 +136,23 @@ var reset = module.exports.reset = function() { close: sinon.stub(), }; + module.exports.ConnectionDescription = { + close: sinon.stub(), + setRtcpMux: sinon.stub(), + setProfile: sinon.stub(), + setBundle: sinon.stub(), + setAudioAndVideo: sinon.stub(), + setVideoSsrcList: sinon.stub(), + postProcessInfo: sinon.stub(), + }; + module.exports.WebRtcConnection = { wrtcId: '', init: sinon.stub(), close: sinon.stub(), createOffer: sinon.stub(), setRemoteSdp: sinon.stub(), + setRemoteDescription: sinon.stub(), addRemoteCandidate: sinon.stub(), addMediaStream: sinon.stub(), }; @@ -175,6 +186,7 @@ var reset = module.exports.reset = function() { module.exports.erizoAPI = createMock('../../erizoAPI/build/Release/addon', { OneToManyProcessor: sinon.stub().returns(module.exports.OneToManyProcessor), + ConnectionDescription: sinon.stub().returns(module.exports.ConnectionDescription), WebRtcConnection: sinon.stub().returns(module.exports.WebRtcConnection), MediaStream: sinon.stub().returns(module.exports.MediaStream), ExternalInput: sinon.stub().returns(module.exports.ExternalInput), From 4df92b8f491b8351de87ddd6ec34aeb9a5d433b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cervi=C3=B1o?= Date: Tue, 2 Jan 2018 10:27:58 +0100 Subject: [PATCH 4/4] Make lint happy --- erizo/src/erizo/SdpInfo.h | 1 - 1 file changed, 1 deletion(-) diff --git a/erizo/src/erizo/SdpInfo.h b/erizo/src/erizo/SdpInfo.h index 2367ef3928..58c2fbcf73 100644 --- a/erizo/src/erizo/SdpInfo.h +++ b/erizo/src/erizo/SdpInfo.h @@ -331,7 +331,6 @@ class SdpInfo { std::string stringifyCandidate(const CandidateInfo & candidate); void gen_random(char* s, int len); void maybeAddSsrcToList(uint32_t ssrc); - }; } // namespace erizo #endif // ERIZO_SRC_ERIZO_SDPINFO_H_