diff --git a/Dockerfile b/Dockerfile index df6001b98f..bfde1bafa1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y git wget curl COPY .nvmrc package.json /opt/licode/ -COPY scripts/installUbuntuDeps.sh scripts/checkNvm.sh scripts/libnice-014.patch0 /opt/licode/scripts/ +COPY scripts/installUbuntuDeps.sh scripts/checkNvm.sh /opt/licode/scripts/ WORKDIR /opt/licode/scripts diff --git a/erizo/src/CMakeLists.txt b/erizo/src/CMakeLists.txt index 75d7610385..c3b3495ba2 100644 --- a/erizo/src/CMakeLists.txt +++ b/erizo/src/CMakeLists.txt @@ -67,7 +67,6 @@ include_directories(${GLIB_INCLUDE_DIRS}) # BOOST set (BOOST_LIBS thread regex system) find_package(Boost COMPONENTS ${BOOST_LIBS} REQUIRED) -set(ERIZO_CMAKE_CXX_FLAGS "${ERIZO_CMAKE_CXX_FLAGS} -DBOOST_THREAD_PROVIDES_FUTURE -DBOOST_THREAD_PROVIDES_FUTURE_CONTINUATION -DBOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY") # GTHREAD find_library(GTHREAD gthread-2.0 HINTS "${THIRD_PARTY_LIB}") diff --git a/erizo/src/erizo/MediaStream.cpp b/erizo/src/erizo/MediaStream.cpp index 4aa897e35a..3c4cb17c5a 100644 --- a/erizo/src/erizo/MediaStream.cpp +++ b/erizo/src/erizo/MediaStream.cpp @@ -91,7 +91,6 @@ MediaStream::MediaStream(std::shared_ptr worker, rate_control_ = 0; sending_ = true; - ready_ = false; } MediaStream::~MediaStream() { @@ -126,7 +125,6 @@ void MediaStream::syncClose() { return; } sending_ = false; - ready_ = false; video_sink_ = nullptr; audio_sink_ = nullptr; fb_sink_ = nullptr; @@ -144,10 +142,7 @@ void MediaStream::close() { }); } -bool MediaStream::init(bool doNotWaitForRemoteSdp) { - if (doNotWaitForRemoteSdp) { - ready_ = true; - } +bool MediaStream::init() { return true; } @@ -160,48 +155,28 @@ bool MediaStream::isSinkSSRC(uint32_t ssrc) { } bool MediaStream::setRemoteSdp(std::shared_ptr sdp) { - ELOG_DEBUG("%s message: setting remote SDP to Stream, sending: %d, initialized: %d", - toLog(), sending_, pipeline_initialized_); + ELOG_DEBUG("%s message: setting remote SDP", toLog()); if (!sending_) { return true; } - - std::shared_ptr remote_sdp = std::make_shared(*sdp.get()); - auto video_ssrc_list_it = remote_sdp->video_ssrc_map.find(getLabel()); - auto audio_ssrc_it = remote_sdp->audio_ssrc_map.find(getLabel()); - - if (isPublisher() && !ready_) { - bool stream_found = false; - - if (video_ssrc_list_it != remote_sdp->video_ssrc_map.end() || - audio_ssrc_it != remote_sdp->audio_ssrc_map.end()) { - stream_found = true; - } - - if (!stream_found) { - return true; - } - } - - remote_sdp_ = remote_sdp; - + remote_sdp_ = std::make_shared(*sdp.get()); if (remote_sdp_->videoBandwidth != 0) { ELOG_DEBUG("%s message: Setting remote BW, maxVideoBW: %u", toLog(), remote_sdp_->videoBandwidth); this->rtcp_processor_->setMaxVideoBW(remote_sdp_->videoBandwidth*1000); } - ready_ = true; - if (pipeline_initialized_ && pipeline_) { pipeline_->notifyUpdate(); return true; } bundle_ = remote_sdp_->isBundle; + auto video_ssrc_list_it = remote_sdp_->video_ssrc_map.find(getLabel()); if (video_ssrc_list_it != remote_sdp_->video_ssrc_map.end()) { setVideoSourceSSRCList(video_ssrc_list_it->second); } + auto audio_ssrc_it = remote_sdp_->audio_ssrc_map.find(getLabel()); if (audio_ssrc_it != remote_sdp_->audio_ssrc_map.end()) { setAudioSourceSSRC(audio_ssrc_it->second); } @@ -229,8 +204,6 @@ bool MediaStream::setRemoteSdp(std::shared_ptr sdp) { initializeStats(); - notifyMediaStreamEvent("ready", ""); - return true; } @@ -374,9 +347,6 @@ void MediaStream::printStats() { } void MediaStream::initializePipeline() { - if (pipeline_initialized_) { - return; - } handler_manager_ = std::make_shared(shared_from_this()); pipeline_->addService(shared_from_this()); pipeline_->addService(handler_manager_); @@ -774,16 +744,13 @@ void MediaStream::notifyUpdateToHandlers() { }); } -boost::future MediaStream::asyncTask(std::function)> f) { - auto task_promise = std::make_shared>(); +void MediaStream::asyncTask(std::function)> f) { std::weak_ptr weak_this = shared_from_this(); - worker_->task([weak_this, f, task_promise] { + worker_->task([weak_this, f] { if (auto this_ptr = weak_this.lock()) { f(this_ptr); } - task_promise->set_value(); }); - return task_promise->get_future(); } void MediaStream::sendPacket(std::shared_ptr p) { diff --git a/erizo/src/erizo/MediaStream.h b/erizo/src/erizo/MediaStream.h index ee44cee980..634cecabef 100644 --- a/erizo/src/erizo/MediaStream.h +++ b/erizo/src/erizo/MediaStream.h @@ -3,7 +3,6 @@ #define ERIZO_SRC_ERIZO_MEDIASTREAM_H_ #include -#include #include #include @@ -69,7 +68,7 @@ class MediaStream: public MediaSink, public MediaSource, public FeedbackSink, * Destructor. */ virtual ~MediaStream(); - bool init(bool doNotWaitForRemoteSdp); + bool init(); void close() override; virtual uint32_t getMaxVideoBW(); virtual uint32_t getBitrateFromMaxQualityLayer() { return bitrate_from_max_quality_layer_; } @@ -129,8 +128,7 @@ class MediaStream: public MediaSink, public MediaSource, public FeedbackSink, void notifyToEventSink(MediaEventPtr event); - - boost::future asyncTask(std::function)> f); + void asyncTask(std::function)> f); void initializeStats(); void printStats(); @@ -157,7 +155,6 @@ class MediaStream: public MediaSink, public MediaSource, public FeedbackSink, bool isPipelineInitialized() { return pipeline_initialized_; } bool isRunning() { return pipeline_initialized_ && sending_; } - bool isReady() { return ready_; } Pipeline::Ptr getPipeline() { return pipeline_; } bool isPublisher() { return is_publisher_; } void setBitrateFromMaxQualityLayer(uint64_t bitrate) { bitrate_from_max_quality_layer_ = bitrate; } @@ -189,7 +186,6 @@ class MediaStream: public MediaSink, public MediaSource, public FeedbackSink, bool should_send_feedback_; bool slide_show_mode_; bool sending_; - bool ready_; int bundle_; uint32_t rate_control_; // Target bitrate for hacky rate control in BPS diff --git a/erizo/src/erizo/NicerConnection.cpp b/erizo/src/erizo/NicerConnection.cpp index a5871a869a..8b6243fcdb 100644 --- a/erizo/src/erizo/NicerConnection.cpp +++ b/erizo/src/erizo/NicerConnection.cpp @@ -162,9 +162,6 @@ void NicerConnection::async(function)> f) } void NicerConnection::start() { - ufrag_ = getNewUfrag(); - upass_ = getNewPwd(); - async([] (std::shared_ptr this_ptr) { this_ptr->startSync(); }); @@ -173,6 +170,8 @@ void NicerConnection::start() { void NicerConnection::startSync() { UINT4 flags = NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; + ufrag_ = getNewUfrag(); + upass_ = getNewPwd(); if (ufrag_.empty() || upass_.empty()) { start_promise_.set_value(); return; diff --git a/erizo/src/erizo/SdpInfo.cpp b/erizo/src/erizo/SdpInfo.cpp index 1d324c968c..25f9224727 100644 --- a/erizo/src/erizo/SdpInfo.cpp +++ b/erizo/src/erizo/SdpInfo.cpp @@ -54,7 +54,6 @@ namespace erizo { isRtcpMux = false; isFingerprint = false; dtlsRole = ACTPASS; - internal_dtls_role = ACTPASS; hasAudio = false; hasVideo = false; profile = SAVPF; @@ -473,12 +472,8 @@ namespace erizo { // TODO(pedro): Should provide hints void SdpInfo::createOfferSdp(bool videoEnabled, bool audioEnabled, bool bundle) { - ELOG_DEBUG("Creating offerSDP: video %d, audio %d, bundle %d, payloadVector: %d, extSize: %d", - videoEnabled, audioEnabled, bundle, payloadVector.size(), extMapVector.size()); - if (payloadVector.size() == 0) { - payloadVector = internalPayloadVector_; - } - + ELOG_DEBUG("Creating offerSDP: video %d, audio %d, bundle %d", videoEnabled, audioEnabled, bundle); + this->payloadVector = internalPayloadVector_; this->isBundle = bundle; this->profile = SAVPF; this->isRtcpMux = true; @@ -487,8 +482,8 @@ namespace erizo { if (audioEnabled) this->audioSdpMLine = 0; - for (unsigned int it = 0; it < payloadVector.size(); it++) { - RtpMap& rtp = payloadVector[it]; + for (unsigned int it = 0; it < internalPayloadVector_.size(); it++) { + RtpMap& rtp = internalPayloadVector_[it]; if (rtp.media_type == VIDEO_TYPE) { videoCodecs++; } else if (rtp.media_type == AUDIO_TYPE) { @@ -503,17 +498,6 @@ namespace erizo { ELOG_DEBUG("Setting Offer SDP"); } - void SdpInfo::copyInfoFromSdp(std::shared_ptr offerSdp) { - payloadVector = offerSdp->payloadVector; - videoCodecs = offerSdp->videoCodecs; - audioCodecs = offerSdp->audioCodecs; - inOutPTMap = offerSdp->inOutPTMap; - outInPTMap = offerSdp->outInPTMap; - extMapVector = offerSdp->extMapVector; - ELOG_DEBUG("Offer SDP successfully copied, extSize: %d, payloadSize: %d, videoCodecs: %d, audioCodecs: %d", - extMapVector.size(), payloadVector.size(), videoCodecs, audioCodecs); - } - void SdpInfo::setOfferSdp(std::shared_ptr offerSdp) { this->videoCodecs = offerSdp->videoCodecs; this->audioCodecs = offerSdp->audioCodecs; diff --git a/erizo/src/erizo/SdpInfo.h b/erizo/src/erizo/SdpInfo.h index 416f40c5c0..674f4fce00 100644 --- a/erizo/src/erizo/SdpInfo.h +++ b/erizo/src/erizo/SdpInfo.h @@ -238,8 +238,6 @@ class SdpInfo { bool supportPayloadType(const unsigned int payloadType); void createOfferSdp(bool videoEnabled, bool audioEnabled, bool bundle); - - void copyInfoFromSdp(std::shared_ptr offerSdp); /** * @brief copies relevant information from the offer sdp for which this will be an answer sdp * @param offerSdp The offer SDP as received via signaling and parsed @@ -297,10 +295,6 @@ class SdpInfo { */ DtlsRole dtlsRole; /** - * Internal DTLS Role - */ - DtlsRole internal_dtls_role; - /** * Mapping from internal PT (key) to external PT (value) */ std::map inOutPTMap; diff --git a/erizo/src/erizo/WebRtcConnection.cpp b/erizo/src/erizo/WebRtcConnection.cpp index 63dc240e9b..38fe2dba06 100644 --- a/erizo/src/erizo/WebRtcConnection.cpp +++ b/erizo/src/erizo/WebRtcConnection.cpp @@ -2,6 +2,7 @@ * WebRTCConnection.cpp */ +#include #include #include #include @@ -97,63 +98,41 @@ void WebRtcConnection::close() { } bool WebRtcConnection::init() { - maybeNotifyWebRtcConnectionEvent(global_state_, ""); - return true; -} - -boost::future WebRtcConnection::createOffer(bool video_enabled, bool audio_enabled, bool bundle) { - return asyncTask([video_enabled, audio_enabled, bundle] (std::shared_ptr connection) { - connection->createOfferSync(video_enabled, audio_enabled, bundle); - }); + maybeNotifyWebRtcConnectionEvent(global_state_, ""); + return true; } -bool WebRtcConnection::createOfferSync(bool video_enabled, bool audio_enabled, bool bundle) { +bool WebRtcConnection::createOffer(bool video_enabled, bool audioEnabled, bool bundle) { boost::mutex::scoped_lock lock(update_state_mutex_); bundle_ = bundle; video_enabled_ = video_enabled; - audio_enabled_ = audio_enabled; + audio_enabled_ = audioEnabled; local_sdp_->createOfferSdp(video_enabled_, audio_enabled_, bundle_); - local_sdp_->dtlsRole = ACTPASS; - if (local_sdp_->internal_dtls_role == ACTPASS) { - local_sdp_->internal_dtls_role = PASSIVE; - } - ELOG_DEBUG("%s message: Creating sdp offer, isBundle: %d, setup: %d", - toLog(), bundle_, local_sdp_->internal_dtls_role); + ELOG_DEBUG("%s message: Creating sdp offer, isBundle: %d", toLog(), bundle_); - forEachMediaStream([this] (const std::shared_ptr &media_stream) { - if (!media_stream->isReady() || media_stream->isPublisher()) { - ELOG_DEBUG("%s message: getting local SDPInfo stream not running, stream_id: %s", toLog(), media_stream->getId()); - return; - } - if (video_enabled_) { + if (video_enabled_) { + forEachMediaStream([this] (const std::shared_ptr &media_stream) { std::vector video_ssrc_list = std::vector(); - if (media_stream->getVideoSinkSSRC() != kDefaultVideoSinkSSRC && media_stream->getVideoSinkSSRC() != 0) { - video_ssrc_list.push_back(media_stream->getVideoSinkSSRC()); - } - ELOG_DEBUG("%s message: getting local SDPInfo, stream_id: %s, audio_ssrc: %u", - toLog(), media_stream->getId(), media_stream->getAudioSinkSSRC()); - if (!video_ssrc_list.empty()) { - local_sdp_->video_ssrc_map[media_stream->getLabel()] = video_ssrc_list; - } - } - if (audio_enabled_) { - if (media_stream->getAudioSinkSSRC() != kDefaultAudioSinkSSRC && media_stream->getAudioSinkSSRC() != 0) { - local_sdp_->audio_ssrc_map[media_stream->getLabel()] = media_stream->getAudioSinkSSRC(); - } - } - }); + video_ssrc_list.push_back(media_stream->getVideoSinkSSRC()); + local_sdp_->video_ssrc_map[media_stream->getLabel()] = video_ssrc_list; + }); + } + if (audio_enabled_) { + forEachMediaStream([this] (const std::shared_ptr &media_stream) { + local_sdp_->audio_ssrc_map[media_stream->getLabel()] = media_stream->getAudioSinkSSRC(); + }); + } + auto listener = std::dynamic_pointer_cast(shared_from_this()); if (bundle_) { - if (video_transport_.get() == nullptr && (video_enabled_ || audio_enabled_)) { - video_transport_.reset(new DtlsTransport(VIDEO_TYPE, "video", connection_id_, bundle_, true, - listener, ice_config_ , "", "", true, worker_, io_worker_)); - video_transport_->copyLogContextFrom(*this); - video_transport_->start(); - } + video_transport_.reset(new DtlsTransport(VIDEO_TYPE, "video", connection_id_, bundle_, true, + listener, ice_config_ , "", "", true, worker_, io_worker_)); + video_transport_->copyLogContextFrom(*this); + video_transport_->start(); } else { if (video_transport_.get() == nullptr && video_enabled_) { // For now we don't re/check transports, if they are already created we leave them there @@ -176,15 +155,15 @@ bool WebRtcConnection::createOfferSync(bool video_enabled, bool audio_enabled, b return true; } -boost::future WebRtcConnection::addMediaStream(std::shared_ptr media_stream) { - return asyncTask([media_stream] (std::shared_ptr connection) { +void WebRtcConnection::addMediaStream(std::shared_ptr media_stream) { + asyncTask([media_stream] (std::shared_ptr connection) { ELOG_DEBUG("%s message: Adding mediaStream, id: %s", connection->toLog(), media_stream->getId().c_str()); connection->media_streams_.push_back(media_stream); }); } -boost::future WebRtcConnection::removeMediaStream(const std::string& stream_id) { - return asyncTask([stream_id] (std::shared_ptr connection) { +void WebRtcConnection::removeMediaStream(const std::string& stream_id) { + asyncTask([stream_id] (std::shared_ptr connection) { boost::mutex::scoped_lock lock(connection->update_state_mutex_); ELOG_DEBUG("%s message: removing mediaStream, id: %s", connection->toLog(), stream_id.c_str()); connection->media_streams_.erase(std::remove_if(connection->media_streams_.begin(), @@ -210,88 +189,34 @@ void WebRtcConnection::forEachMediaStream(std::function WebRtcConnection::forEachMediaStreamAsync( - std::function&)> func) { - auto futures = std::make_shared>>(); - std::for_each(media_streams_.begin(), media_streams_.end(), - [func, futures] (const std::shared_ptr &stream) { - futures->push_back(stream->asyncTask([func] (const std::shared_ptr &stream) { - func(stream); - })); - }); - auto future_when = boost::when_all(futures->begin(), futures->end()); - - // This task will show issues with futures that are not completed in time - // TODO(javier): Remove it once we check this does not happen anymore - std::shared_ptr task = worker_->scheduleFromNow([futures]() { - int number_of_unfinished_futures = 0; - std::for_each(futures->begin(), futures->end(), - [&number_of_unfinished_futures](const boost::future &future) { - if (!future.is_ready()) { - number_of_unfinished_futures++; - } - }); - if (number_of_unfinished_futures > 0) { - ELOG_ERROR("message: Future not ready after 10 seconds, unfinished: %d, total: %d", - number_of_unfinished_futures, futures->size()); - } - }, std::chrono::seconds(10)); - - return future_when.then([futures, task](decltype(future_when)) { - task->cancel(); - // free the list of futures that is used by boost::when_all() - }); -} - -void WebRtcConnection::forEachMediaStreamAsyncNoPromise( - std::function&)> func) { +void WebRtcConnection::forEachMediaStreamAsync(std::function&)> func) { std::for_each(media_streams_.begin(), media_streams_.end(), [func] (const std::shared_ptr &stream) { - stream->asyncTask([func] (const std::shared_ptr &stream) { - func(stream); - }); + stream->asyncTask([func] (const std::shared_ptr &stream) { + func(stream); + }); }); } -boost::future WebRtcConnection::setRemoteSdpInfo( - std::shared_ptr sdp) { - std::weak_ptr weak_this = shared_from_this(); - auto task_promise = std::make_shared>(); - worker_->task([weak_this, sdp, task_promise] { - if (auto connection = weak_this.lock()) { - ELOG_DEBUG("%s message: setting remote SDPInfo", connection->toLog()); - if (!connection->sending_) { - task_promise->set_value(); - return; - } - connection->remote_sdp_ = sdp; - auto futures = std::make_shared>>(); - boost::future future = connection->processRemoteSdp().then([task_promise, futures] (boost::future) { - task_promise->set_value(); - }); - futures->push_back(move(future)); +bool WebRtcConnection::setRemoteSdpInfo(std::shared_ptr sdp, std::string stream_id) { + asyncTask([sdp, stream_id] (std::shared_ptr connection) { + ELOG_DEBUG("%s message: setting remote SDPInfo", connection->toLog()); + + if (!connection->sending_) { return; } - task_promise->set_value(); - }); - - return task_promise->get_future(); -} -void WebRtcConnection::copyDataToLocalSdpIndo(std::shared_ptr sdp_info) { - asyncTask([sdp_info] (std::shared_ptr connection) { - if (connection->sending_) { - connection->local_sdp_->copyInfoFromSdp(sdp_info); - connection->local_sdp_->updateSupportedExtensionMap(connection->extension_processor_.getSupportedExtensionMap()); - } + connection->remote_sdp_ = sdp; + connection->processRemoteSdp(stream_id); }); + return true; } std::shared_ptr WebRtcConnection::getLocalSdpInfo() { boost::mutex::scoped_lock lock(update_state_mutex_); ELOG_DEBUG("%s message: getting local SDPInfo", toLog()); forEachMediaStream([this] (const std::shared_ptr &media_stream) { - if (!media_stream->isReady() || media_stream->isPublisher()) { + if (!media_stream->isRunning() || media_stream->isPublisher()) { ELOG_DEBUG("%s message: getting local SDPInfo stream not running, stream_id: %s", toLog(), media_stream->getId()); return; } @@ -337,48 +262,56 @@ std::shared_ptr WebRtcConnection::getLocalSdpInfo() { return local_sdp_; } -boost::future WebRtcConnection::setRemoteSdp(const std::string &sdp) { - std::shared_ptr> p = std::make_shared>(); - boost::future f = p->get_future(); - asyncTask([sdp, p] (std::shared_ptr connection) { +bool WebRtcConnection::setRemoteSdp(const std::string &sdp, std::string stream_id) { + asyncTask([sdp, stream_id] (std::shared_ptr connection) { ELOG_DEBUG("%s message: setting remote SDP", connection->toLog()); if (!connection->sending_) { - p->set_value(); return; } connection->remote_sdp_->initWithSdp(sdp, ""); - boost::future f = connection->processRemoteSdp(); - f.then([p](boost::future future) { - p->set_value(); - }); + connection->processRemoteSdp(stream_id); }); - return f; + return true; } -boost::future WebRtcConnection::setRemoteSdpsToMediaStreams() { - ELOG_DEBUG("%s message: setting remote SDP, streams: %d", toLog(), media_streams_.size()); - std::weak_ptr weak_this = shared_from_this(); +void WebRtcConnection::setRemoteSdpsToMediaStreams(std::string stream_id) { + ELOG_DEBUG("%s message: setting remote SDP, stream: %s", toLog(), stream_id); - return forEachMediaStreamAsync([weak_this](std::shared_ptr media_stream) { - if (auto connection = weak_this.lock()) { - media_stream->setRemoteSdp(connection->remote_sdp_); - ELOG_DEBUG("%s message: setting remote SDP to stream, stream: %s", - connection->toLog(), media_stream->getId()); - } + auto stream = std::find_if(media_streams_.begin(), media_streams_.end(), + [stream_id, this](const std::shared_ptr &media_stream) { + ELOG_DEBUG("%s message: setting remote SDP, stream: %s, stream_id: %s", + toLog(), media_stream->getId(), stream_id); + return media_stream->getId() == stream_id; + }); + + if (stream != media_streams_.end()) { + std::weak_ptr weak_this = shared_from_this(); + (*stream)->asyncTask([weak_this, stream_id] (const std::shared_ptr &media_stream) { + if (auto connection = weak_this.lock()) { + media_stream->setRemoteSdp(connection->remote_sdp_); + ELOG_DEBUG("%s message: setting remote SDP to stream, stream: %s", connection->toLog(), media_stream->getId()); + connection->onRemoteSdpsSetToMediaStreams(stream_id); + } + }); + } else { + onRemoteSdpsSetToMediaStreams(stream_id); + } +} + +void WebRtcConnection::onRemoteSdpsSetToMediaStreams(std::string stream_id) { + asyncTask([stream_id] (std::shared_ptr connection) { + ELOG_DEBUG("%s message: SDP processed", connection->toLog()); + std::string sdp = connection->getLocalSdp(); + connection->maybeNotifyWebRtcConnectionEvent(CONN_SDP_PROCESSED, sdp, stream_id); }); } -boost::future WebRtcConnection::processRemoteSdp() { +bool WebRtcConnection::processRemoteSdp(std::string stream_id) { ELOG_DEBUG("%s message: processing remote SDP", toLog()); - if (!first_remote_sdp_processed_ && local_sdp_->internal_dtls_role == ACTPASS) { - local_sdp_->internal_dtls_role = ACTIVE; - } - local_sdp_->dtlsRole = local_sdp_->internal_dtls_role; - ELOG_DEBUG("%s message: process remote sdp, setup: %d", toLog(), local_sdp_->internal_dtls_role); - if (first_remote_sdp_processed_) { - return setRemoteSdpsToMediaStreams(); + setRemoteSdpsToMediaStreams(stream_id); + return true; } bundle_ = remote_sdp_->isBundle; @@ -386,6 +319,9 @@ boost::future WebRtcConnection::processRemoteSdp() { extension_processor_.setSdpInfo(local_sdp_); local_sdp_->updateSupportedExtensionMap(extension_processor_.getSupportedExtensionMap()); + if (remote_sdp_->dtlsRole == ACTPASS) { + local_sdp_->dtlsRole = ACTIVE; + } audio_enabled_ = remote_sdp_->hasAudio; video_enabled_ = remote_sdp_->hasVideo; @@ -440,18 +376,13 @@ boost::future WebRtcConnection::processRemoteSdp() { } } } - + setRemoteSdpsToMediaStreams(stream_id); first_remote_sdp_processed_ = true; - return setRemoteSdpsToMediaStreams(); + return true; } -boost::future WebRtcConnection::addRemoteCandidate(std::string mid, int mLineIndex, std::string sdp) { - return asyncTask([mid, mLineIndex, sdp] (std::shared_ptr connection) { - connection->addRemoteCandidateSync(mid, mLineIndex, sdp); - }); -} -bool WebRtcConnection::addRemoteCandidateSync(std::string mid, int mLineIndex, std::string sdp) { +bool WebRtcConnection::addRemoteCandidate(const std::string &mid, int mLineIndex, const std::string &sdp) { // TODO(pedro) Check type of transport. ELOG_DEBUG("%s message: Adding remote Candidate, candidate: %s, mid: %s, sdpMLine: %d", toLog(), sdp.c_str(), mid.c_str(), mLineIndex); @@ -607,25 +538,22 @@ void WebRtcConnection::onTransportData(std::shared_ptr packet, Trans } } -void WebRtcConnection::maybeNotifyWebRtcConnectionEvent(const WebRTCEvent& event, const std::string& message) { +void WebRtcConnection::maybeNotifyWebRtcConnectionEvent(const WebRTCEvent& event, const std::string& message, + const std::string& stream_id) { boost::mutex::scoped_lock lock(event_listener_mutex_); if (!conn_event_listener_) { return; } - conn_event_listener_->notifyEvent(event, message); + conn_event_listener_->notifyEvent(event, message, stream_id); } -boost::future WebRtcConnection::asyncTask( - std::function)> f) { - auto task_promise = std::make_shared>(); +void WebRtcConnection::asyncTask(std::function)> f) { std::weak_ptr weak_this = shared_from_this(); - worker_->task([weak_this, f, task_promise] { + worker_->task([weak_this, f] { if (auto this_ptr = weak_this.lock()) { f(this_ptr); } - task_promise->set_value(); }); - return task_promise->get_future(); } void WebRtcConnection::updateState(TransportState state, Transport * transport) { @@ -687,7 +615,7 @@ void WebRtcConnection::updateState(TransportState state, Transport * transport) if (bundle_) { temp = CONN_READY; trackTransportInfo(); - forEachMediaStreamAsyncNoPromise([] (const std::shared_ptr &media_stream) { + forEachMediaStreamAsync([] (const std::shared_ptr &media_stream) { media_stream->sendPLIToFeedback(); }); } else { @@ -698,7 +626,7 @@ void WebRtcConnection::updateState(TransportState state, Transport * transport) // WebRTCConnection will be ready only when all channels are ready. temp = CONN_READY; trackTransportInfo(); - forEachMediaStreamAsyncNoPromise([] (const std::shared_ptr &media_stream) { + forEachMediaStreamAsync([] (const std::shared_ptr &media_stream) { media_stream->sendPLIToFeedback(); }); } @@ -753,7 +681,7 @@ void WebRtcConnection::trackTransportInfo() { } asyncTask([audio_info, video_info] (std::shared_ptr connection) { - connection->forEachMediaStreamAsyncNoPromise( + connection->forEachMediaStreamAsync( [audio_info, video_info] (const std::shared_ptr &media_stream) { media_stream->setTransportInfo(audio_info, video_info); }); diff --git a/erizo/src/erizo/WebRtcConnection.h b/erizo/src/erizo/WebRtcConnection.h index 5e7dd5c3c7..a202aae7a2 100644 --- a/erizo/src/erizo/WebRtcConnection.h +++ b/erizo/src/erizo/WebRtcConnection.h @@ -1,8 +1,6 @@ #ifndef ERIZO_SRC_ERIZO_WEBRTCCONNECTION_H_ #define ERIZO_SRC_ERIZO_WEBRTCCONNECTION_H_ -#include -#include #include #include @@ -50,7 +48,7 @@ class WebRtcConnectionEventListener { public: virtual ~WebRtcConnectionEventListener() { } - virtual void notifyEvent(WebRTCEvent newEvent, const std::string& message) = 0; + virtual void notifyEvent(WebRTCEvent newEvent, const std::string& message, const std::string &stream_id = "") = 0; }; /** @@ -84,34 +82,26 @@ class WebRtcConnection: public TransportListener, public LogContext, void close(); void syncClose(); - boost::future setRemoteSdpInfo(std::shared_ptr sdp); + bool setRemoteSdpInfo(std::shared_ptr sdp, std::string stream_id); /** * Sets the SDP of the remote peer. * @param sdp The SDP. * @return true if the SDP was received correctly. */ - boost::future setRemoteSdp(const std::string &sdp); - - boost::future createOffer(bool video_enabled, bool audio_enabled, bool bundle); - - boost::future addRemoteCandidate(std::string mid, int mLineIndex, std::string sdp); + bool setRemoteSdp(const std::string &sdp, std::string stream_id); + bool createOffer(bool video_enabled, bool audio_enabled, bool bundle); /** * Add new remote candidate (from remote peer). * @param sdp The candidate in SDP format. * @return true if the SDP was received correctly. */ - bool addRemoteCandidateSync(std::string mid, int mLineIndex, std::string sdp); - + bool addRemoteCandidate(const std::string &mid, int mLineIndex, const std::string &sdp); /** * Obtains the local SDP. * @return The SDP as a SdpInfo. */ std::shared_ptr getLocalSdpInfo(); - /** - * Copy some SdpInfo data to local SdpInfo - */ - void copyDataToLocalSdpIndo(std::shared_ptr sdp_info); /** * Obtains the local SDP. * @return The SDP as a string. @@ -140,16 +130,15 @@ class WebRtcConnection: public TransportListener, public LogContext, void write(std::shared_ptr packet); void syncWrite(std::shared_ptr packet); - boost::future asyncTask(std::function)> f); + void asyncTask(std::function)> f); bool isAudioMuted() { return audio_muted_; } bool isVideoMuted() { return video_muted_; } - boost::future addMediaStream(std::shared_ptr media_stream); - boost::future removeMediaStream(const std::string& stream_id); + void addMediaStream(std::shared_ptr media_stream); + void removeMediaStream(const std::string& stream_id); void forEachMediaStream(std::function&)> func); - boost::future forEachMediaStreamAsync(std::function&)> func); - void forEachMediaStreamAsyncNoPromise(std::function&)> func); + void forEachMediaStreamAsync(std::function&)> func); void setTransport(std::shared_ptr transport); // Only for Testing purposes @@ -164,14 +153,15 @@ class WebRtcConnection: public TransportListener, public LogContext, } private: - bool createOfferSync(bool video_enabled, bool audio_enabled, bool bundle); - boost::future processRemoteSdp(); - boost::future setRemoteSdpsToMediaStreams(); + bool processRemoteSdp(std::string stream_id); + void setRemoteSdpsToMediaStreams(std::string stream_id); + void onRemoteSdpsSetToMediaStreams(std::string stream_id); std::string getJSONCandidate(const std::string& mid, const std::string& sdp); void trackTransportInfo(); void onRtcpFromTransport(std::shared_ptr packet, Transport *transport); void onREMBFromTransport(RtcpHeader *chead, Transport *transport); - void maybeNotifyWebRtcConnectionEvent(const WebRTCEvent& event, const std::string& message); + void maybeNotifyWebRtcConnectionEvent(const WebRTCEvent& event, const std::string& message, + const std::string& stream_id = ""); protected: std::atomic global_state_; diff --git a/erizo/src/erizo/rtp/FecReceiverHandler.cpp b/erizo/src/erizo/rtp/FecReceiverHandler.cpp index 1f1ece54fa..e951161fc9 100644 --- a/erizo/src/erizo/rtp/FecReceiverHandler.cpp +++ b/erizo/src/erizo/rtp/FecReceiverHandler.cpp @@ -33,8 +33,7 @@ void FecReceiverHandler::notifyUpdate() { return; } bool is_slide_show_mode_active = stream->isSlideShowModeEnabled(); - if ((stream->getRemoteSdpInfo() && !stream->getRemoteSdpInfo()->supportPayloadType(RED_90000_PT)) || - is_slide_show_mode_active) { + if (!stream->getRemoteSdpInfo()->supportPayloadType(RED_90000_PT) || is_slide_show_mode_active) { enable(); } else { disable(); diff --git a/erizo/src/erizo/thread/Worker.cpp b/erizo/src/erizo/thread/Worker.cpp index b74c56db46..a03184275d 100644 --- a/erizo/src/erizo/thread/Worker.cpp +++ b/erizo/src/erizo/thread/Worker.cpp @@ -34,7 +34,7 @@ Worker::~Worker() { } void Worker::task(Task f) { - service_.dispatch(f); + service_.post(f); } void Worker::start() { @@ -51,9 +51,7 @@ void Worker::start(std::shared_ptr> start_promise) { } return size_t(0); }; - auto thread = new boost::thread(worker); - thread_id_ = thread->get_id(); - group_.add_thread(thread); + group_.add_thread(new boost::thread(worker)); } void Worker::close() { diff --git a/erizo/src/erizo/thread/Worker.h b/erizo/src/erizo/thread/Worker.h index 3793688f32..34abb77717 100644 --- a/erizo/src/erizo/thread/Worker.h +++ b/erizo/src/erizo/thread/Worker.h @@ -41,7 +41,6 @@ class Worker : public std::enable_shared_from_this { virtual void start(); virtual void start(std::shared_ptr> start_promise); virtual void close(); - virtual boost::thread::id getId() { return thread_id_; } virtual std::shared_ptr scheduleFromNow(Task f, duration delta); virtual void unschedule(std::shared_ptr id); @@ -62,7 +61,6 @@ class Worker : public std::enable_shared_from_this { asio_worker service_worker_; boost::thread_group group_; std::atomic closed_; - boost::thread::id thread_id_; }; class SimulatedWorker : public Worker { diff --git a/erizoAPI/AsyncPromiseWorker.cc b/erizoAPI/AsyncPromiseWorker.cc deleted file mode 100644 index f05a4b3a7d..0000000000 --- a/erizoAPI/AsyncPromiseWorker.cc +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include "AsyncPromiseWorker.h" - -AsyncPromiseWorker::AsyncPromiseWorker(Nan::Persistent *persistent) - : AsyncWorker(nullptr) { - _persistent = persistent; -} - -AsyncPromiseWorker::~AsyncPromiseWorker() {} - -void AsyncPromiseWorker::Execute() { -} - -void AsyncPromiseWorker::HandleOKCallback() { - Nan::HandleScope scope; - auto resolver = Nan::New(*_persistent); - resolver->Resolve(Nan::GetCurrentContext(), Nan::New("").ToLocalChecked()); -} - -void AsyncPromiseWorker::HandleErrorCallback() { - Nan::HandleScope scope; - auto resolver = Nan::New(*_persistent); - resolver->Reject(Nan::GetCurrentContext(), Nan::New("").ToLocalChecked()); -} - diff --git a/erizoAPI/AsyncPromiseWorker.h b/erizoAPI/AsyncPromiseWorker.h deleted file mode 100644 index 66fdb84541..0000000000 --- a/erizoAPI/AsyncPromiseWorker.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ERIZOAPI_ASYNCPROMISEWORKER_H_ -#define ERIZOAPI_ASYNCPROMISEWORKER_H_ - -#include - -class AsyncPromiseWorker : public Nan::AsyncWorker { - public: - explicit AsyncPromiseWorker(Nan::Persistent *persistent); - ~AsyncPromiseWorker(); - void Execute() override; - void HandleOKCallback() override; - void HandleErrorCallback() override; - - private: - Nan::Persistent *_persistent; -}; - -#endif // ERIZOAPI_ASYNCPROMISEWORKER_H_ - diff --git a/erizoAPI/ConnectionDescription.cc b/erizoAPI/ConnectionDescription.cc index a43724978c..f6b914d480 100644 --- a/erizoAPI/ConnectionDescription.cc +++ b/erizoAPI/ConnectionDescription.cc @@ -104,7 +104,6 @@ NAN_MODULE_INIT(ConnectionDescription::Init) { Nan::SetPrototypeMethod(tpl, "getRids", getRids); Nan::SetPrototypeMethod(tpl, "postProcessInfo", postProcessInfo); - Nan::SetPrototypeMethod(tpl, "copyInfoFromSdp", copyInfoFromSdp); constructor.Reset(tpl->GetFunction()); Nan::Set(target, Nan::New("ConnectionDescription").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); @@ -747,15 +746,6 @@ NAN_METHOD(ConnectionDescription::postProcessInfo) { info.GetReturnValue().Set(Nan::New(success)); } -NAN_METHOD(ConnectionDescription::copyInfoFromSdp) { - GET_SDP(); - ConnectionDescription* source = - Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); - - std::shared_ptr source_sdp = source->me; - sdp->copyInfoFromSdp(source_sdp); -} - NAN_METHOD(ConnectionDescription::close) { GET_SDP(); obj->me.reset(); diff --git a/erizoAPI/ConnectionDescription.h b/erizoAPI/ConnectionDescription.h index 964e0c4027..2730f9db29 100644 --- a/erizoAPI/ConnectionDescription.h +++ b/erizoAPI/ConnectionDescription.h @@ -83,8 +83,6 @@ class ConnectionDescription : public Nan::ObjectWrap { static NAN_METHOD(postProcessInfo); - static NAN_METHOD(copyInfoFromSdp); - static Nan::Persistent constructor; }; diff --git a/erizoAPI/ExternalInput.cc b/erizoAPI/ExternalInput.cc index 4bb8c22800..0ff0bc6b3b 100644 --- a/erizoAPI/ExternalInput.cc +++ b/erizoAPI/ExternalInput.cc @@ -30,8 +30,7 @@ class AsyncDeleter : public Nan::AsyncWorker { Local argv[] = { Nan::New(msg.c_str()).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.externalInput.deleter"); - callback->Call(1, argv, &resource); + callback->Call(1, argv); } } private: diff --git a/erizoAPI/ExternalOutput.cc b/erizoAPI/ExternalOutput.cc index 0c3625f837..be65c25385 100644 --- a/erizoAPI/ExternalOutput.cc +++ b/erizoAPI/ExternalOutput.cc @@ -34,11 +34,9 @@ class AsyncCloser : public Nan::AsyncWorker { Local argv[] = { Nan::New(msg.c_str()).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.externalOutput.closer"); - callback->Call(1, argv, &resource); + callback->Call(1, argv); } } - private: std::shared_ptr external_output_; }; diff --git a/erizoAPI/FuturesManager.cc b/erizoAPI/FuturesManager.cc deleted file mode 100644 index 632c57efbd..0000000000 --- a/erizoAPI/FuturesManager.cc +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef BUILDING_NODE_EXTENSION -#define BUILDING_NODE_EXTENSION -#endif -#include "FuturesManager.h" - -DEFINE_LOGGER(FuturesManager, "ErizoAPI.FuturesManager"); - -FuturesManager::FuturesManager() { -} - -FuturesManager::~FuturesManager() { -} - -void FuturesManager::add(boost::future future) { - futures.push_back(std::move(future)); -} - -void FuturesManager::cleanResolvedFutures() { - futures.erase(std::remove_if(futures.begin(), futures.end(), - [](const boost::future& future) { - return future.is_ready(); - }), futures.end()); - ELOG_DEBUG("message: futures after removing resolved, size: %d", numberOfUnresolvedFutures()); -} - -int FuturesManager::numberOfUnresolvedFutures() { - return futures.size(); -} diff --git a/erizoAPI/FuturesManager.h b/erizoAPI/FuturesManager.h deleted file mode 100644 index 8c8688444d..0000000000 --- a/erizoAPI/FuturesManager.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef ERIZOAPI_FUTURESMANAGER_H_ -#define ERIZOAPI_FUTURESMANAGER_H_ - -#include -#include -#include // NOLINT - -/* - * Wrapper class of erizo::ExternalInput - * - * Represents a OneToMany connection. - * Receives media from one publisher and retransmits it to every subscriber. - */ -class FuturesManager { - public: - DECLARE_LOGGER(); - FuturesManager(); - ~FuturesManager(); - void add(boost::future future); - void cleanResolvedFutures(); - int numberOfUnresolvedFutures(); - - private: - std::vector> futures; -}; - -#endif // ERIZOAPI_FUTURESMANAGER_H_ diff --git a/erizoAPI/MediaStream.cc b/erizoAPI/MediaStream.cc index e83d835305..5a993e18c9 100644 --- a/erizoAPI/MediaStream.cc +++ b/erizoAPI/MediaStream.cc @@ -40,8 +40,7 @@ void StatCallWorker::HandleOKCallback() { Local argv[] = { Nan::New(stat_).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.statCall"); - callback->Call(1, argv, &resource); + callback->Call(1, argv); } void destroyAsyncHandle(uv_handle_t *handle) { @@ -177,8 +176,7 @@ NAN_METHOD(MediaStream::init) { if (!me) { return; } - bool force = info.Length() > 0 ? info[0]->BooleanValue() : false; - bool r = me->init(force); + bool r = me->init(); info.GetReturnValue().Set(Nan::New(r)); } @@ -440,8 +438,7 @@ NAUV_WORK_CB(MediaStream::statsCallback) { if (obj->has_stats_callback_) { while (!obj->stats_messages.empty()) { Local args[] = {Nan::New(obj->stats_messages.front().c_str()).ToLocalChecked()}; - Nan::AsyncResource resource("erizo::addon.stream.statsCallback"); - resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), obj->stats_callback_->GetFunction(), 1, args); + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), obj->stats_callback_->GetFunction(), 1, args); obj->stats_messages.pop(); } } @@ -459,8 +456,7 @@ NAUV_WORK_CB(MediaStream::eventCallback) { while (!obj->event_messages.empty()) { Local args[] = {Nan::New(obj->event_messages.front().first.c_str()).ToLocalChecked(), Nan::New(obj->event_messages.front().second.c_str()).ToLocalChecked()}; - Nan::AsyncResource resource("erizo::addon.stream.eventCallback"); - resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), obj->event_callback_->GetFunction(), 2, args); + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), obj->event_callback_->GetFunction(), 2, args); obj->event_messages.pop(); } } diff --git a/erizoAPI/OneToManyProcessor.cc b/erizoAPI/OneToManyProcessor.cc index f6bce9f2dc..52b8cf64f8 100644 --- a/erizoAPI/OneToManyProcessor.cc +++ b/erizoAPI/OneToManyProcessor.cc @@ -31,8 +31,8 @@ class AsyncDeleter : public Nan::AsyncWorker { Local argv[] = { Nan::New(msg.c_str()).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.oneToManyProcessor.deleter"); - callback->Call(1, argv, &resource); + + callback->Call(1, argv); } } private: diff --git a/erizoAPI/OneToManyTranscoder.cc b/erizoAPI/OneToManyTranscoder.cc index 950bd9f39c..8594a7e259 100644 --- a/erizoAPI/OneToManyTranscoder.cc +++ b/erizoAPI/OneToManyTranscoder.cc @@ -30,8 +30,8 @@ class AsyncDeleter : public Nan::AsyncWorker { Local argv[] = { Nan::New(msg.c_str()).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.oneToManyTranscoder.deleter"); - callback->Call(1, argv), &resource; + + callback->Call(1, argv); } } private: diff --git a/erizoAPI/SyntheticInput.cc b/erizoAPI/SyntheticInput.cc index 4aecbe04c0..c18d9b3b5f 100644 --- a/erizoAPI/SyntheticInput.cc +++ b/erizoAPI/SyntheticInput.cc @@ -31,8 +31,7 @@ class AsyncDeleter : public Nan::AsyncWorker { Local argv[] = { Nan::New(msg.c_str()).ToLocalChecked() }; - Nan::AsyncResource resource("erizo::addon.SyntheticInput.deleter"); - callback->Call(1, argv, &resource); + callback->Call(1, argv); } } private: diff --git a/erizoAPI/WebRtcConnection.cc b/erizoAPI/WebRtcConnection.cc index d6281a9bda..319acd5a99 100644 --- a/erizoAPI/WebRtcConnection.cc +++ b/erizoAPI/WebRtcConnection.cc @@ -7,8 +7,6 @@ #include "MediaStream.h" #include // NOLINT -#include // NOLINT -#include // NOLINT #include "lib/json.hpp" #include "IOThreadPool.h" @@ -33,9 +31,7 @@ void destroyWebRtcConnectionAsyncHandle(uv_handle_t *handle) { WebRtcConnection::WebRtcConnection() : closed_{false}, id_{"undefined"} { async_ = new uv_async_t; - future_async_ = new uv_async_t; uv_async_init(uv_default_loop(), async_, &WebRtcConnection::eventsCallback); - uv_async_init(uv_default_loop(), future_async_, &WebRtcConnection::promiseResolver); } WebRtcConnection::~WebRtcConnection() { @@ -60,13 +56,7 @@ void WebRtcConnection::close() { ELOG_DEBUG("%s, message: Closing handle", toLog()); uv_close(reinterpret_cast(async_), destroyWebRtcConnectionAsyncHandle); } - - if (!uv_is_closing(reinterpret_cast(future_async_))) { - ELOG_DEBUG("%s, message: Closing future handle", toLog()); - uv_close(reinterpret_cast(future_async_), destroyWebRtcConnectionAsyncHandle); - } async_ = nullptr; - future_async_ = nullptr; closed_ = true; ELOG_DEBUG("%s, message: Closed", toLog()); } @@ -94,7 +84,6 @@ NAN_MODULE_INIT(WebRtcConnection::Init) { Nan::SetPrototypeMethod(tpl, "setMetadata", setMetadata); Nan::SetPrototypeMethod(tpl, "addMediaStream", addMediaStream); Nan::SetPrototypeMethod(tpl, "removeMediaStream", removeMediaStream); - Nan::SetPrototypeMethod(tpl, "copySdpToLocalDescription", copySdpToLocalDescription); constructor.Reset(tpl->GetFunction()); Nan::Set(target, Nan::New("WebRtcConnection").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); @@ -221,6 +210,7 @@ NAN_METHOD(WebRtcConnection::New) { obj->me = std::make_shared(worker, io_worker, wrtcId, iceConfig, rtp_mappings, ext_mappings, obj); obj->Wrap(info.This()); + ELOG_DEBUG("%s, message: Created", obj->toLog()); info.GetReturnValue().Set(info.This()); } else { // TODO(pedro) Check what happens here @@ -259,15 +249,8 @@ NAN_METHOD(WebRtcConnection::createOffer) { bool audio_enabled = info[1]->BooleanValue(); bool bundle = info[2]->BooleanValue(); - v8::Local resolver = v8::Promise::Resolver::New(info.GetIsolate()); - Nan::Persistent *persistent = new Nan::Persistent(resolver); - - obj->futures_manager_.add(me->createOffer(video_enabled, audio_enabled, bundle).then( - [persistent, obj] (boost::future) { - obj->notifyFuture(persistent); - })); - - info.GetReturnValue().Set(resolver->GetPromise()); + bool r = me->createOffer(video_enabled, audio_enabled, bundle); + info.GetReturnValue().Set(Nan::New(r)); } NAN_METHOD(WebRtcConnection::setMetadata) { @@ -307,21 +290,18 @@ NAN_METHOD(WebRtcConnection::setRemoteSdp) { v8::String::Utf8Value param(Nan::To(info[0]).ToLocalChecked()); std::string sdp = std::string(*param); - v8::Local resolver = v8::Promise::Resolver::New(info.GetIsolate()); - Nan::Persistent *persistent = new Nan::Persistent(resolver); + v8::String::Utf8Value stream_id_param(Nan::To(info[1]).ToLocalChecked()); + std::string stream_id = std::string(*stream_id_param); - obj->futures_manager_.add(me->setRemoteSdp(sdp).then([persistent, obj] (boost::future) { - obj->notifyFuture(persistent); - })); + bool r = me->setRemoteSdp(sdp, stream_id); - info.GetReturnValue().Set(resolver->GetPromise()); + info.GetReturnValue().Set(Nan::New(r)); } NAN_METHOD(WebRtcConnection::setRemoteDescription) { WebRtcConnection* obj = Nan::ObjectWrap::Unwrap(info.Holder()); std::shared_ptr me = obj->me; if (!me) { - info.GetReturnValue().Set(Nan::New(false)); return; } @@ -329,14 +309,11 @@ NAN_METHOD(WebRtcConnection::setRemoteDescription) { Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); auto sdp = std::make_shared(*param->me.get()); - v8::Local resolver = v8::Promise::Resolver::New(info.GetIsolate()); - Nan::Persistent *persistent = new Nan::Persistent(resolver); - - obj->futures_manager_.add(me->setRemoteSdpInfo(sdp).then([persistent, obj] (boost::future) { - obj->notifyFuture(persistent); - })); + v8::String::Utf8Value stream_id_param(Nan::To(info[1]).ToLocalChecked()); + std::string stream_id = std::string(*stream_id_param); - info.GetReturnValue().Set(resolver->GetPromise()); + bool r = me->setRemoteSdpInfo(sdp, stream_id); + info.GetReturnValue().Set(Nan::New(r)); } NAN_METHOD(WebRtcConnection::getLocalDescription) { @@ -354,21 +331,6 @@ NAN_METHOD(WebRtcConnection::getLocalDescription) { info.GetReturnValue().Set(instance); } -NAN_METHOD(WebRtcConnection::copySdpToLocalDescription) { - WebRtcConnection* obj = Nan::ObjectWrap::Unwrap(info.Holder()); - std::shared_ptr me = obj->me; - if (!me) { - return; - } - - ConnectionDescription* source = - Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); - - std::shared_ptr source_sdp = source->me; - - me->copyDataToLocalSdpIndo(source_sdp); -} - NAN_METHOD(WebRtcConnection::addRemoteCandidate) { WebRtcConnection* obj = Nan::ObjectWrap::Unwrap(info.Holder()); std::shared_ptr me = obj->me; @@ -384,9 +346,9 @@ NAN_METHOD(WebRtcConnection::addRemoteCandidate) { v8::String::Utf8Value param2(Nan::To(info[2]).ToLocalChecked()); std::string sdp = std::string(*param2); - me->addRemoteCandidate(mid, sdpMLine, sdp); + bool r = me->addRemoteCandidate(mid, sdpMLine, sdp); - info.GetReturnValue().Set(Nan::New(true)); + info.GetReturnValue().Set(Nan::New(r)); } NAN_METHOD(WebRtcConnection::getLocalSdp) { @@ -421,15 +383,9 @@ NAN_METHOD(WebRtcConnection::addMediaStream) { } MediaStream* param = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); - auto ms = std::shared_ptr(param->me); - - v8::Local resolver = v8::Promise::Resolver::New(info.GetIsolate()); - Nan::Persistent *persistent = new Nan::Persistent(resolver); - obj->futures_manager_.add(me->addMediaStream(ms).then([persistent, obj] (boost::future) { - obj->notifyFuture(persistent); - })); + auto wr = std::shared_ptr(param->me); - info.GetReturnValue().Set(resolver->GetPromise()); + me->addMediaStream(wr); } NAN_METHOD(WebRtcConnection::removeMediaStream) { @@ -440,31 +396,24 @@ NAN_METHOD(WebRtcConnection::removeMediaStream) { } v8::String::Utf8Value param(Nan::To(info[0]).ToLocalChecked()); - std::string stream_id = std::string(*param); - - v8::Local resolver = v8::Promise::Resolver::New(info.GetIsolate()); - Nan::Persistent *persistent = new Nan::Persistent(resolver); - - obj->futures_manager_.add(me->removeMediaStream(stream_id).then([persistent, obj] (boost::future) { - obj->notifyFuture(persistent); - })); - - info.GetReturnValue().Set(resolver->GetPromise()); + std::string streamId = std::string(*param); + me->removeMediaStream(streamId); } // Async methods -void WebRtcConnection::notifyEvent(erizo::WebRTCEvent event, const std::string& message) { +void WebRtcConnection::notifyEvent(erizo::WebRTCEvent event, const std::string& message, const std::string& stream_id) { boost::mutex::scoped_lock lock(mutex); if (!async_) { return; } this->event_status.push(event); - this->event_messages.push(message); + this->event_messages.push(std::make_pair(message, stream_id)); async_->data = this; uv_async_send(async_); } + NAUV_WORK_CB(WebRtcConnection::eventsCallback) { Nan::HandleScope scope; WebRtcConnection* obj = reinterpret_cast(async->data); @@ -475,39 +424,11 @@ NAUV_WORK_CB(WebRtcConnection::eventsCallback) { ELOG_DEBUG("%s, message: eventsCallback", obj->toLog()); while (!obj->event_status.empty()) { Local args[] = {Nan::New(obj->event_status.front()), - Nan::New(obj->event_messages.front().c_str()).ToLocalChecked()}; - Nan::AsyncResource resource("erizo::addon.connection.eventsCallback"); - resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), obj->event_callback_->GetFunction(), 2, args); + Nan::New(obj->event_messages.front().first.c_str()).ToLocalChecked(), + Nan::New(obj->event_messages.front().second.c_str()).ToLocalChecked()}; + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), obj->event_callback_->GetFunction(), 3, args); obj->event_messages.pop(); obj->event_status.pop(); } ELOG_DEBUG("%s, message: eventsCallback finished", obj->toLog()); } - -void WebRtcConnection::notifyFuture(Nan::Persistent *persistent) { - boost::mutex::scoped_lock lock(mutex); - if (!future_async_) { - return; - } - this->futures.push(persistent); - future_async_->data = this; - uv_async_send(future_async_); -} - -NAUV_WORK_CB(WebRtcConnection::promiseResolver) { - Nan::HandleScope scope; - WebRtcConnection* obj = reinterpret_cast(async->data); - if (!obj || !obj->me) { - return; - } - boost::mutex::scoped_lock lock(obj->mutex); - ELOG_DEBUG("%s, message: promiseResolver", obj->toLog()); - obj->futures_manager_.cleanResolvedFutures(); - while (!obj->futures.empty()) { - auto persistent = obj->futures.front(); - v8::Local resolver = Nan::New(*persistent); - resolver->Resolve(Nan::GetCurrentContext(), Nan::New("").ToLocalChecked()); - obj->futures.pop(); - } - ELOG_DEBUG("%s, message: promiseResolver finished", obj->toLog()); -} diff --git a/erizoAPI/WebRtcConnection.h b/erizoAPI/WebRtcConnection.h index 18cbd4c612..e9fea54826 100644 --- a/erizoAPI/WebRtcConnection.h +++ b/erizoAPI/WebRtcConnection.h @@ -4,7 +4,6 @@ #include #include #include -#include "FuturesManager.h" #include "MediaDefinitions.h" #include "OneToManyProcessor.h" #include "ConnectionDescription.h" @@ -27,9 +26,7 @@ class WebRtcConnection : public erizo::WebRtcConnectionEventListener, std::shared_ptr me; std::queue event_status; - std::queue event_messages; - std::queue *> futures; - FuturesManager futures_manager_; + std::queue> event_messages; boost::mutex mutex; @@ -42,7 +39,6 @@ class WebRtcConnection : public erizo::WebRtcConnectionEventListener, Nan::Callback *event_callback_; uv_async_t *async_; - uv_async_t *future_async_; bool closed_; std::string id_; /* @@ -109,16 +105,13 @@ class WebRtcConnection : public erizo::WebRtcConnectionEventListener, static NAN_METHOD(addMediaStream); static NAN_METHOD(removeMediaStream); - static NAN_METHOD(copySdpToLocalDescription); - static Nan::Persistent constructor; static NAUV_WORK_CB(eventsCallback); - static NAUV_WORK_CB(promiseResolver); virtual void notifyEvent(erizo::WebRTCEvent event, - const std::string& message = ""); - virtual void notifyFuture(Nan::Persistent *persistent); + const std::string& message = "", + const std::string& stream_id = ""); }; #endif // ERIZOAPI_WEBRTCCONNECTION_H_ diff --git a/erizoAPI/binding.gyp b/erizoAPI/binding.gyp index 366d0ccde1..ec3c91d9ca 100644 --- a/erizoAPI/binding.gyp +++ b/erizoAPI/binding.gyp @@ -1,6 +1,6 @@ { 'variables' : { - 'common_sources': [ 'addon.cc', 'IOThreadPool.cc', 'AsyncPromiseWorker.cc', 'FuturesManager.cc', 'ThreadPool.cc', 'MediaStream.cc', 'WebRtcConnection.cc', 'OneToManyProcessor.cc', 'ExternalInput.cc', 'ExternalOutput.cc', 'SyntheticInput.cc', 'ConnectionDescription.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' : [" { this.nuveProxy.getErizoControllers().then((controllers) => { diff --git a/erizo_controller/erizoClient/lib/state-machine.js b/erizo_controller/erizoClient/lib/state-machine.js deleted file mode 100644 index fcf4316274..0000000000 --- a/erizo_controller/erizoClient/lib/state-machine.js +++ /dev/null @@ -1,691 +0,0 @@ -/* -Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2018, Jake Gordon and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define("StateMachine", [], factory); - else if(typeof exports === 'object') - exports["StateMachine"] = factory(); - else - root["StateMachine"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 5); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = function(target, sources) { - var n, source, key; - for(n = 1 ; n < arguments.length ; n++) { - source = arguments[n]; - for(key in source) { - if (source.hasOwnProperty(key)) - target[key] = source[key]; - } - } - return target; -} - - -/***/ }), -/* 1 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -//------------------------------------------------------------------------------------------------- - -var mixin = __webpack_require__(0); - -//------------------------------------------------------------------------------------------------- - -module.exports = { - - build: function(target, config) { - var n, max, plugin, plugins = config.plugins; - for(n = 0, max = plugins.length ; n < max ; n++) { - plugin = plugins[n]; - if (plugin.methods) - mixin(target, plugin.methods); - if (plugin.properties) - Object.defineProperties(target, plugin.properties); - } - }, - - hook: function(fsm, name, additional) { - var n, max, method, plugin, - plugins = fsm.config.plugins, - args = [fsm.context]; - - if (additional) - args = args.concat(additional) - - for(n = 0, max = plugins.length ; n < max ; n++) { - plugin = plugins[n] - method = plugins[n][name] - if (method) - method.apply(plugin, args); - } - } - -} - -//------------------------------------------------------------------------------------------------- - - -/***/ }), -/* 2 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -//------------------------------------------------------------------------------------------------- - -function camelize(label) { - - if (label.length === 0) - return label; - - var n, result, word, words = label.split(/[_-]/); - - // single word with first character already lowercase, return untouched - if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0])) - return label; - - result = words[0].toLowerCase(); - for(n = 1 ; n < words.length ; n++) { - result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase(); - } - - return result; -} - -//------------------------------------------------------------------------------------------------- - -camelize.prepended = function(prepend, label) { - label = camelize(label); - return prepend + label[0].toUpperCase() + label.substring(1); -} - -//------------------------------------------------------------------------------------------------- - -module.exports = camelize; - - -/***/ }), -/* 3 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -//------------------------------------------------------------------------------------------------- - -var mixin = __webpack_require__(0), - camelize = __webpack_require__(2); - -//------------------------------------------------------------------------------------------------- - -function Config(options, StateMachine) { - - options = options || {}; - - this.options = options; // preserving original options can be useful (e.g visualize plugin) - this.defaults = StateMachine.defaults; - this.states = []; - this.transitions = []; - this.map = {}; - this.lifecycle = this.configureLifecycle(); - this.init = this.configureInitTransition(options.init); - this.data = this.configureData(options.data); - this.methods = this.configureMethods(options.methods); - - this.map[this.defaults.wildcard] = {}; - - this.configureTransitions(options.transitions || []); - - this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin); - -} - -//------------------------------------------------------------------------------------------------- - -mixin(Config.prototype, { - - addState: function(name) { - if (!this.map[name]) { - this.states.push(name); - this.addStateLifecycleNames(name); - this.map[name] = {}; - } - }, - - addStateLifecycleNames: function(name) { - this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name); - this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name); - this.lifecycle.on[name] = camelize.prepended('on', name); - }, - - addTransition: function(name) { - if (this.transitions.indexOf(name) < 0) { - this.transitions.push(name); - this.addTransitionLifecycleNames(name); - } - }, - - addTransitionLifecycleNames: function(name) { - this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name); - this.lifecycle.onAfter[name] = camelize.prepended('onAfter', name); - this.lifecycle.on[name] = camelize.prepended('on', name); - }, - - mapTransition: function(transition) { - var name = transition.name, - from = transition.from, - to = transition.to; - this.addState(from); - if (typeof to !== 'function') - this.addState(to); - this.addTransition(name); - this.map[from][name] = transition; - return transition; - }, - - configureLifecycle: function() { - return { - onBefore: { transition: 'onBeforeTransition' }, - onAfter: { transition: 'onAfterTransition' }, - onEnter: { state: 'onEnterState' }, - onLeave: { state: 'onLeaveState' }, - on: { transition: 'onTransition' } - }; - }, - - configureInitTransition: function(init) { - if (typeof init === 'string') { - return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true })); - } - else if (typeof init === 'object') { - return this.mapTransition(mixin({}, this.defaults.init, init, { active: true })); - } - else { - this.addState(this.defaults.init.from); - return this.defaults.init; - } - }, - - configureData: function(data) { - if (typeof data === 'function') - return data; - else if (typeof data === 'object') - return function() { return data; } - else - return function() { return {}; } - }, - - configureMethods: function(methods) { - return methods || {}; - }, - - configurePlugins: function(plugins, builtin) { - plugins = plugins || []; - var n, max, plugin; - for(n = 0, max = plugins.length ; n < max ; n++) { - plugin = plugins[n]; - if (typeof plugin === 'function') - plugins[n] = plugin = plugin() - if (plugin.configure) - plugin.configure(this); - } - return plugins - }, - - configureTransitions: function(transitions) { - var i, n, transition, from, to, wildcard = this.defaults.wildcard; - for(n = 0 ; n < transitions.length ; n++) { - transition = transitions[n]; - from = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard] - to = transition.to || wildcard; - for(i = 0 ; i < from.length ; i++) { - this.mapTransition({ name: transition.name, from: from[i], to: to }); - } - } - }, - - transitionFor: function(state, transition) { - var wildcard = this.defaults.wildcard; - return this.map[state][transition] || - this.map[wildcard][transition]; - }, - - transitionsFor: function(state) { - var wildcard = this.defaults.wildcard; - return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard])); - }, - - allStates: function() { - return this.states; - }, - - allTransitions: function() { - return this.transitions; - } - -}); - -//------------------------------------------------------------------------------------------------- - -module.exports = Config; - -//------------------------------------------------------------------------------------------------- - - -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { - - -var mixin = __webpack_require__(0), - Exception = __webpack_require__(6), - plugin = __webpack_require__(1), - UNOBSERVED = [ null, [] ]; - -//------------------------------------------------------------------------------------------------- - -function JSM(context, config) { - this.context = context; - this.config = config; - this.state = config.init.from; - this.observers = [context]; -} - -//------------------------------------------------------------------------------------------------- - -mixin(JSM.prototype, { - - init: function(args) { - mixin(this.context, this.config.data.apply(this.context, args)); - plugin.hook(this, 'init'); - if (this.config.init.active) - return this.fire(this.config.init.name, []); - }, - - is: function(state) { - return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state); - }, - - isPending: function() { - return this.pending; - }, - - can: function(transition) { - return !this.isPending() && !!this.seek(transition); - }, - - cannot: function(transition) { - return !this.can(transition); - }, - - allStates: function() { - return this.config.allStates(); - }, - - allTransitions: function() { - return this.config.allTransitions(); - }, - - transitions: function() { - return this.config.transitionsFor(this.state); - }, - - seek: function(transition, args) { - var wildcard = this.config.defaults.wildcard, - entry = this.config.transitionFor(this.state, transition), - to = entry && entry.to; - if (typeof to === 'function') - return to.apply(this.context, args); - else if (to === wildcard) - return this.state - else - return to - }, - - fire: function(transition, args) { - return this.transit(transition, this.state, this.seek(transition, args), args); - }, - - transit: function(transition, from, to, args) { - - var lifecycle = this.config.lifecycle, - changed = this.config.options.observeUnchangedState || (from !== to); - - if (!to) - return this.context.onInvalidTransition(transition, from, to); - - if (this.isPending()) - return this.context.onPendingTransition(transition, from, to); - - this.config.addState(to); // might need to add this state if it's unknown (e.g. conditional transition or goto) - - this.beginTransit(); - - args.unshift({ // this context will be passed to each lifecycle event observer - transition: transition, - from: from, - to: to, - fsm: this.context - }); - - return this.observeEvents([ - this.observersForEvent(lifecycle.onBefore.transition), - this.observersForEvent(lifecycle.onBefore[transition]), - changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED, - changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED, - this.observersForEvent(lifecycle.on.transition), - changed ? [ 'doTransit', [ this ] ] : UNOBSERVED, - changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED, - changed ? this.observersForEvent(lifecycle.onEnter[to]) : UNOBSERVED, - changed ? this.observersForEvent(lifecycle.on[to]) : UNOBSERVED, - this.observersForEvent(lifecycle.onAfter.transition), - this.observersForEvent(lifecycle.onAfter[transition]), - this.observersForEvent(lifecycle.on[transition]) - ], args); - }, - - beginTransit: function() { this.pending = true; }, - endTransit: function(result) { this.pending = false; return result; }, - failTransit: function(result) { this.pending = false; throw result; }, - doTransit: function(lifecycle) { this.state = lifecycle.to; }, - - observe: function(args) { - if (args.length === 2) { - var observer = {}; - observer[args[0]] = args[1]; - this.observers.push(observer); - } - else { - this.observers.push(args[0]); - } - }, - - observersForEvent: function(event) { // TODO: this could be cached - var n = 0, max = this.observers.length, observer, result = []; - for( ; n < max ; n++) { - observer = this.observers[n]; - if (observer[event]) - result.push(observer); - } - return [ event, result, true ] - }, - - observeEvents: function(events, args, previousEvent, previousResult) { - if (events.length === 0) { - return this.endTransit(previousResult === undefined ? true : previousResult); - } - - var event = events[0][0], - observers = events[0][1], - pluggable = events[0][2]; - - args[0].event = event; - if (event && pluggable && event !== previousEvent) - plugin.hook(this, 'lifecycle', args); - - if (observers.length === 0) { - events.shift(); - return this.observeEvents(events, args, event, previousResult); - } - else { - var observer = observers.shift(), - result = observer[event].apply(observer, args); - if (result && typeof result.then === 'function') { - return result.then(this.observeEvents.bind(this, events, args, event)) - .catch(this.failTransit.bind(this)) - } - else if (result === false) { - return this.endTransit(false); - } - else { - return this.observeEvents(events, args, event, result); - } - } - }, - - onInvalidTransition: function(transition, from, to) { - throw new Exception("transition is invalid in current state", transition, from, to, this.state); - }, - - onPendingTransition: function(transition, from, to) { - throw new Exception("transition is invalid while previous transition is still in progress", transition, from, to, this.state); - } - -}); - -//------------------------------------------------------------------------------------------------- - -module.exports = JSM; - -//------------------------------------------------------------------------------------------------- - - -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -//----------------------------------------------------------------------------------------------- - -var mixin = __webpack_require__(0), - camelize = __webpack_require__(2), - plugin = __webpack_require__(1), - Config = __webpack_require__(3), - JSM = __webpack_require__(4); - -//----------------------------------------------------------------------------------------------- - -var PublicMethods = { - is: function(state) { return this._fsm.is(state) }, - can: function(transition) { return this._fsm.can(transition) }, - cannot: function(transition) { return this._fsm.cannot(transition) }, - observe: function() { return this._fsm.observe(arguments) }, - transitions: function() { return this._fsm.transitions() }, - allTransitions: function() { return this._fsm.allTransitions() }, - allStates: function() { return this._fsm.allStates() }, - onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to) }, - onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to) }, -} - -var PublicProperties = { - state: { - configurable: false, - enumerable: true, - get: function() { - return this._fsm.state; - }, - set: function(state) { - throw Error('use transitions to change state') - } - } -} - -//----------------------------------------------------------------------------------------------- - -function StateMachine(options) { - return apply(this || {}, options); -} - -function factory() { - var cstor, options; - if (typeof arguments[0] === 'function') { - cstor = arguments[0]; - options = arguments[1] || {}; - } - else { - cstor = function() { this._fsm.apply(this, arguments) }; - options = arguments[0] || {}; - } - var config = new Config(options, StateMachine); - build(cstor.prototype, config); - cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance - return cstor; -} - -//------------------------------------------------------------------------------------------------- - -function apply(instance, options) { - var config = new Config(options, StateMachine); - build(instance, config); - instance._fsm(); - return instance; -} - -function build(target, config) { - if ((typeof target !== 'object') || Array.isArray(target)) - throw Error('StateMachine can only be applied to objects'); - plugin.build(target, config); - Object.defineProperties(target, PublicProperties); - mixin(target, PublicMethods); - mixin(target, config.methods); - config.allTransitions().forEach(function(transition) { - target[camelize(transition)] = function() { - return this._fsm.fire(transition, [].slice.call(arguments)) - } - }); - target._fsm = function() { - this._fsm = new JSM(this, config); - this._fsm.init(arguments); - } -} - -//----------------------------------------------------------------------------------------------- - -StateMachine.version = '3.0.1'; -StateMachine.factory = factory; -StateMachine.apply = apply; -StateMachine.defaults = { - wildcard: '*', - init: { - name: 'init', - from: 'none' - } -} - -//=============================================================================================== - -module.exports = StateMachine; - - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = function(message, transition, from, to, current) { - this.message = message; - this.transition = transition; - this.from = from; - this.to = to; - this.current = current; -} - - -/***/ }) -/******/ ]); -}); diff --git a/erizo_controller/erizoClient/src/ErizoConnectionManager.js b/erizo_controller/erizoClient/src/ErizoConnectionManager.js index 6894882c2c..ae1888aa23 100644 --- a/erizo_controller/erizoClient/src/ErizoConnectionManager.js +++ b/erizo_controller/erizoClient/src/ErizoConnectionManager.js @@ -23,7 +23,6 @@ class ErizoConnection extends EventEmitterConst { ErizoSessionId += 1; spec.sessionId = ErizoSessionId; this.sessionId = ErizoSessionId; - this.connectionId = spec.connectionId; if (!spec.streamRemovedListener) { spec.streamRemovedListener = () => {}; @@ -85,12 +84,8 @@ class ErizoConnection extends EventEmitterConst { this.stack.close(); } - createOffer(isSubscribe, forceOfferToReceive) { - this.stack.createOffer(isSubscribe, forceOfferToReceive); - } - - sendOffer() { - this.stack.sendOffer(); + createOffer(isSubscribe, forceOfferToReceive, streamId) { + this.stack.createOffer(isSubscribe, forceOfferToReceive, streamId); } addStream(stream) { @@ -109,6 +104,8 @@ class ErizoConnection extends EventEmitterConst { } if (stream.local) { this.stack.removeStream(stream.stream); + } else if (this.streamsMap.size() === 1) { + this.streamRemovedListener(stream.getLabel()); } this.streamsMap.remove(streamId); } diff --git a/erizo_controller/erizoClient/src/Room.js b/erizo_controller/erizoClient/src/Room.js index 7b2e4a8977..cbe8626dc0 100644 --- a/erizo_controller/erizoClient/src/Room.js +++ b/erizo_controller/erizoClient/src/Room.js @@ -122,10 +122,9 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { const getP2PConnectionOptions = (stream, peerSocket) => { const options = { - callback(msg, streamIds) { - socket.sendSDP('streamMessageP2P', { + callback(msg) { + socket.sendSDP('signaling_message', { streamId: stream.getID(), - streamIds, peerSocket, msg }); }, @@ -170,6 +169,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { } }); connection.addStream(stream); + connection.createOffer(); }; const removeLocalStreamP2PConnection = (streamInput, peerSocket) => { @@ -192,25 +192,15 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { }); }; - const getErizoConnectionOptions = (stream, connectionId, erizoId, options, isRemote) => { + const getErizoConnectionOptions = (stream, options, isRemote) => { const connectionOpts = { callback(message, streamId = stream.getID()) { Logger.info('Sending message', message, stream.getID(), streamId); - if (message && message.type && message.type === 'updatestream') { - socket.sendSDP('streamMessage', { - streamId, - erizoId, - msg: message, - browser: stream.pc && stream.pc.browser }, undefined, () => {}); - } else { - socket.sendSDP('connectionMessage', { - connectionId, - erizoId, - msg: message, - browser: stream.pc && stream.pc.browser }, undefined, () => {}); - } + socket.sendSDP('signaling_message', { + streamId, + msg: message, + browser: stream.pc && stream.pc.browser }, undefined, () => {}); }, - connectionId, nop2p: true, audio: options.audio && stream.hasAudio(), video: options.video && stream.hasVideo(), @@ -232,11 +222,10 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { return connectionOpts; }; - const createRemoteStreamErizoConnection = (streamInput, connectionId, erizoId, options) => { + const createRemoteStreamErizoConnection = (streamInput, erizoId, options) => { const stream = streamInput; stream.addPC(that.erizoConnectionManager.getOrBuildErizoConnection( - getErizoConnectionOptions(stream, connectionId, erizoId, options, true), - erizoId, spec.singlePC)); + getErizoConnectionOptions(stream, options, true), erizoId, spec.singlePC)); stream.on('added', dispatchStreamSubscribed.bind(null, stream)); stream.on('icestatechanged', (evt) => { Logger.info(`${stream.getID()} - iceConnectionState: ${evt.msg.state}`); @@ -244,12 +233,13 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { onStreamFailed(stream); } }); + stream.pc.createOffer(true, false, stream.getID()); }; - const createLocalStreamErizoConnection = (streamInput, connectionId, erizoId, options) => { + const createLocalStreamErizoConnection = (streamInput, erizoId, options) => { const stream = streamInput; stream.addPC(that.erizoConnectionManager.getOrBuildErizoConnection( - getErizoConnectionOptions(stream, connectionId, erizoId, options), erizoId, spec.singlePC)); + getErizoConnectionOptions(stream, options), erizoId, spec.singlePC)); stream.on('icestatechanged', (evt) => { Logger.info(`${stream.getID()} - iceConnectionState: ${evt.msg.state}`); @@ -258,38 +248,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { } }); stream.pc.addStream(stream); - }; - - const onAutomaticStreamsSubscription = (args) => { - const streamIds = args.streamIds; - const erizoId = args.erizoId; - const connectionId = args.connectionId; - const options = args.options; - let stream; - switch (args.type) { - case 'multiple-initializing': - streamIds.forEach((id) => { - stream = remoteStreams.get(id); - // Prepare each stream to listen to PC events. - createRemoteStreamErizoConnection(stream, connectionId, erizoId, options); - }); - break; - default: - break; - } - }; - - const onAutomaticStreamsUnsubscription = (args) => { - const streamIds = args.streamIds; - let stream; - streamIds.forEach((id) => { - stream = remoteStreams.get(id); - }); - streamIds.forEach((id) => { - stream = remoteStreams.get(id); - removeStream(stream); - delete stream.failed; - }); + if (!options.createOffer) { stream.pc.createOffer(false, spec.singlePC, stream.getID()); } }; // We receive an event with a new stream in the room. @@ -313,39 +272,22 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { that.dispatchEvent(evt); }; - const socketOnStreamMessageFromErizo = (arg) => { - if (arg.context === 'auto-streams-subscription') { - onAutomaticStreamsSubscription(arg.mess); - } else if (arg.context === 'auto-streams-unsubscription') { - onAutomaticStreamsUnsubscription(arg.mess); + const socketOnErizoMessage = (arg) => { + let stream; + if (arg.peerId) { + stream = remoteStreams.get(arg.peerId); } else { - Logger.debug('Failed applying a stream message from erizo', arg); + stream = localStreams.get(arg.streamId); } - }; - const socketOnConnectionMessageFromErizo = (arg) => { - let done = false; - localStreams.forEach((stream) => { - if (!done && !stream.failed && stream.pc && stream.pc.connectionId === arg.connectionId) { - stream.pc.processSignalingMessage(arg.evt); - done = true; - } - }); - if (done) { - return; - } - remoteStreams.forEach((stream) => { - if (!done && !stream.failed && stream.pc && stream.pc.connectionId === arg.connectionId) { - stream.pc.processSignalingMessage(arg.evt); - done = true; - } - }); - if (!done) { - Logger.warning('Received signaling message to unknown connectionId', arg.connectionId); + if (stream && stream.pc && !stream.failed) { + stream.pc.processSignalingMessage(arg.mess); + } else { + Logger.debug('Failed applying a signaling message, stream is no longer present'); } }; - const socketOnStreamMessageFromP2P = (arg) => { + const socketOnPeerMessage = (arg) => { let stream = localStreams.get(arg.streamId); if (stream && !stream.failed) { @@ -557,14 +499,14 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { constraints.maxVideoBW = options.maxVideoBW; constraints.scheme = options.scheme; - socket.sendSDP('publish', constraints, undefined, (id, erizoId, connectionId, error) => { + socket.sendSDP('publish', constraints, undefined, (id, erizoId, error) => { if (id === null) { Logger.error('Error publishing stream', error); callback(undefined, error); return; } populateStreamFunctions(id, stream, error, undefined); - createLocalStreamErizoConnection(stream, connectionId, erizoId, options); + createLocalStreamErizoConnection(stream, erizoId, options); callback(id); }); }; @@ -601,16 +543,16 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { metadata: options.metadata, muteStream: options.muteStream, slideShowMode: options.slideShowMode }; - socket.sendSDP('subscribe', constraint, undefined, (result, erizoId, connectionId, error) => { + socket.sendSDP('subscribe', constraint, undefined, (result, erizoId, error) => { if (result === null) { Logger.error('Error subscribing to stream ', error); callback(undefined, error); return; } - Logger.info('Subscriber added', erizoId, connectionId); - createRemoteStreamErizoConnection(stream, connectionId, erizoId, options); - stream.pc.sendOffer(); + Logger.info('Subscriber added'); + createRemoteStreamErizoConnection(stream, erizoId, options); + callback(true); }); }; @@ -851,7 +793,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { that.sendControlMessage = (stream, type, action) => { if (stream && stream.getID()) { const msg = { type: 'control', action }; - socket.sendSDP('streamMessage', { streamId: stream.getID(), msg }); + socket.sendSDP('signaling_message', { streamId: stream.getID(), msg }); } }; @@ -936,30 +878,6 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { } }; - // const selectors = { - // '/id': '23', - // '/attributes/group': '23', - // '/attributes/kind': 'professor', - // '/attributes/externalId': '10' - // }; - // const negativeSelectors = { - // '/id': '23', - // '/attributes/group': '23', - // '/attributes/kind': 'professor', - // '/attributes/externalId': '10' - // }; - // const options = {audio: true, video: false, forceTurn: true}; - that.autoSubscribe = (selectors, negativeSelectors, options, callback) => { - if (!socket) { - return; - } - socket.sendMessage('autoSubscribe', { selectors, negativeSelectors, options }, (result) => { - if (result) { - callback(result); - } - }); - }; - that.getStreamStats = (stream, callback = () => {}) => { if (!socket) { return 'Error getting stats - no socket'; @@ -992,9 +910,8 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => { that.on('room-disconnected', clearAll); socket.on('onAddStream', socketEventToArgs.bind(null, socketOnAddStream)); - socket.on('stream_message_erizo', socketEventToArgs.bind(null, socketOnStreamMessageFromErizo)); - socket.on('stream_message_p2p', socketEventToArgs.bind(null, socketOnStreamMessageFromP2P)); - socket.on('connection_message_erizo', socketEventToArgs.bind(null, socketOnConnectionMessageFromErizo)); + socket.on('signaling_message_erizo', socketEventToArgs.bind(null, socketOnErizoMessage)); + socket.on('signaling_message_peer', socketEventToArgs.bind(null, socketOnPeerMessage)); socket.on('publish_me', socketEventToArgs.bind(null, socketOnPublishMe)); socket.on('unpublish_me', socketEventToArgs.bind(null, socketOnUnpublishMe)); socket.on('onBandwidthAlert', socketEventToArgs.bind(null, socketOnBandwidthAlert)); diff --git a/erizo_controller/erizoClient/src/Socket.js b/erizo_controller/erizoClient/src/Socket.js index 9d7a703b38..ea29b959c8 100644 --- a/erizo_controller/erizoClient/src/Socket.js +++ b/erizo_controller/erizoClient/src/Socket.js @@ -71,9 +71,8 @@ const Socket = (newIo) => { that.socket = socket; socket.on('onAddStream', emit.bind(that, 'onAddStream')); - socket.on('stream_message_erizo', emit.bind(that, 'stream_message_erizo')); - socket.on('stream_message_p2p', emit.bind(that, 'stream_message_p2p')); - socket.on('connection_message_erizo', emit.bind(that, 'connection_message_erizo')); + socket.on('signaling_message_erizo', emit.bind(that, 'signaling_message_erizo')); + socket.on('signaling_message_peer', emit.bind(that, 'signaling_message_peer')); socket.on('publish_me', emit.bind(that, 'publish_me')); socket.on('unpublish_me', emit.bind(that, 'unpublish_me')); socket.on('onBandwidthAlert', emit.bind(that, 'onBandwidthAlert')); @@ -87,8 +86,6 @@ const Socket = (newIo) => { // We receive an event of a stream removed from the room socket.on('onRemoveStream', emit.bind(that, 'onRemoveStream')); - socket.on('onAutomaticStreamsSubscription', emit.bind(that, 'onAutomaticStreamsSubscription')); - // The socket has disconnected socket.on('disconnect', (reason) => { Logger.debug('disconnect', that.id, reason); diff --git a/erizo_controller/erizoClient/src/webrtc-stacks/BaseStack.js b/erizo_controller/erizoClient/src/webrtc-stacks/BaseStack.js index caaeb5a853..724dfe031d 100644 --- a/erizo_controller/erizoClient/src/webrtc-stacks/BaseStack.js +++ b/erizo_controller/erizoClient/src/webrtc-stacks/BaseStack.js @@ -1,26 +1,20 @@ /* global RTCSessionDescription, RTCIceCandidate, RTCPeerConnection */ - // eslint-disable-next-line import SemanticSdp from '../../../common/semanticSdp/SemanticSdp'; -import Setup from '../../../common/semanticSdp/Setup'; -import Direction from '../../../common/semanticSdp/Direction'; - -import PeerConnectionFsm from './PeerConnectionFsm'; import SdpHelpers from '../utils/SdpHelpers'; import Logger from '../utils/Logger'; -import FunctionQueue from '../utils/FunctionQueue'; const BaseStack = (specInput) => { const that = {}; const specBase = specInput; - const negotiationQueue = new FunctionQueue(); - const firstLocalDescriptionQueue = new FunctionQueue(); - let firstLocalDescriptionSet = false; + const offerQueue = []; let localDesc; let remoteDesc; let localSdp; let remoteSdp; + let processOffer; + let isNegotiating = false; let latestSessionVersion = -1; Logger.info('Starting Base stack', specBase); @@ -55,431 +49,193 @@ const BaseStack = (specInput) => { }; that.peerConnection = new RTCPeerConnection(that.pcConfig, that.con); - let negotiationneededCount = 0; - that.peerConnection.onnegotiationneeded = () => { // one per media which is added - let medias = that.audio ? 1 : 0; - medias += that.video ? 1 : 0; - if (negotiationneededCount % medias === 0) { - that.createOffer(true, true); + + // Aux functions + + const errorCallback = (where, errorcb, message) => { + Logger.error('message:', message, 'in baseStack at', where); + if (errorcb !== undefined) { + errorcb('error'); } - negotiationneededCount += 1; }; - const onFsmError = (message) => { - that.peerConnectionFsm.error(message); + const successCallback = (message) => { + Logger.info('Success in BaseStack', message); }; - const configureLocalSdpAsAnswer = () => { - localDesc.sdp = that.enableSimulcast(localDesc.sdp); - localDesc.type = 'answer'; - localSdp = SemanticSdp.SDPInfo.processString(localDesc.sdp); - SdpHelpers.setMaxBW(localSdp, specBase); - - const numberOfRemoteMedias = that.remoteSdp.getStreams().size; - const numberOfLocalMedias = localSdp.getStreams().size; + const onIceCandidate = (event) => { + let candidateObject = {}; + const candidate = event.candidate; + if (!candidate) { + Logger.info('Gathered all candidates. Sending END candidate'); + candidateObject = { + sdpMLineIndex: -1, + sdpMid: 'end', + candidate: 'end', + }; + } else { + candidateObject = { + sdpMLineIndex: candidate.sdpMLineIndex, + sdpMid: candidate.sdpMid, + candidate: candidate.candidate, + }; + if (!candidateObject.candidate.match(/a=/)) { + candidateObject.candidate = `a=${candidateObject.candidate}`; + } + } - let direction = Direction.reverse('sendrecv'); - if (numberOfRemoteMedias > 0 && numberOfLocalMedias > 0) { - direction = Direction.reverse('sendrecv'); - } else if (numberOfRemoteMedias > 0 && numberOfLocalMedias === 0) { - direction = Direction.reverse('recvonly'); - } else if (numberOfRemoteMedias === 0 && numberOfLocalMedias > 0) { - direction = Direction.reverse('sendonly'); + if (specBase.remoteDescriptionSet) { + specBase.callback({ type: 'candidate', candidate: candidateObject }); } else { - direction = Direction.reverse('inactive'); + specBase.localCandidates.push(candidateObject); + Logger.info('Storing candidate: ', specBase.localCandidates.length, candidateObject); } - localSdp.getMedias().forEach((media) => { - media.setDirection(direction); - }); + }; - localDesc.sdp = localSdp.toString(); - that.localSdp = localSdp; + const checkOfferQueue = () => { + if (!isNegotiating && offerQueue.length > 0) { + const args = offerQueue.shift(); + if (args[0] === 'local') { + that.createOffer(args[1], args[2], args[3]); + } else { + processOffer(args[1]); + } + } }; - const configureLocalSdpAsOffer = () => { - localDesc.sdp = that.enableSimulcast(localDesc.sdp); - localDesc.type = 'offer'; + const setLocalDescForOffer = (isSubscribe, streamId, sessionDescription) => { + localDesc = sessionDescription; + if (!isSubscribe) { + localDesc.sdp = that.enableSimulcast(localDesc.sdp); + } localSdp = SemanticSdp.SDPInfo.processString(localDesc.sdp); SdpHelpers.setMaxBW(localSdp, specBase); - - localSdp.medias.forEach((media) => { - if (media.getSetup() !== Setup.ACTPASS) { - media.setSetup(Setup.ACTPASS); - } - }); localDesc.sdp = localSdp.toString(); that.localSdp = localSdp; - }; - - const setLocalDescForOffer = (isSubscribe, sessionDescription) => { - localDesc = sessionDescription; - - configureLocalSdpAsOffer(); specBase.callback({ type: localDesc.type, sdp: localDesc.sdp, config: { maxVideoBW: specBase.maxVideoBW }, - }); + }, streamId); }; const setLocalDescForAnswer = (sessionDescription) => { localDesc = sessionDescription; - configureLocalSdpAsAnswer(); + localSdp = SemanticSdp.SDPInfo.processString(localDesc.sdp); + SdpHelpers.setMaxBW(localSdp, specBase); + localDesc.sdp = localSdp.toString(); + that.localSdp = localSdp; specBase.callback({ type: localDesc.type, sdp: localDesc.sdp, config: { maxVideoBW: specBase.maxVideoBW }, }); Logger.info('Setting local description', localDesc); - Logger.debug('processOffer - Local Description', localDesc.type, localDesc.sdp); - return that.peerConnection.setLocalDescription(localDesc); + that.peerConnection.setLocalDescription(localDesc).then(() => { + isNegotiating = false; + checkOfferQueue(); + successCallback(); + }).catch(errorCallback); }; - // Functions that are protected by a functionQueue - that.enqueuedCalls = { - negotiationQueue: { - createOffer: negotiationQueue.protectFunction((isSubscribe = false) => { - that.peerConnectionFsm.createOffer(isSubscribe).catch(onFsmError.bind(this)); - }), - - processOffer: negotiationQueue.protectFunction((message) => { - that.peerConnectionFsm.processOffer(message).catch(onFsmError.bind(this)); - }), - - processAnswer: negotiationQueue.protectFunction((message) => { - that.peerConnectionFsm.processAnswer(message).catch(onFsmError.bind(this)); - }), - - negotiateMaxBW: negotiationQueue.protectFunction((configInput, callback) => { - that.peerConnectionFsm.negotiateMaxBW(configInput, callback).catch(onFsmError.bind(this)); - }), - - processNewCandidate: negotiationQueue.protectFunction((message) => { - const msg = message; - try { - let obj; - if (typeof (msg.candidate) === 'object') { - obj = msg.candidate; - } else { - obj = JSON.parse(msg.candidate); - } - if (obj.candidate === 'end') { - // ignore the end candidate for chrome - return; - } - obj.candidate = obj.candidate.replace(/a=/g, ''); - obj.sdpMLineIndex = parseInt(obj.sdpMLineIndex, 10); - const candidate = new RTCIceCandidate(obj); - if (specBase.remoteDescriptionSet) { - negotiationQueue.startEnqueuing(); - that.peerConnectionFsm.addIceCandidate(candidate).catch(onFsmError.bind(this)); - } else { - specBase.remoteCandidates.push(candidate); - } - } catch (e) { - Logger.error('Error parsing candidate', msg.candidate); - } - }), - - addStream: negotiationQueue.protectFunction((stream) => { - negotiationQueue.startEnqueuing(); - that.peerConnectionFsm.addStream(stream).catch(onFsmError.bind(this)); - }), - - removeStream: negotiationQueue.protectFunction((stream) => { - negotiationQueue.startEnqueuing(); - that.peerConnectionFsm.removeStream(stream).catch(onFsmError.bind(this)); - }), - - close: negotiationQueue.protectFunction(() => { - negotiationQueue.startEnqueuing(); - that.peerConnectionFsm.close().catch(onFsmError.bind(this)); - }), - }, - - firstLocalDescriptionQueue: { - createOffer: - firstLocalDescriptionQueue.protectFunction((isSubscribe = false, - forceOfferToReceive = false) => { - if (!firstLocalDescriptionSet) { - firstLocalDescriptionQueue.startEnqueuing(); - } - if (!isSubscribe && !forceOfferToReceive) { - that.mediaConstraints = { - offerToReceiveVideo: false, - offerToReceiveAudio: false, - }; - } - that.enqueuedCalls.negotiationQueue.createOffer(isSubscribe); - }), + processOffer = (message) => { + const msg = message; + if (isNegotiating) { + offerQueue.push(['remote', message]); + return; + } + remoteSdp = SemanticSdp.SDPInfo.processString(msg.sdp); - sendOffer: firstLocalDescriptionQueue.protectFunction(() => { - if (!firstLocalDescriptionSet) { - that.createOffer(true, true); - return; - } - setLocalDescForOffer(true, localDesc); - }), + const sessionVersion = remoteSdp && remoteSdp.origin && remoteSdp.origin.sessionVersion; + if (latestSessionVersion >= sessionVersion) { + Logger.warning(`message: processOffer discarding old sdp sessionVersion: ${sessionVersion}, latestSessionVersion: ${latestSessionVersion}`); + return; + } + isNegotiating = true; + latestSessionVersion = sessionVersion; + + SdpHelpers.setMaxBW(remoteSdp, specBase); + msg.sdp = remoteSdp.toString(); + that.remoteSdp = remoteSdp; + that.peerConnection.setRemoteDescription(msg).then(() => { + that.peerConnection.createAnswer(that.mediaConstraints) + .then(setLocalDescForAnswer) + .catch(errorCallback.bind(null, 'createAnswer', undefined)); + specBase.remoteDescriptionSet = true; + }).catch(errorCallback.bind(null, 'process Offer', undefined)); + }; + + const processAnswer = (message) => { + const msg = message; - processOffer: - firstLocalDescriptionQueue.protectFunction((message) => { - if (!firstLocalDescriptionSet) { - firstLocalDescriptionQueue.startEnqueuing(); + remoteSdp = SemanticSdp.SDPInfo.processString(msg.sdp); + const sessionVersion = remoteSdp && remoteSdp.origin && remoteSdp.origin.sessionVersion; + if (latestSessionVersion >= sessionVersion) { + Logger.warning(`processAnswer discarding old sdp, sessionVersion: ${sessionVersion}, latestSessionVersion: ${latestSessionVersion}`); + return; + } + Logger.info('Set remote and local description'); + latestSessionVersion = sessionVersion; + + SdpHelpers.setMaxBW(remoteSdp, specBase); + that.setStartVideoBW(remoteSdp); + that.setHardMinVideoBW(remoteSdp); + + msg.sdp = remoteSdp.toString(); + Logger.debug('Remote Description', msg.sdp); + Logger.debug('Local Description', localDesc.sdp); + that.remoteSdp = remoteSdp; + + remoteDesc = msg; + that.peerConnection.setLocalDescription(localDesc).then(() => { + that.peerConnection.setRemoteDescription(new RTCSessionDescription(msg)).then(() => { + specBase.remoteDescriptionSet = true; + Logger.info('Candidates to be added: ', specBase.remoteCandidates.length, + specBase.remoteCandidates); + while (specBase.remoteCandidates.length > 0) { + // IMPORTANT: preserve ordering of candidates + that.peerConnection.addIceCandidate(specBase.remoteCandidates.shift()); + } + Logger.info('Local candidates to send:', specBase.localCandidates.length); + while (specBase.localCandidates.length > 0) { + // IMPORTANT: preserve ordering of candidates + specBase.callback({ type: 'candidate', candidate: specBase.localCandidates.shift() }); } - that.enqueuedCalls.negotiationQueue.processOffer(message); - }), - }, + isNegotiating = false; + checkOfferQueue(); + }).catch(errorCallback.bind(null, 'processAnswer', undefined)); + }).catch(errorCallback.bind(null, 'processAnswer', undefined)); }; - // Functions that are protected by the FSM. - // The promise of one has to be resolved before another can be called. - that.protectedCalls = { - protectedAddStream: (stream) => { - that.peerConnection.addStream(stream); - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - return Promise.resolve(); - }, - - protectedRemoveStream: (stream) => { - that.peerConnection.removeStream(stream); - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - return Promise.resolve(); - }, - - protectedCreateOffer: (isSubscribe = false) => { - negotiationQueue.startEnqueuing(); - Logger.debug('Creating offer', that.mediaConstraints); - const rejectMessages = []; - return that.peerConnection.createOffer(that.mediaConstraints) - .then(setLocalDescForOffer.bind(null, isSubscribe)) - .catch((error) => { - rejectMessages.push(`in protectedCreateOffer-createOffer, error: ${error}`); - }) - .then(() => { - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - if (rejectMessages.length !== 0) { - return Promise.reject(rejectMessages); - } - return Promise.resolve(); - }); - }, - - protectedProcessOffer: (message) => { - Logger.info('Protected process Offer,', message, 'localDesc', localDesc); - const msg = message; - remoteSdp = SemanticSdp.SDPInfo.processString(msg.sdp); - - const sessionVersion = remoteSdp && remoteSdp.origin && remoteSdp.origin.sessionVersion; - if (latestSessionVersion >= sessionVersion) { - Logger.warning(`message: processOffer discarding old sdp sessionVersion: ${sessionVersion}, latestSessionVersion: ${latestSessionVersion}`); - // We send an answer back to finish this negotiation - specBase.callback({ - type: 'answer', - sdp: localDesc.sdp, - config: { maxVideoBW: specBase.maxVideoBW }, - }); - return Promise.resolve(); + const processNewCandidate = (message) => { + const msg = message; + try { + let obj; + if (typeof (msg.candidate) === 'object') { + obj = msg.candidate; + } else { + obj = JSON.parse(msg.candidate); } - negotiationQueue.startEnqueuing(); - latestSessionVersion = sessionVersion; - - SdpHelpers.setMaxBW(remoteSdp, specBase); - msg.sdp = remoteSdp.toString(); - that.remoteSdp = remoteSdp; - const rejectMessage = []; - return that.peerConnection.setRemoteDescription(msg) - .then(() => { - specBase.remoteDescriptionSet = true; - }).then(() => that.peerConnection.createAnswer(that.mediaConstraints)) - .catch((error) => { - rejectMessage.push(`in: protectedProcessOffer-createAnswer, error: ${error}`); - }) - .then(setLocalDescForAnswer.bind(this)) - .catch((error) => { - rejectMessage.push(`in: protectedProcessOffer-setLocalDescForAnswer, error: ${error}`); - }) - .then(() => { - setTimeout(() => { - firstLocalDescriptionSet = true; - firstLocalDescriptionQueue.stopEnqueuing(); - firstLocalDescriptionQueue.dequeueAll(); - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - if (rejectMessage.length !== 0) { - return Promise.reject(rejectMessage); - } - return Promise.resolve(); - }); - }, - - protectedProcessAnswer: (message) => { - const msg = message; - - remoteSdp = SemanticSdp.SDPInfo.processString(msg.sdp); - const sessionVersion = remoteSdp && remoteSdp.origin && remoteSdp.origin.sessionVersion; - if (latestSessionVersion >= sessionVersion) { - Logger.warning(`processAnswer discarding old sdp, sessionVersion: ${sessionVersion}, latestSessionVersion: ${latestSessionVersion}`); - return Promise.resolve(); + if (obj.candidate === 'end') { + // ignore the end candidate for chrome + return; } - negotiationQueue.startEnqueuing(); - latestSessionVersion = sessionVersion; - Logger.info('Set remote and local description'); - - SdpHelpers.setMaxBW(remoteSdp, specBase); - that.setStartVideoBW(remoteSdp); - that.setHardMinVideoBW(remoteSdp); - - msg.sdp = remoteSdp.toString(); - - configureLocalSdpAsOffer(); - - Logger.debug('processAnswer - Remote Description', msg.type, msg.sdp); - Logger.debug('processAnswer - Local Description', msg.type, localDesc.sdp); - that.remoteSdp = remoteSdp; - - remoteDesc = msg; - const rejectMessages = []; - return that.peerConnection.setLocalDescription(localDesc) - .then(() => { - that.peerConnection.setRemoteDescription(new RTCSessionDescription(msg)); - }) - .then(() => { - specBase.remoteDescriptionSet = true; - Logger.info('Candidates to be added: ', specBase.remoteCandidates.length, - specBase.remoteCandidates); - while (specBase.remoteCandidates.length > 0) { - // IMPORTANT: preserve ordering of candidates - that.peerConnectionFsm.addIceCandidate(specBase.remoteCandidates.shift()) - .catch(onFsmError.bind(this)); - } - Logger.info('Local candidates to send:', specBase.localCandidates.length); - while (specBase.localCandidates.length > 0) { - // IMPORTANT: preserve ordering of candidates - specBase.callback({ type: 'candidate', candidate: specBase.localCandidates.shift() }); - } - }) - .catch((error) => { - rejectMessages.push(`in: protectedProcessAnswer, error: ${error}`); - }) - .then(() => { - setTimeout(() => { - firstLocalDescriptionSet = true; - firstLocalDescriptionQueue.stopEnqueuing(); - firstLocalDescriptionQueue.dequeueAll(); - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - if (rejectMessages.length !== 0) { - return Promise.reject(rejectMessages); - } - return Promise.resolve(); - }); - }, - - protectedNegotiateMaxBW: (configInput, callback) => { - const config = configInput; - if (config.Sdp || config.maxAudioBW) { - negotiationQueue.startEnqueuing(); - const rejectMessages = []; - - configureLocalSdpAsOffer(); - that.peerConnection.setLocalDescription(localDesc) - .then(() => { - remoteSdp = SemanticSdp.SDPInfo.processString(remoteDesc.sdp); - SdpHelpers.setMaxBW(remoteSdp, specBase); - remoteDesc.sdp = remoteSdp.toString(); - that.remoteSdp = remoteSdp; - return that.peerConnection.setRemoteDescription( - new RTCSessionDescription(remoteDesc)); - }).then(() => { - specBase.remoteDescriptionSet = true; - specBase.callback({ type: 'offer-noanswer', sdp: localDesc.sdp }); - }).catch((error) => { - callback('error', 'updateSpec'); - rejectMessages.push(`in: protectedNegotiateMaxBW error: ${error}`); - }) - .then(() => { - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - if (rejectMessages.length !== 0) { - return Promise.reject(rejectMessages); - } - return Promise.resolve(); - }); + obj.candidate = obj.candidate.replace(/a=/g, ''); + obj.sdpMLineIndex = parseInt(obj.sdpMLineIndex, 10); + const candidate = new RTCIceCandidate(obj); + if (specBase.remoteDescriptionSet) { + that.peerConnection.addIceCandidate(candidate); + } else { + specBase.remoteCandidates.push(candidate); } - }, - - protectedAddIceCandiate: (candidate) => { - const rejectMessages = []; - return that.peerConnection.addIceCandidate(candidate) - .catch((error) => { - rejectMessages.push(`in: protectedAddIceCandidate, error: ${error}`); - }).then(() => { - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - if (rejectMessages.length !== 0) { - return Promise.reject(rejectMessages); - } - return Promise.resolve(); - }); - }, - - protectedClose: () => { - that.peerConnection.close(); - setTimeout(() => { - negotiationQueue.stopEnqueuing(); - negotiationQueue.nextInQueue(); - }, 0); - return Promise.resolve(); - }, - }; - - - const onIceCandidate = (event) => { - let candidateObject = {}; - const candidate = event.candidate; - if (!candidate) { - Logger.info('Gathered all candidates. Sending END candidate'); - candidateObject = { - sdpMLineIndex: -1, - sdpMid: 'end', - candidate: 'end', - }; - } else { - candidateObject = { - sdpMLineIndex: candidate.sdpMLineIndex, - sdpMid: candidate.sdpMid, - candidate: candidate.candidate, - }; - if (!candidateObject.candidate.match(/a=/)) { - candidateObject.candidate = `a=${candidateObject.candidate}`; - } - } - - if (specBase.remoteDescriptionSet) { - specBase.callback({ type: 'candidate', candidate: candidateObject }); - } else { - specBase.localCandidates.push(candidateObject); - Logger.info('Storing candidate: ', specBase.localCandidates.length, candidateObject); + } catch (e) { + Logger.error('Error parsing candidate', msg.candidate); } }; // Peerconnection events + that.peerConnection.onicecandidate = onIceCandidate; // public functions @@ -498,6 +254,11 @@ const BaseStack = (specInput) => { return sdpInput; }; + that.close = () => { + that.state = 'closed'; + that.peerConnection.close(); + }; + that.setSimulcast = (enable) => { that.simulcast = enable; }; @@ -514,23 +275,47 @@ const BaseStack = (specInput) => { const config = configInput; const shouldApplyMaxVideoBWToSdp = specBase.p2p && config.maxVideoBW; const shouldSendMaxVideoBWInOptions = !specBase.p2p && config.maxVideoBW; - if (config.maxVideoBW) { - Logger.debug('Maxvideo Requested:', config.maxVideoBW, - 'limit:', specBase.limitMaxVideoBW); - if (config.maxVideoBW > specBase.limitMaxVideoBW) { - config.maxVideoBW = specBase.limitMaxVideoBW; + if (shouldApplyMaxVideoBWToSdp || config.maxAudioBW) { + if (config.maxVideoBW) { + Logger.debug('Maxvideo Requested:', config.maxVideoBW, + 'limit:', specBase.limitMaxVideoBW); + if (config.maxVideoBW > specBase.limitMaxVideoBW) { + config.maxVideoBW = specBase.limitMaxVideoBW; + } + specBase.maxVideoBW = config.maxVideoBW; + Logger.debug('Result', specBase.maxVideoBW); } - specBase.maxVideoBW = config.maxVideoBW; - Logger.debug('Result', specBase.maxVideoBW); - } - if (config.maxAudioBW) { - if (config.maxAudioBW > specBase.limitMaxAudioBW) { - config.maxAudioBW = specBase.limitMaxAudioBW; + if (config.maxAudioBW) { + if (config.maxAudioBW > specBase.limitMaxAudioBW) { + config.maxAudioBW = specBase.limitMaxAudioBW; + } + specBase.maxAudioBW = config.maxAudioBW; + } + + localSdp = SemanticSdp.SDPInfo.processString(localDesc.sdp); + SdpHelpers.setMaxBW(localSdp, specBase); + localDesc.sdp = localSdp.toString(); + that.localSdp = localSdp; + + if (config.Sdp || config.maxAudioBW) { + Logger.debug('Updating with SDP renegotiation', specBase.maxVideoBW, specBase.maxAudioBW); + that.peerConnection.setLocalDescription(localDesc) + .then(() => { + remoteSdp = SemanticSdp.SDPInfo.processString(remoteDesc.sdp); + SdpHelpers.setMaxBW(remoteSdp, specBase); + remoteDesc.sdp = remoteSdp.toString(); + that.remoteSdp = remoteSdp; + return that.peerConnection.setRemoteDescription(new RTCSessionDescription(remoteDesc)); + }).then(() => { + specBase.remoteDescriptionSet = true; + specBase.callback({ type: 'updatestream', sdp: localDesc.sdp }, streamId); + }).catch(errorCallback.bind(null, 'updateSpec', callback)); + } else { + Logger.debug('Updating without SDP renegotiation, ' + + 'newVideoBW:', specBase.maxVideoBW, + 'newAudioBW:', specBase.maxAudioBW); + specBase.callback({ type: 'updatestream', sdp: localDesc.sdp }, streamId); } - specBase.maxAudioBW = config.maxAudioBW; - } - if (shouldApplyMaxVideoBWToSdp || config.maxAudioBW) { - that.enqueuedCalls.negotiationQueue.negotiateMaxBW(config, callback); } if (shouldSendMaxVideoBWInOptions || config.minVideoBW || @@ -549,38 +334,42 @@ const BaseStack = (specInput) => { } }; + that.createOffer = (isSubscribe = false, forceOfferToReceive = false, streamId = '') => { + if (!isSubscribe && !forceOfferToReceive) { + that.mediaConstraints = { + offerToReceiveVideo: false, + offerToReceiveAudio: false, + }; + } + if (isNegotiating) { + offerQueue.push(['local', isSubscribe, forceOfferToReceive, streamId]); + return; + } + isNegotiating = true; + Logger.debug('Creating offer', that.mediaConstraints, streamId); + that.peerConnection.createOffer(that.mediaConstraints) + .then(setLocalDescForOffer.bind(null, isSubscribe, streamId)) + .catch(errorCallback.bind(null, 'Create Offer', undefined)); + }; - // We need to protect it against calling multiple times to createOffer. - // Otherwise it could change the ICE credentials before calling setLocalDescription - // the first time in Chrome. - that.createOffer = that.enqueuedCalls.firstLocalDescriptionQueue.createOffer; - - that.sendOffer = that.enqueuedCalls.firstLocalDescriptionQueue.sendOffer; - - that.addStream = that.enqueuedCalls.negotiationQueue.addStream; - - that.removeStream = that.enqueuedCalls.negotiationQueue.removeStream; - - that.close = that.enqueuedCalls.negotiationQueue.close; + that.addStream = (stream) => { + that.peerConnection.addStream(stream); + }; + that.removeStream = (stream) => { + that.peerConnection.removeStream(stream); + }; that.processSignalingMessage = (msgInput) => { if (msgInput.type === 'offer') { - that.enqueuedCalls.firstLocalDescriptionQueue.processOffer(msgInput); + processOffer(msgInput); } else if (msgInput.type === 'answer') { - that.enqueuedCalls.negotiationQueue.processAnswer(msgInput); + processAnswer(msgInput); } else if (msgInput.type === 'candidate') { - that.enqueuedCalls.negotiationQueue.processNewCandidate(msgInput); - } else if (msgInput.type === 'error') { - Logger.error('Received error signaling message, state:', msgInput.previousType, firstLocalDescriptionQueue.isEnqueueing()); - if (msgInput.previousType === 'offer' && firstLocalDescriptionQueue.isEnqueueing()) { - firstLocalDescriptionQueue.stopEnqueuing(); - firstLocalDescriptionQueue.nextInQueue(); - } + processNewCandidate(msgInput); } }; - that.peerConnectionFsm = new PeerConnectionFsm(that.protectedCalls); return that; }; diff --git a/erizo_controller/erizoClient/src/webrtc-stacks/ChromeStableStack.js b/erizo_controller/erizoClient/src/webrtc-stacks/ChromeStableStack.js index 6cb059538b..e83c3f780e 100644 --- a/erizo_controller/erizoClient/src/webrtc-stacks/ChromeStableStack.js +++ b/erizo_controller/erizoClient/src/webrtc-stacks/ChromeStableStack.js @@ -18,10 +18,7 @@ const ChromeStableStack = (specInput) => { if (!that.simulcast) { return sdp; } - const hasAlreadySetSimulcast = sdp.match(new RegExp('a=ssrc-group:SIM', 'g')) !== null; - if (hasAlreadySetSimulcast) { - return sdp; - } + // TODO(javier): Improve the way we check for current video ssrcs const matchGroup = sdp.match(/a=ssrc-group:FID ([0-9]*) ([0-9]*)\r?\n/); if (!matchGroup || (matchGroup.length <= 0)) { diff --git a/erizo_controller/erizoClient/src/webrtc-stacks/PeerConnectionFsm.js b/erizo_controller/erizoClient/src/webrtc-stacks/PeerConnectionFsm.js deleted file mode 100644 index 5be7f723fe..0000000000 --- a/erizo_controller/erizoClient/src/webrtc-stacks/PeerConnectionFsm.js +++ /dev/null @@ -1,109 +0,0 @@ -/* global */ - -import StateMachine from '../../lib/state-machine'; -import Logger from '../utils/Logger'; - -const activeStates = ['initial', 'failed', 'stable']; -const HISTORY_SIZE_LIMIT = 200; -// FSM -const PeerConnectionFsm = StateMachine.factory({ - init: 'initial', - transitions: [ - { name: 'create-offer', from: activeStates, to: 'stable' }, - { name: 'add-ice-candidate', from: activeStates, to: function nextState() { return this.state; } }, - { name: 'process-answer', from: activeStates, to: 'stable' }, - { name: 'process-offer', from: activeStates, to: 'stable' }, - { name: 'negotiate-max-bw', from: activeStates, to: 'stable' }, - { name: 'add-stream', from: activeStates, to: function nextState() { return this.state; } }, - { name: 'remove-stream', from: activeStates, to: function nextState() { return this.state; } }, - { name: 'close', from: activeStates, to: 'closed' }, - { name: 'error', from: '*', to: 'failed' }, - ], - data: function data(baseStackCalls) { - return { baseStackCalls, - history: [], - }; - }, - methods: { - getHistory: function getHistory() { - return this.history; - }, - - onBeforeClose: function onBeforeClose(lifecycle) { - Logger.info(`FSM onBeforeClose from: ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedClose(); - }, - - onBeforeAddIceCandidate: function onBeforeAddIceCandidate(lifecycle, candidate) { - Logger.info(`FSM onBeforeAddIceCandidate, from ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedAddIceCandidate(candidate); - }, - - onBeforeAddStream: function onBeforeAddStream(lifecycle, stream) { - Logger.info(`FSM onBeforeAddStream, from ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedAddStream(stream); - }, - - onBeforeRemoveStream: function onBeforeRemoveStream(lifecycle, stream) { - Logger.info(`FSM onBeforeRemoveStream, from ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedRemoveStream(stream); - }, - - onBeforeCreateOffer: function onBeforeCreateOffer(lifecycle, isSubscribe) { - Logger.info(`FSM onBeforeCreateOffer, from ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedCreateOffer(isSubscribe); - }, - - onBeforeProcessOffer: - function onBeforeProcessOffer(lifecycle, message) { - Logger.info(`FSM onBeforeProcessOffer, from ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedProcessOffer(message); - }, - - onBeforeProcessAnswer: - function onBeforeProcessAnswer(lifecycle, message) { - Logger.info(`FSM onBeforeProcessAnswer from: ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedProcessAnswer(message); - }, - - onBeforeNegotiateMaxBW: - function onBeforeNegotiateMaxBW(lifecycle, configInput, callback) { - Logger.info(`FSM onBeforeNegotiateMaxBW from: ${lifecycle.from}, to: ${lifecycle.to}`); - return this.baseStackCalls.protectedNegotiateMaxBW(configInput, callback); - }, - - onStable: function onStable(lifecycle) { - Logger.info(`FSM reached STABLE, from ${lifecycle.from}, to: ${lifecycle.to}`); - }, - - onClosed: function onClosed(lifecycle) { - Logger.info(`FSM reached close, from ${lifecycle.from}, to: ${lifecycle.to}`); - }, - - onTransition: function saveToHistory(lifecycle) { - Logger.debug(`FSM onTransition, transition: ${lifecycle.transition}, from ${lifecycle.from}, to: ${lifecycle.to}`); - this.history.push( - { from: lifecycle.from, to: lifecycle.to, transition: lifecycle.transition }); - if (this.history.length > HISTORY_SIZE_LIMIT) { - this.history.shift(); - } - }, - - onError: function onError(lifecycle, message) { - Logger.error(`FSM Error Transition Failed, message: ${message}, from: ${lifecycle.from}, to: ${lifecycle.to}, printing history:`); - this.history.forEach((item) => { - Logger.error(item); - }); - }, - - onInvalidTransition: function onInvalidTransition(transition, from, to) { - Logger.error(`FSM Error Invalid transition: ${transition}, from: ${from}, to: ${to}`); - }, - - onPendingTransition: function onPendingTransition(transition, from, to) { - Logger.error(`FSM Error Pending transition: ${transition}, from: ${from}, to: ${to}`); - }, - }, -}); - -export default PeerConnectionFsm; diff --git a/erizo_controller/erizoController/erizoController.js b/erizo_controller/erizoController/erizoController.js index 2a4c646fa3..d893ed9206 100644 --- a/erizo_controller/erizoController/erizoController.js +++ b/erizo_controller/erizoController/erizoController.js @@ -308,7 +308,7 @@ const listen = () => { channel.on('connected', (token, options, callback) => { options = options || {}; try { - const room = rooms.getOrCreateRoom(myId, token.room, token.p2p); + const room = rooms.getOrCreateRoom(token.room, token.p2p); options.singlePC = getSinglePCConfig(options.singlePC); const client = room.createClient(channel, token, options); log.info(`message: client connected, clientId: ${client.id}, ` + @@ -439,14 +439,6 @@ exports.deleteRoom = (roomId, callback) => { exports.getContext = () => rooms; -exports.connectionStatusEvent = (clientId, connectionId, info, evt) => { - log.info('connectionStatusEvent', clientId, connectionId, info, evt); - const room = rooms.getRoomWithClientId(clientId); - if (room) { - room.sendConnectionMessageToClient(clientId, connectionId, info, evt); - } -}; - amqper.connect(() => { try { rooms.on('updated', updateMyState); diff --git a/erizo_controller/erizoController/models/Client.js b/erizo_controller/erizoController/models/Client.js index 27182af62f..c907e3aeba 100644 --- a/erizo_controller/erizoController/models/Client.js +++ b/erizo_controller/erizoController/models/Client.js @@ -34,9 +34,7 @@ class Client extends events.EventEmitter { listenToSocketEvents() { log.debug(`message: Adding listeners to socket events, client.id: ${this.id}`); this.socketEventListeners.set('sendDataStream', this.onSendDataStream.bind(this)); - this.socketEventListeners.set('connectionMessage', this.onConnectionMessage.bind(this)); - this.socketEventListeners.set('streamMessage', this.onStreamMessage.bind(this)); - this.socketEventListeners.set('streamMessageP2P', this.onStreamMessageP2P.bind(this)); + this.socketEventListeners.set('signaling_message', this.onSignalingMessage.bind(this)); this.socketEventListeners.set('updateStreamAttributes', this.onUpdateStreamAttributes.bind(this)); this.socketEventListeners.set('publish', this.onPublish.bind(this)); this.socketEventListeners.set('subscribe', this.onSubscribe.bind(this)); @@ -44,7 +42,6 @@ class Client extends events.EventEmitter { this.socketEventListeners.set('stopRecorder', this.onStopRecorder.bind(this)); this.socketEventListeners.set('unpublish', this.onUnpublish.bind(this)); this.socketEventListeners.set('unsubscribe', this.onUnsubscribe.bind(this)); - this.socketEventListeners.set('autoSubscribe', this.onAutoSubscribe.bind(this)); this.socketEventListeners.set('getStreamStats', this.onGetStreamStats.bind(this)); this.socketEventListeners.forEach((value, key) => { this.channel.socketOn(key, value); @@ -74,158 +71,6 @@ class Client extends events.EventEmitter { this.channel.sendBuffer(buffer); } - setSelectors(selectors, negativeSelectors, options) { - this.selectors = selectors; - this.negativeSelectors = negativeSelectors; - this.selectorOptions = options; - this.onInternalAutoSubscriptionChange(); - } - - onInternalAutoSubscriptionChange() { - if (!this.selectors && !this.negativeSelectors) { - return; - } - const subscribableStreams = []; - const unsubscribableStreams = []; - this.room.forEachStream((stream) => { - // We don't subscribe/unsubscribe to own published - if (this.streams.indexOf(stream.getID()) !== -1) { - return; - } - if (stream.meetAnySelector(this.selectors) && - !stream.meetAnySelector(this.negativeSelectors)) { - if (stream.hasData() && this.options.data !== false) { - stream.addDataSubscriber(this.id); - } - if (stream.hasAudio() || stream.hasVideo() || stream.hasScreen()) { - subscribableStreams.push(stream); - } - } else { - if (stream.hasData() && this.options.data !== false) { - stream.removeDataSubscriber(this.id); - } - if (stream.hasAudio() || stream.hasVideo() || stream.hasScreen()) { - unsubscribableStreams.push(stream); - } - } - }); - if (subscribableStreams.length > 0) { - this.onMultipleSubscribe(subscribableStreams, this.selectorOptions); - } - if (unsubscribableStreams.length > 0) { - this.onMultipleUnsubscribe(unsubscribableStreams); - } - } - - onMultipleSubscribe(streams, options = {}) { - if (this.room.p2p) { - streams.forEach((stream) => { - const clientId = stream.getClient(); - const client = this.room.getClientById(clientId); - client.sendMessage('publish_me', { streamId: stream.getID(), peerSocket: this.id }); - }); - return; - } - log.info('message: addMultipleSubscribers requested, ' + - `streams: ${streams}, ` + - `clientId: ${this.id}`); - options.mediaConfiguration = this.token.mediaConfiguration; - options.singlePC = this.options.singlePC || false; - const streamIds = streams.map(stream => stream.getID()); - this.room.controller.addMultipleSubscribers(this.id, streamIds, options, (signMess) => { - // We can receive multiple initializing messages with subsets of streamIds. Each subset - // is sent from a single ErizoJS. - if (signMess.type === 'initializing') { - log.info('message: addMultipleSubscribers, ' + - 'state: SUBSCRIBER_INITIAL, ' + - `clientId: ${this.id}, ` + - `streamIds: ${signMess.streamIds}`); - if (global.config.erizoController.report.session_events) { - const timeStamp = new Date(); - if (signMess.streamIds) { - signMess.streamIds.forEach((streamId) => { - this.room.amqper.broadcast('event', { room: this.room.id, - user: this.id, - name: this.user.name, - type: 'subscribe', - stream: streamId, - timestamp: timeStamp.getTime() }); - }); - } - } - } else if (signMess.type === 'failed') { - // TODO: Add Stats event - log.warn('message: addMultipleSubscribers ICE Failed, ' + - 'state: SUBSCRIBER_FAILED, ' + - `streamId: ${signMess.streamId}, ` + - `clientId: ${this.id}`); - this.sendMessage('connection_failed', { type: 'subscribe', - streamId: signMess.streamId }); - return; - } else if (signMess.type === 'ready') { - log.info('message: addMultipleSubscribers, ' + - 'state: SUBSCRIBER_READY, ' + - `streamId: ${signMess.streamId}, ` + - `clientId: ${this.id}`); - } else if (signMess.type === 'bandwidthAlert') { - this.sendMessage('onBandwidthAlert', { streamID: signMess.streamId, - message: signMess.message, - bandwidth: signMess.bandwidth }); - return; - } else if (signMess === 'timeout') { - log.error('message: addMultipleSubscribers timeout when contacting ErizoJS, ' + - `streamId: ${signMess.streamId}, ` + - `clientId: ${this.id}`); - return; - } - - this.sendMessage('stream_message_erizo', { mess: signMess, - options, - context: signMess.context, - peerIds: signMess.streamIds }); - }); - } - - onMultipleUnsubscribe(streams) { - if (this.room.p2p) { - streams.forEach((stream) => { - const clientId = stream.getClient(); - const client = this.room.getClientById(clientId); - client.sendMessage('unpublish_me', { streamId: stream.getID(), peerSocket: this.id }); - }); - return; - } - const streamIds = streams.map(stream => stream.getID()); - log.debug('message: removeMultipleSubscribers requested, ' + - `streamIds: ${streamIds}, ` + - `clientId: ${this.id}`); - - this.room.controller.removeMultipleSubscribers(this.id, streamIds, (signMess) => { - if (global.config.erizoController.report.session_events) { - if (signMess === 'timeout') { - log.error('message: removeMultipleSubscribers timeout when contacting ErizoJS, ' + - `streamId: ${signMess.streamId}, ` + - `clientId: ${this.id}`); - return; - } - - const timeStamp = new Date(); - signMess.streamIds.forEach((streamId) => { - this.room.amqper.broadcast('event', { room: this.room.id, - user: this.id, - type: 'unsubscribe', - stream: streamId, - timestamp: timeStamp.getTime() }); - }); - } - - this.sendMessage('stream_message_erizo', { mess: signMess, - options: {}, - context: signMess.context, - peerIds: signMess.streamIds }); - }); - } - sendMessage(type, arg) { this.channel.sendMessage(type, arg); } @@ -265,74 +110,27 @@ class Client extends events.EventEmitter { }); } - onStreamMessageP2P(message) { + onSignalingMessage(message) { if (this.room === undefined) { - log.error('message: streamMessageP2P for user in undefined room' + + log.error('message: singaling_message for user in undefined room' + `, streamId: ${message.streamId}, user: ${this.user}`); this.disconnect(); - return; - } - if (!this.room.p2p) { - log.error('message: streamMessageP2P for user in non p2p room' + - `, streamId: ${message.streamId}, user: ${this.user}`); - return; - } - const targetClient = this.room.getClientById(message.peerSocket); - if (targetClient) { - targetClient.sendMessage('stream_message_p2p', - { streamId: message.streamId, - peerSocket: this.id, - msg: message.msg }); - } - } - - onConnectionMessage(message) { - if (this.room === undefined) { - log.error('message: connectionMessage for user in undefined room' + - `, connectionId: ${message.connectionId}, user: ${this.user}`); - this.disconnect(); - return; } if (this.room.p2p) { - log.error('message: connectionMessage for user in p2p room' + - `, connectionId: ${message.connectionId}, user: ${this.user}`); - return; - } - const callback = (result) => { - let type = message && message.msg && message.msg.type; - type = type || 'unknown'; - if (result.error && type === 'offer') { - this.sendMessage('connection_message_erizo', { - connectionId: message.connectionId, - info: 'error', - evt: { type: 'error', previousType: 'offer' }, - }); + const targetClient = this.room.getClientById(message.peerSocket); + if (targetClient) { + targetClient.sendMessage('signaling_message_peer', + { streamId: message.streamId, peerSocket: this.id, msg: message.msg }); } - }; - this.room.controller.processConnectionMessageFromClient(message.erizoId, this.id, - message.connectionId, message.msg, callback.bind(this)); - } - - onStreamMessage(message) { - if (this.room === undefined) { - log.error('message: streamMessage for user in undefined room' + - `, streamId: ${message.streamId}, user: ${this.user}`); - this.disconnect(); - return; - } - if (this.room.p2p) { - log.error('message: streamMessage for user in p2p room' + - `, streamId: ${message.streamId}, user: ${this.user}`); - return; - } - const isControlMessage = message.msg.type === 'control'; - if (!isControlMessage || - (isControlMessage && this.hasPermission(message.msg.action.name))) { - this.room.controller.processStreamMessageFromClient(message.erizoId, this.id, - message.streamId, message.msg); } else { - log.info('message: User unauthorized to execute action on stream, action: ' + - `${message.msg.action.name}, streamId: ${message.streamId}`); + const isControlMessage = message.msg.type === 'control'; + if (!isControlMessage || + (isControlMessage && this.hasPermission(message.msg.action.name))) { + this.room.controller.processSignaling(this.id, message.streamId, message.msg); + } else { + log.info('message: User unauthorized to execute action on stream, action: ' + + `${message.msg.action.name}, streamId: ${message.streamId}`); + } } } @@ -352,9 +150,6 @@ class Client extends events.EventEmitter { client.sendMessage('onUpdateAttributeStream', message); } }); - this.room.forEachClient((client) => { - client.onInternalAutoSubscriptionChange(); - }); } onPublish(options, sdp, callback) { @@ -403,7 +198,7 @@ class Client extends events.EventEmitter { logger.objectToLog(options.attributes)); this.room.controller.addPublisher(this.id, id, options, (signMess) => { if (signMess.type === 'initializing') { - callback(id, signMess.erizoId, signMess.connectionId); + callback(id, signMess.erizoId); st = ST.Stream({ id, client: this.id, audio: options.audio, @@ -432,7 +227,6 @@ class Client extends events.EventEmitter { agent: signMess.agentId, attributes: options.attributes }); } - return; } else if (signMess.type === 'failed') { log.warn('message: addPublisher ICE Failed, ' + 'state: PUBLISHER_FAILED, ' + @@ -443,17 +237,11 @@ class Client extends events.EventEmitter { return; } else if (signMess.type === 'ready') { st.status = PUBLISHER_READY; - this.room.forEachClient((client) => { - client.onInternalAutoSubscriptionChange(); - }); this.room.sendMessage('onAddStream', st.getPublicStream()); log.info('message: addPublisher, ' + 'state: PUBLISHER_READY, ' + `streamId: ${id}, ` + `clientId: ${this.id}`); - return; - } else if (signMess.type === 'started') { - return; } else if (signMess === 'timeout-erizojs') { log.error('message: addPublisher timeout when contacting ErizoJS, ' + `streamId: ${id}, clientId: ${this.id}`); @@ -471,7 +259,7 @@ class Client extends events.EventEmitter { return; } log.debug('Sending message back to the client', id); - this.sendMessage('stream_message_erizo', { mess: signMess, streamId: id }); + this.sendMessage('signaling_message_erizo', { mess: signMess, streamId: id }); }); } else { const st = ST.Stream({ id, @@ -522,7 +310,7 @@ class Client extends events.EventEmitter { 'state: SUBSCRIBER_INITIAL, ' + `clientId: ${this.id}, ` + `streamId: ${options.streamId}`); - callback(true, signMess.erizoId, signMess.connectionId); + callback(true, signMess.erizoId); if (global.config.erizoController.report.session_events) { const timeStamp = new Date(); this.room.amqper.broadcast('event', { room: this.room.id, @@ -542,14 +330,11 @@ class Client extends events.EventEmitter { this.sendMessage('connection_failed', { type: 'subscribe', streamId: options.streamId }); return; - } else if (signMess.type === 'started') { - return; } else if (signMess.type === 'ready') { log.info('message: addSubscriber, ' + 'state: SUBSCRIBER_READY, ' + `streamId: ${options.streamId}, ` + `clientId: ${this.id}`); - return; } else if (signMess.type === 'bandwidthAlert') { this.sendMessage('onBandwidthAlert', { streamID: options.streamId, message: signMess.message, @@ -562,7 +347,7 @@ class Client extends events.EventEmitter { return; } - this.sendMessage('stream_message_erizo', { mess: signMess, + this.sendMessage('signaling_message_erizo', { mess: signMess, peerId: options.streamId }); }); } @@ -722,20 +507,6 @@ class Client extends events.EventEmitter { } } - onAutoSubscribe(data, callback = () => {}) { - if (!this.hasPermission(Permission.SUBSCRIBE)) { - if (callback) callback(null, 'Unauthorized'); - return; - } - - const selectors = (data && data.selectors) || {}; - const negativeSelectors = (data && data.negativeSelectors) || {}; - const options = (data && data.options) || {}; - - this.setSelectors(selectors, negativeSelectors, options); - callback(); - } - onDisconnect() { this.stopListeningToSocketEvents(); const timeStamp = new Date(); diff --git a/erizo_controller/erizoController/models/Room.js b/erizo_controller/erizoController/models/Room.js index 194d67535b..1eff4366b5 100644 --- a/erizo_controller/erizoController/models/Room.js +++ b/erizo_controller/erizoController/models/Room.js @@ -7,12 +7,11 @@ const logger = require('./../../common/logger').logger; const log = logger.getLogger('ErizoController - Room'); class Room extends events.EventEmitter { - constructor(erizoControllerId, amqper, ecch, id, p2p) { + constructor(amqper, ecch, id, p2p) { super(); this.streams = new Map(); this.clients = new Map(); this.id = id; - this.erizoControllerId = erizoControllerId; this.p2p = p2p; this.amqper = amqper; this.ecch = ecch; @@ -35,10 +34,6 @@ class Room extends events.EventEmitter { return this.streams.delete(id); } - hasClientWithId(id) { - return this.clients.has(id); - } - getClientById(id) { return this.clients.get(id); } @@ -68,10 +63,7 @@ class Room extends events.EventEmitter { } setupRoomController() { - this.controller = controller.RoomController({ - amqper: this.amqper, - ecch: this.ecch, - erizoControllerId: this.erizoControllerId }); + this.controller = controller.RoomController({ amqper: this.amqper, ecch: this.ecch }); this.controller.addEventListener(this.onRoomControllerEvent.bind(this)); } @@ -87,13 +79,6 @@ class Room extends events.EventEmitter { } } - sendConnectionMessageToClient(clientId, connectionId, info, evt) { - const client = this.getClientById(clientId); - if (client) { - client.sendMessage('connection_message_erizo', { connectionId, info, evt }); - } - } - sendMessage(method, args) { this.forEachClient((client) => { log.debug('message: sendMsgToRoom,', @@ -117,10 +102,10 @@ class Rooms extends events.EventEmitter { return this.rooms.size; } - getOrCreateRoom(erizoControllerId, id, p2p) { + getOrCreateRoom(id, p2p) { let room = this.rooms.get(id); if (room === undefined) { - room = new Room(erizoControllerId, this.amqper, this.ecch, id, p2p); + room = new Room(this.amqper, this.ecch, id, p2p); this.rooms.set(room.id, room); room.on('room-empty', this.deleteRoom.bind(this, id)); this.emit('updated'); @@ -134,16 +119,6 @@ class Rooms extends events.EventEmitter { }); } - getRoomWithClientId(id) { - // eslint-disable-next-line no-restricted-syntax - for (const room of this.rooms.values()) { - if (room.hasClientWithId(id)) { - return room; - } - } - return undefined; - } - getRoomById(id) { return this.rooms.get(id); } diff --git a/erizo_controller/erizoController/models/Stream.js b/erizo_controller/erizoController/models/Stream.js index 42dac272ae..b297f5f9b9 100644 --- a/erizo_controller/erizoController/models/Stream.js +++ b/erizo_controller/erizoController/models/Stream.js @@ -67,29 +67,5 @@ exports.Stream = (spec) => { attributes: spec.attributes }; }; - // const selectors = { - // '/id': '23', - // '/attributes/group': '23', - // '/attributes/kind': 'professor', - // '/attributes/externalId': '10' - // }; - that.meetAnySelector = (selectors) => { - // eslint-disable-next-line no-restricted-syntax - for (const selector of Object.keys(selectors)) { - const value = selectors[selector]; - if (selector.startsWith('/attributes')) { - const attribute = selector.replace('/attributes/', ''); - if (that.getAttributes()[attribute] === value) { - return true; - } - } else if (selector === '/id' && value === that.getID()) { - return true; - } else if (selector === '/label' && value === spec.label) { - return true; - } - } - return false; - }; - return that; }; diff --git a/erizo_controller/erizoController/roomController.js b/erizo_controller/erizoController/roomController.js index 59f087774c..54d02504d9 100644 --- a/erizo_controller/erizoController/roomController.js +++ b/erizo_controller/erizoController/roomController.js @@ -21,7 +21,6 @@ exports.RoomController = (spec) => { const externalOutputs = {}; const amqper = spec.amqper; const ecch = spec.ecch; - const erizoControllerId = spec.erizoControllerId; const KEEPALIVE_INTERVAL = 5 * 1000; const TIMEOUT_LIMIT = 2; const MAX_ERIZOJS_RETRIES = 3; @@ -116,8 +115,7 @@ exports.RoomController = (spec) => { }); }; - const getErizoQueueFromStreamId = streamId => `ErizoJS_${publishers[streamId]}`; - const getErizoQueueFromErizoId = erizoId => `ErizoJS_${erizoId}`; + const getErizoQueue = streamId => `ErizoJS_${publishers[streamId]}`; that.getPublishers = () => publishers; that.getSubscribers = () => subscribers; @@ -138,7 +136,7 @@ exports.RoomController = (spec) => { publishers[publisherId] = erizoId; subscribers[publisherId] = []; - amqper.callRpc(getErizoQueueFromStreamId(publisherId), 'addExternalInput', args, + amqper.callRpc(getErizoQueue(publisherId), 'addExternalInput', args, { callback }, 20000); erizos.findById(erizoId).publishers.push(publisherId); @@ -155,7 +153,7 @@ exports.RoomController = (spec) => { const args = [publisherId, url, options]; - amqper.callRpc(getErizoQueueFromStreamId(publisherId), 'addExternalOutput', args, undefined); + amqper.callRpc(getErizoQueue(publisherId), 'addExternalOutput', args, undefined); // Track external outputs externalOutputs[url] = publisherId; @@ -173,7 +171,7 @@ exports.RoomController = (spec) => { log.info(`removeExternalOutput, url: ${url}`); const args = [publisherId, url]; - amqper.callRpc(getErizoQueueFromStreamId(publisherId), 'removeExternalOutput', args, undefined); + amqper.callRpc(getErizoQueue(publisherId), 'removeExternalOutput', args, undefined); // Remove track delete externalOutputs[url]; @@ -186,14 +184,18 @@ exports.RoomController = (spec) => { } }; - that.processConnectionMessageFromClient = (erizoId, clientId, connectionId, msg, callback) => { - const args = [erizoControllerId, clientId, connectionId, msg]; - amqper.callRpc(getErizoQueueFromErizoId(erizoId), 'processConnectionMessage', args, { callback }); - }; - - that.processStreamMessageFromClient = (erizoId, clientId, streamId, msg) => { - const args = [erizoControllerId, clientId, streamId, msg]; - amqper.callRpc(getErizoQueueFromErizoId(erizoId), 'processStreamMessage', args, {}); + that.processSignaling = (clientId, streamId, msg) => { + if (publishers[streamId] !== undefined) { + log.info('message: processSignaling, ' + + `clientId: ${clientId}, ` + + `streamId: ${streamId}`); + const args = [clientId, streamId, msg]; + amqper.callRpc(getErizoQueue(streamId), 'processSignaling', args, {}); + } else { + log.warn('message: processSignaling no publisher, ' + + `clientId: ${clientId}, ` + + `streamId: ${streamId}`); + } }; /* @@ -226,17 +228,17 @@ exports.RoomController = (spec) => { logger.objectToLog(options.metadata)); // Track publisher locally // then we call its addPublisher method. - const args = [erizoControllerId, clientId, streamId, options]; + const args = [clientId, streamId, options]; publishers[streamId] = erizoId; subscribers[streamId] = []; - amqper.callRpc(getErizoQueueFromStreamId(streamId), 'addPublisher', args, + amqper.callRpc(getErizoQueue(streamId), 'addPublisher', args, { callback: (data) => { if (data === 'timeout') { if (retries < MAX_ERIZOJS_RETRIES) { log.warn('message: addPublisher ErizoJS timeout, ' + `streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)}, ` + + `erizoId: ${getErizoQueue(streamId)}, ` + `retries: ${retries}, `, logger.objectToLog(options.metadata)); publishers[streamId] = undefined; @@ -246,7 +248,7 @@ exports.RoomController = (spec) => { } log.warn('message: addPublisher ErizoJS timeout no retry, ' + `retries: ${retries}, streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)},`, + `erizoId: ${getErizoQueue(streamId)},`, logger.objectToLog(options.metadata)); const erizo = erizos.findById(publishers[streamId]); if (erizo !== undefined) { @@ -255,7 +257,6 @@ exports.RoomController = (spec) => { } callback('timeout-erizojs'); } else { - // TODO (javier): Check new path for this if (data.type === 'initializing') { data.agentId = agentId; data.erizoId = erizoId; @@ -299,9 +300,9 @@ exports.RoomController = (spec) => { if (options.audio === undefined) options.audio = true; if (options.video === undefined) options.video = true; - const args = [erizoControllerId, clientId, streamId, options]; + const args = [clientId, streamId, options]; - amqper.callRpc(getErizoQueueFromStreamId(streamId, undefined), 'addSubscriber', args, + amqper.callRpc(getErizoQueue(streamId, undefined), 'addSubscriber', args, { callback: (data) => { if (!publishers[streamId] && !subscribers[streamId]) { log.warn('message: addSubscriber rpc callback has arrived after ' + @@ -318,7 +319,7 @@ exports.RoomController = (spec) => { log.warn('message: addSubscriber ErizoJS timeout, ' + `clientId: ${clientId}, ` + `streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)}, ` + + `erizoId: ${getErizoQueue(streamId)}, ` + `retries: ${retries},`, logger.objectToLog(options.metadata)); that.addSubscriber(clientId, streamId, options, callback, retries); @@ -327,14 +328,12 @@ exports.RoomController = (spec) => { log.warn('message: addSubscriber ErizoJS timeout no retry, ' + `clientId: ${clientId}, ` + `streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)},`, + `erizoId: ${getErizoQueue(streamId)},`, logger.objectToLog(options.metadata)); callback('timeout'); return; } else if (data.type === 'initializing') { - if (subscribers[streamId].indexOf(clientId) === -1) { - subscribers[streamId].push(clientId); - } + subscribers[streamId].push(clientId); } log.info('message: addSubscriber finished, ' + `streamId: ${streamId}, ` + @@ -361,85 +360,6 @@ exports.RoomController = (spec) => { } }; - that.addMultipleSubscribers = (clientId, streamIds, options, callback, retries) => { - if (clientId === null) { - log.warn('message: addMultipleSubscribers null clientId, ' + - `streams: ${streamIds.length}, ` + - `clientId: ${clientId},`, - logger.objectToLog(options.metadata)); - callback('Error: null clientId'); - return; - } - - if (retries === undefined) { retries = 0; } - - streamIds = streamIds.filter(streamId => - publishers[streamId] !== undefined && - subscribers[streamId].indexOf(clientId) === -1); - - if (streamIds.length === 0) { - return; - } - - const erizoIds = Array.from(new Set(streamIds.map(streamId => - getErizoQueueFromStreamId(streamId)))); - - erizoIds.forEach((erizoId) => { - const streamIdsInErizo = streamIds.filter(streamId => - getErizoQueueFromStreamId(streamId) === erizoId); - const args = [erizoControllerId, clientId, streamIdsInErizo, options]; - log.info('message: addMultipleSubscribers, ' + - `streams: ${streamIdsInErizo}, ` + - `clientId: ${clientId},`, - logger.objectToLog(options), - logger.objectToLog(options.metadata)); - - amqper.callRpc(erizoId, 'addMultipleSubscribers', args, - { callback: (data) => { - if (data === 'timeout') { - if (retries < MAX_ERIZOJS_RETRIES) { - retries += 1; - log.warn('message: addMultipleSubscribers ErizoJS timeout, ' + - `clientId: ${clientId}, ` + - `streams: ${streamIdsInErizo}, ` + - `erizoId: ${erizoId}, ` + - `retries: ${retries},`, - logger.objectToLog(options.metadata)); - that.addMultipleSubscribers(clientId, streamIdsInErizo, options, callback, retries); - return; - } - log.warn('message: addMultipleSubscribers ErizoJS timeout no retry, ' + - `clientId: ${clientId}, ` + - `streams: ${streamIdsInErizo.length}, ` + - `erizoId: ${erizoId},`, - logger.objectToLog(options.metadata)); - callback('timeout'); - return; - } else if (data.type === 'multiple-initializing') { - if (data.streamIds) { - data.streamIds.forEach((streamId) => { - if (subscribers[streamId].indexOf(clientId) === -1) { - subscribers[streamId].push(clientId); - } - }); - } else if (data.streamId) { - if (subscribers[data.streamId].indexOf(clientId) === -1) { - subscribers[data.streamId].push(clientId); - } - } - } - log.info('message: addMultipleSubscribers finished, ' + - `streams: ${streamIdsInErizo}, ` + - `clientId: ${clientId},`, - logger.objectToLog(options), - logger.objectToLog(options.metadata)); - data.erizoId = publishers[streamIdsInErizo[0]]; - callback(data); - }, - }); - }); - }; - /* * Removes a publisher from the room. This also deletes the associated OneToManyProcessor. */ @@ -447,10 +367,10 @@ exports.RoomController = (spec) => { if (subscribers[streamId] !== undefined && publishers[streamId] !== undefined) { log.info('message: removePublisher, ' + `streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)}`); + `erizoId: ${getErizoQueue(streamId)}`); const args = [clientId, streamId]; - amqper.callRpc(getErizoQueueFromStreamId(streamId), 'removePublisher', args, { + amqper.callRpc(getErizoQueue(streamId), 'removePublisher', args, { callback: () => { const erizo = erizos.findById(publishers[streamId]); @@ -460,7 +380,7 @@ exports.RoomController = (spec) => { } else { log.warn('message: removePublisher was already removed, ' + `streamId: ${streamId}, ` + - `erizoId: ${getErizoQueueFromStreamId(streamId)}`); + `erizoId: ${getErizoQueue(streamId)}`); } delete subscribers[streamId]; @@ -500,7 +420,7 @@ exports.RoomController = (spec) => { `streamId: ${streamId}`); const args = [subscriberId, streamId]; - amqper.callRpc(getErizoQueueFromStreamId(streamId), 'removeSubscriber', args, { + amqper.callRpc(getErizoQueue(streamId), 'removeSubscriber', args, { callback: (message) => { log.info('message: removeSubscriber finished, ' + `response: ${message}, ` + @@ -518,44 +438,6 @@ exports.RoomController = (spec) => { } }; - /* - * Removes a subscriber from the room. - * This also removes it from the associated OneToManyProcessor. - */ - that.removeMultipleSubscribers = (subscriberId, streamIds, callback) => { - streamIds = streamIds.filter(streamId => - subscribers[streamId] !== undefined && - subscribers[streamId].indexOf(subscriberId) !== -1); - - if (streamIds.length === 0) { - return; - } - - const erizoIds = Array.from(new Set(streamIds.map(streamId => - getErizoQueueFromStreamId(streamId)))); - - erizoIds.forEach((erizoId) => { - const streamIdsInErizo = streamIds.filter(streamId => - getErizoQueueFromStreamId(streamId) === erizoId); - log.info('message: removeMultipleSubscribers, ' + - `clientId: ${subscriberId}, ` + - `streamIds: ${streamIdsInErizo}`); - const args = [subscriberId, streamIdsInErizo]; - amqper.callRpc(erizoId, 'removeMultipleSubscribers', args, { - callback: (data) => { - log.info('message: removeMultipleSubscribers finished, ' + - `clientId: ${subscriberId}, ` + - `streamIds: ${streamIds}`); - data.streamIds.forEach((streamId) => { - const newIndex = subscribers[streamId].indexOf(subscriberId); - subscribers[streamId].splice(newIndex, 1); - }); - callback(data); - }, - }); - }); - }; - /* * Removes all the subscribers related with a client. */ @@ -574,7 +456,7 @@ exports.RoomController = (spec) => { `streamId: ${streamId}`); const args = [subscriberId, streamId]; - amqper.callRpc(getErizoQueueFromStreamId(streamId), 'removeSubscriber', args, undefined); + amqper.callRpc(getErizoQueue(streamId), 'removeSubscriber', args, undefined); // Remove tracks subscribers[streamId].splice(index, 1); @@ -585,9 +467,9 @@ exports.RoomController = (spec) => { that.getStreamStats = (streamId, callback) => { if (publishers[streamId]) { const args = [streamId]; - const theId = getErizoQueueFromStreamId(streamId); + const theId = getErizoQueue(streamId); log.debug('Get stats for publisher ', streamId, 'theId', theId); - amqper.callRpc(getErizoQueueFromStreamId(streamId), 'getStreamStats', args, { + amqper.callRpc(getErizoQueue(streamId), 'getStreamStats', args, { callback: (data) => { callback(data); } }); diff --git a/erizo_controller/erizoController/rpc/rpcPublic.js b/erizo_controller/erizoController/rpc/rpcPublic.js index 529fa332a3..de4247c4c4 100644 --- a/erizo_controller/erizoController/rpc/rpcPublic.js +++ b/erizo_controller/erizoController/rpc/rpcPublic.js @@ -30,10 +30,6 @@ exports.deleteUser = (args, callback) => { }); }; -exports.connectionStatusEvent = (clientId, connectionId, info, evt) => { - erizoController.connectionStatusEvent(clientId, connectionId, info, evt); -}; - exports.rovMessage = (args, callback) => { if (!replManager) { replManager = new RovReplManager(erizoController.getContext()); diff --git a/erizo_controller/erizoJS/erizoJSController.js b/erizo_controller/erizoJS/erizoJSController.js index 3943cce702..aaa566b736 100644 --- a/erizo_controller/erizoJS/erizoJSController.js +++ b/erizo_controller/erizoJS/erizoJSController.js @@ -65,6 +65,16 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { } }; + const getOrCreateClient = (clientId, singlePC = false) => { + log.debug(`getOrCreateClient with id ${clientId}`); + let client = clients.get(clientId); + if (client === undefined) { + client = new Client(clientId, threadPool, ioThreadPool, !!singlePC); + clients.set(clientId, client); + } + return client; + }; + const onAdaptSchemeNotify = (callbackRpc, type, message) => { callbackRpc(type, message); }; @@ -77,28 +87,24 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { timestamp: timeStamp.getTime() }); }; - const onConnectionStatusEvent = (erizoControllerId, clientId, - connectionId, connectionEvent, newStatus) => { - const rpcID = `erizoController_${erizoControllerId}`; - amqper.callRpc(rpcID, 'connectionStatusEvent', [clientId, connectionId, newStatus, connectionEvent]); - }; - - const getOrCreateClient = (erizoControllerId, clientId, singlePC = false) => { - let client = clients.get(clientId); - if (client === undefined) { - client = new Client(erizoControllerId, clientId, threadPool, ioThreadPool, !!singlePC); - client.on('status_event', onConnectionStatusEvent.bind(this)); - clients.set(clientId, client); + const onConnectionStatusEvent = (callbackRpc, clientId, streamId, connectionEvent, newStatus) => { + if (newStatus && global.config.erizoController.report.connection_events) { + const timeStamp = new Date(); + amqper.broadcast('event', { pub: streamId, + subs: clientId, + type: 'connection_status', + status: newStatus, + timestamp: timeStamp.getTime() }); } - return client; + callbackRpc('callback', connectionEvent); }; - const closeNode = (node, sendOffer) => { + const closeNode = (node) => { const clientId = node.clientId; const connection = node.connection; log.debug(`message: closeNode, clientId: ${node.clientId}, streamId: ${node.streamId}`); - const closePromise = node.close(sendOffer); + const closePromise = node.close(); const client = clients.get(clientId); if (client === undefined) { @@ -110,7 +116,6 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { const remainingConnections = client.maybeCloseConnection(connection.id); if (remainingConnections === 0) { log.debug(`message: Removing empty client from list, clientId: ${client.id}`); - client.removeAllListeners(); clients.delete(client.id); } return closePromise; @@ -120,10 +125,10 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { replManager.processRpcMessage(args, callback); }; - that.addExternalInput = (erizoControllerId, streamId, url, callbackRpc) => { + that.addExternalInput = (streamId, url, callbackRpc) => { updateUptimeInfo(); if (publishers[streamId] === undefined) { - const client = getOrCreateClient(erizoControllerId, url); + const client = getOrCreateClient(url); publishers[streamId] = new ExternalInput(url, streamId, threadPool); const ei = publishers[streamId]; const answer = ei.init(); @@ -153,57 +158,18 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { } }; - that.processConnectionMessage = (erizoControllerId, clientId, connectionId, msg, - callbackRpc = () => {}) => { - log.info('message: Process Connection message, ' + - `clientId: ${clientId}, connectionId: ${connectionId}`); - let error; - const client = clients.get(clientId); - if (!client) { - log.warn('message: Process Connection message to unknown clientId, ' + - `clientId: ${clientId}, connectionId: ${connectionId}`); - error = 'client-not-found'; - } - - const connection = client.getConnection(connectionId); - if (!connection) { - log.warn('message: Process Connection message to unknown connectionId, ' + - `clientId: ${clientId}, connectionId: ${connectionId}`); - error = 'connection-not-found'; - } - - if (error) { - callbackRpc('callback', { error }); - return Promise.resolve(); - } - - return connection.onSignalingMessage(msg).then(() => { - callbackRpc('callback', {}); - }); - }; - - that.processStreamMessage = (erizoControllerId, clientId, streamId, msg) => { - log.info('message: Process Stream message, ' + - `clientId: ${clientId}, streamId: ${streamId}`); - - let node; - const publisher = publishers[streamId]; - if (!publisher) { - log.warn('message: Process Stream message stream not found, ' + - `clientId: ${clientId}, streamId: ${streamId}`); - return; - } - - if (publisher.cliendId === clientId) { - node = publisher; - } else if (publisher.hasSubscriber(clientId)) { - node = publisher.getSubscriber(clientId); - } else { - log.warn('message: Process Stream message stream not found, ' + - `clientId: ${clientId}, streamId: ${streamId}`); - return; + that.processSignaling = (clientId, streamId, msg) => { + log.info('message: Process Signaling message, ' + + `streamId: ${streamId}, clientId: ${clientId}`); + if (publishers[streamId] !== undefined) { + const publisher = publishers[streamId]; + if (publisher.hasSubscriber(clientId)) { + const subscriber = publisher.getSubscriber(clientId); + subscriber.onSignalingMessage(msg); + } else { + publisher.onSignalingMessage(msg); + } } - node.onStreamMessage(msg); }; /* @@ -211,11 +177,11 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { * and a new WebRtcConnection. This WebRtcConnection will be the publisher * of the OneToManyProcessor. */ - that.addPublisher = (erizoControllerId, clientId, streamId, options, callbackRpc) => { + that.addPublisher = (clientId, streamId, options, callbackRpc) => { updateUptimeInfo(); let publisher; log.info('addPublisher, clientId', clientId, 'streamId', streamId); - const client = getOrCreateClient(erizoControllerId, clientId, options.singlePC); + const client = getOrCreateClient(clientId, options.singlePC); if (publishers[streamId] === undefined) { // eslint-disable-next-line no-param-reassign @@ -233,28 +199,11 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { publisher.initMediaStream(); publisher.on('callback', onAdaptSchemeNotify.bind(this, callbackRpc)); publisher.on('periodic_stats', onPeriodicStats.bind(this, streamId, undefined)); - publisher.promise.then(() => { - connection.init(options.createOffer); - }); - connection.onInitialized.then(() => { - callbackRpc('callback', { type: 'initializing', connectionId: connection.id }); - }); - connection.onReady.then(() => { - callbackRpc('callback', { type: 'ready' }); - }); - connection.onStarted.then(() => { - callbackRpc('callback', { type: 'started' }); - }); - if (options.createOffer) { - let onEvent; - if (options.trickleIce) { - onEvent = connection.onInitialized; - } else { - onEvent = connection.onGathered; - } - onEvent.then(() => { - connection.sendOffer(); - }); + publisher.on('status_event', + onConnectionStatusEvent.bind(this, callbackRpc, clientId, streamId)); + const isNewConnection = connection.init(streamId); + if (options.singlePC && !isNewConnection) { + callbackRpc('callback', { type: 'initializing' }); } } else { publisher = publishers[streamId]; @@ -274,7 +223,7 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { * This WebRtcConnection will be added to the subscribers list of the * OneToManyProcessor. */ - that.addSubscriber = (erizoControllerId, clientId, streamId, options, callbackRpc) => { + that.addSubscriber = (clientId, streamId, options, callbackRpc) => { updateUptimeInfo(); const publisher = publishers[streamId]; if (publisher === undefined) { @@ -286,7 +235,7 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { return; } let subscriber = publisher.getSubscriber(clientId); - const client = getOrCreateClient(erizoControllerId, clientId, options.singlePC); + const client = getOrCreateClient(clientId, options.singlePC); if (subscriber !== undefined) { log.warn('message: Duplicated subscription will resubscribe, ' + `code: ${WARN_CONFLICT}, streamId: ${streamId}, ` + @@ -304,150 +253,12 @@ exports.ErizoJSController = (threadPool, ioThreadPool) => { subscriber.initMediaStream(); subscriber.on('callback', onAdaptSchemeNotify.bind(this, callbackRpc, 'callback')); subscriber.on('periodic_stats', onPeriodicStats.bind(this, clientId, streamId)); - - subscriber.promise.then(() => { - connection.init(options.createOffer); - }); - - connection.onInitialized.then(() => { - callbackRpc('callback', { type: 'initializing', connectionId: connection.id }); - }); - connection.onReady.then(() => { - callbackRpc('callback', { type: 'ready' }); - }); - connection.onStarted.then(() => { - callbackRpc('callback', { type: 'started' }); - }); - if (options.createOffer) { - let onEvent; - if (options.trickleIce) { - onEvent = connection.onInitialized; - } else { - onEvent = connection.onGathered; - } - onEvent.then(() => { - connection.sendOffer(); - }); - } - }; - - /* - * Adds multiple subscribers to the room. - */ - that.addMultipleSubscribers = (erizoControllerId, clientId, streamIds, options, callbackRpc) => { - if (!options.singlePC) { - log.warn('message: addMultipleSubscribers not compatible with no single PC, clientId:', clientId); - callbackRpc('callback', { type: 'error' }); - return; - } - - const knownPublishers = streamIds.map(streamId => publishers[streamId]) - .filter(pub => - pub !== undefined && - !pub.getSubscriber(clientId)); - if (knownPublishers.length === 0) { - log.warn('message: addMultipleSubscribers to unknown publisher, ' + - `code: ${WARN_NOT_FOUND}, streamIds: ${streamIds}, ` + - `clientId: ${clientId}`, - logger.objectToLog(options.metadata)); - callbackRpc('callback', { type: 'error' }); - return; - } - - log.debug('message: addMultipleSubscribers to publishers, ' + - `streamIds: ${knownPublishers}, ` + - `clientId: ${clientId}`, - logger.objectToLog(options.metadata)); - - const client = getOrCreateClient(erizoControllerId, clientId, options.singlePC); - // eslint-disable-next-line no-param-reassign - options.publicIP = that.publicIP; - // eslint-disable-next-line no-param-reassign - options.privateRegexp = that.privateRegexp; - const connection = client.getOrCreateConnection(options); - const promises = []; - knownPublishers.forEach((publisher) => { - const streamId = publisher.streamId; - // eslint-disable-next-line no-param-reassign - options.label = publisher.label; - const subscriber = publisher.addSubscriber(clientId, connection, options); - subscriber.initMediaStream(true); - subscriber.copySdpInfoFromPublisher(); - promises.push(subscriber.promise); - subscriber.on('callback', onAdaptSchemeNotify.bind(this, callbackRpc, 'callback')); - subscriber.on('periodic_stats', onPeriodicStats.bind(this, clientId, streamId)); - }); - - const knownStreamIds = knownPublishers.map(pub => pub.streamId); - - const constraints = { - audio: true, - video: true, - bundle: true, - }; - - connection.init(constraints); - promises.push(connection.createOfferPromise); - Promise.all(promises) - .then(() => { - log.debug('message: autoSubscription waiting for gathering event', connection.alreadyGathered, connection.onGathered); - return connection.onGathered; - }) - .then(() => { - callbackRpc('callback', { type: 'multiple-initializing', connectionId: connection.id, streamIds: knownStreamIds, context: 'auto-streams-subscription', options }); - connection.sendOffer(); - }); - }; - - /* - * Removes multiple subscribers from the room. - */ - that.removeMultipleSubscribers = (clientId, streamIds, callbackRpc) => { - const knownPublishers = streamIds.map(streamId => publishers[streamId]) - .filter(pub => - pub !== undefined && - pub.getSubscriber(clientId)); - if (knownPublishers.length === 0) { - log.warn('message: removeMultipleSubscribers from unknown publisher, ' + - `code: ${WARN_NOT_FOUND}, streamIds: ${streamIds}, ` + - `clientId: ${clientId}`); - callbackRpc('callback', { type: 'error' }); - return; - } - - log.debug('message: removeMultipleSubscribers from publishers, ' + - `streamIds: ${knownPublishers}, ` + - `clientId: ${clientId}`); - - const client = clients.get(clientId); - if (!client) { - callbackRpc('callback', { type: 'error' }); + subscriber.on('status_event', + onConnectionStatusEvent.bind(this, callbackRpc, clientId, streamId)); + const isNewConnection = connection.init(subscriber.erizoStreamId); + if (options.singlePC && !isNewConnection) { + callbackRpc('callback', { type: 'initializing' }); } - - let connection; - - const promises = []; - knownPublishers.forEach((publisher) => { - if (publisher && publisher.hasSubscriber(clientId)) { - const subscriber = publisher.getSubscriber(clientId); - connection = subscriber.connection; - promises.push(closeNode(subscriber, false)); - publisher.removeSubscriber(clientId); - } - }); - - const knownStreamIds = knownPublishers.map(pub => pub.streamId); - - Promise.all(promises) - .then(() => connection.onGathered) - .then(() => { - callbackRpc('callback', { - type: 'multiple-removal', - connectionId: connection.id, - streamIds: knownStreamIds, - context: 'auto-streams-unsubscription' }); - connection.sendOffer(); - }); }; /* diff --git a/erizo_controller/erizoJS/models/Client.js b/erizo_controller/erizoJS/models/Client.js index d05d8547e6..4786ecda2e 100644 --- a/erizo_controller/erizoJS/models/Client.js +++ b/erizo_controller/erizoJS/models/Client.js @@ -1,17 +1,14 @@ const Connection = require('./Connection').Connection; const logger = require('./../../common/logger').logger; -const EventEmitter = require('events').EventEmitter; const log = logger.getLogger('Client'); -class Client extends EventEmitter { +class Client { - constructor(erizoControllerId, id, threadPool, ioThreadPool, singlePc = false) { - super(); + constructor(id, threadPool, ioThreadPool, singlePc = false) { log.debug(`Constructor Client ${id}`); this.id = id; - this.erizoControllerId = erizoControllerId; this.connections = new Map(); this.threadPool = threadPool; this.ioThreadPool = ioThreadPool; @@ -34,9 +31,7 @@ class Client extends EventEmitter { log.info(`message: getOrCreateConnection, clientId: ${this.id}, singlePC: ${this.singlePc}`); if (!this.singlePc || !connection) { const id = this._getNewConnectionClientId(); - connection = new Connection(this.erizoControllerId, id, this.threadPool, - this.ioThreadPool, this.id, options); - connection.on('status_event', this.emit.bind(this, 'status_event')); + connection = new Connection(id, this.threadPool, this.ioThreadPool, options); this.addConnection(connection); } return connection; diff --git a/erizo_controller/erizoJS/models/Connection.js b/erizo_controller/erizoJS/models/Connection.js index 004dae9012..9713a519eb 100644 --- a/erizo_controller/erizoJS/models/Connection.js +++ b/erizo_controller/erizoJS/models/Connection.js @@ -6,7 +6,6 @@ const events = require('events'); const addon = require('./../../../erizoAPI/build/Release/addon'); const logger = require('./../../common/logger').logger; const SessionDescription = require('./SessionDescription'); -const SemanticSdp = require('./../../common/semanticSdp/SemanticSdp'); const Helpers = require('./Helpers'); const log = logger.getLogger('Connection'); @@ -23,15 +22,12 @@ const CONN_FAILED = 500; const WARN_BAD_CONNECTION = 502; const RESEND_LAST_ANSWER_RETRY_TIMEOUT = 50; -const RESEND_LAST_ANSWER_MAX_RETRIES = 10; class Connection extends events.EventEmitter { - constructor(erizoControllerId, id, threadPool, ioThreadPool, clientId, options = {}) { + constructor(id, threadPool, ioThreadPool, options = {}) { super(); log.info(`message: constructor, id: ${id}`); this.id = id; - this.erizoControllerId = erizoControllerId; - this.clientId = clientId; this.threadPool = threadPool; this.ioThreadPool = ioThreadPool; this.mediaConfiguration = 'default'; @@ -42,22 +38,8 @@ class Connection extends events.EventEmitter { this.options = options; this.trickleIce = options.trickleIce || false; this.metadata = this.options.metadata || {}; - this.onGathered = new Promise((resolve, reject) => { - this._gatheredResolveFunction = resolve; - this._gatheredRejectFunction = reject; - }); - this.onInitialized = new Promise((resolve, reject) => { - this._initializeResolveFunction = resolve; - this._initializeRejectFunction = reject; - }); - this.onStarted = new Promise((resolve, reject) => { - this._startResolveFunction = resolve; - this._startRejectFunction = reject; - }); - this.onReady = new Promise((resolve, reject) => { - this._readyResolveFunction = resolve; - this._readyRejectFunction = reject; - }); + this.isProcessingRemoteSdp = false; + this.ready = false; } static _getMediaConfiguration(mediaConfiguration = 'default') { @@ -123,43 +105,22 @@ class Connection extends events.EventEmitter { this.emit('media_stream_event', streamEvent); } - _onStatusEvent(info, evt) { - this.emit('status_event', this.erizoControllerId, this.clientId, this.id, info, evt); - } - - createAnswer() { - return { type: 'answer', sdp: this.getLocalSdp() }; - } - - createOffer() { - return { type: 'offer', sdp: this.getLocalSdp() }; - } - - getLocalSdp() { + _maybeSendAnswer(evt, streamId, forceOffer = false) { + if (this.isProcessingRemoteSdp) { + return; + } + if (!this.alreadyGathered && !this.trickleIce) { + return; + } this.wrtc.localDescription = new SessionDescription(this.wrtc.getLocalDescription()); const sdp = this.wrtc.localDescription.getSdp(this.sessionVersion); this.sessionVersion += 1; let message = sdp.toString(); message = message.replace(this.options.privateRegexp, this.options.publicIP); - return message; - } - - sendOffer() { - if (!this.alreadyGathered && !this.trickleIce) { - return; - } - const info = this.createOffer(); - log.debug(`message: sendAnswer sending event, type: ${info.type}, sessionVersion: ${this.sessionVersion}`); - this._onStatusEvent(info, CONN_SDP); - } - sendAnswer(evt = CONN_SDP_PROCESSED, forceOffer = false) { - if (!this.alreadyGathered && !this.trickleIce) { - return; - } - const info = this.options.createOffer || forceOffer ? this.createOffer() : this.createAnswer(); - log.debug(`message: sendAnswer sending event, type: ${info.type}, sessionVersion: ${this.sessionVersion}`); - this._onStatusEvent(info, evt); + const info = { type: this.options.createOffer || forceOffer ? 'offer' : 'answer', sdp: message }; + log.debug(`message: _maybeSendAnswer sending event, type: ${info.type}, streamId: ${streamId}`); + this.emit('status_event', info, evt, streamId); } _resendLastAnswer(evt, streamId, label, forceOffer = false, removeStream = false) { @@ -180,150 +141,108 @@ class Connection extends events.EventEmitter { const info = { type: this.options.createOffer || forceOffer ? 'offer' : 'answer', sdp: message }; log.debug(`message: _resendLastAnswer sending event, type: ${info.type}, streamId: ${streamId}`); - this._onStatusEvent(info, evt); + this.emit('status_event', info, evt, streamId); return Promise.resolve(); } - init(createOffer = this.options.createOffer) { + init(newStreamId) { if (this.initialized) { return false; } + const firstStreamId = newStreamId; this.initialized = true; log.debug(`message: Init Connection, connectionId: ${this.id} `, logger.objectToLog(this.options)); this.sessionVersion = 0; - this.wrtc.init((newStatus, mess) => { + this.wrtc.init((newStatus, mess, streamId) => { log.info('message: WebRtcConnection status update, ' + `id: ${this.id}, status: ${newStatus}`, logger.objectToLog(this.metadata)); switch (newStatus) { case CONN_INITIAL: - this._startResolveFunction(); + this.emit('status_event', { type: 'started' }, newStatus); break; case CONN_SDP_PROCESSED: + this.isProcessingRemoteSdp = false; + this._maybeSendAnswer(newStatus, streamId); + break; + case CONN_SDP: + this._maybeSendAnswer(newStatus, streamId); break; case CONN_GATHERED: this.alreadyGathered = true; - this._gatheredResolveFunction(); + this._maybeSendAnswer(newStatus, firstStreamId); break; case CONN_CANDIDATE: // eslint-disable-next-line no-param-reassign mess = mess.replace(this.options.privateRegexp, this.options.publicIP); - this._onStatusEvent({ type: 'candidate', candidate: mess }, newStatus); + this.emit('status_event', { type: 'candidate', candidate: mess }, newStatus); break; case CONN_FAILED: log.warn(`message: failed the ICE process, code: ${WARN_BAD_CONNECTION},` + `id: ${this.id}`); - this._onStatusEvent({ type: 'failed', sdp: mess }, newStatus); + this.emit('status_event', { type: 'failed', sdp: mess }, newStatus); break; case CONN_READY: log.debug(`message: connection ready, id: ${this.id} status: ${newStatus}`); - this._readyResolveFunction(); - this._onStatusEvent({ type: 'ready' }, newStatus); + this.ready = true; + this.emit('status_event', { type: 'ready' }, newStatus); break; default: log.error(`message: unknown webrtc status ${newStatus}`); } }); - if (createOffer) { + if (this.options.createOffer) { log.debug('message: create offer requested, id:', this.id); - const audioEnabled = createOffer.audio; - const videoEnabled = createOffer.video; - const bundle = createOffer.bundle; - this.createOfferPromise = this.wrtc.createOffer(videoEnabled, audioEnabled, bundle); + const audioEnabled = this.options.createOffer.audio; + const videoEnabled = this.options.createOffer.video; + const bundle = this.options.createOffer.bundle; + this.wrtc.createOffer(videoEnabled, audioEnabled, bundle); } - this._initializeResolveFunction(); + this.emit('status_event', { type: 'initializing' }); return true; } addMediaStream(id, options, isPublisher) { - let promise = Promise.resolve(); log.info(`message: addMediaStream, connectionId: ${this.id}, mediaStreamId: ${id}`); if (this.mediaStreams.get(id) === undefined) { const mediaStream = this._createMediaStream(id, options, isPublisher); - promise = this.wrtc.addMediaStream(mediaStream); + this.wrtc.addMediaStream(mediaStream); this.mediaStreams.set(id, mediaStream); } - return promise; } - removeMediaStream(id, sendOffer = true) { - let promise = Promise.resolve(); + removeMediaStream(id) { if (this.mediaStreams.get(id) !== undefined) { const label = this.mediaStreams.get(id).label; - promise = this.wrtc.removeMediaStream(id); + this.wrtc.removeMediaStream(id); this.mediaStreams.get(id).close(); this.mediaStreams.delete(id); return Helpers.retryWithPromise( - this._resendLastAnswer.bind(this, CONN_SDP, id, label, sendOffer, true), - RESEND_LAST_ANSWER_RETRY_TIMEOUT, RESEND_LAST_ANSWER_MAX_RETRIES); + this._resendLastAnswer.bind(this, CONN_SDP, id, label, true, true), + RESEND_LAST_ANSWER_RETRY_TIMEOUT); } log.error(`message: Trying to remove mediaStream not found, id: ${id}`); - return promise; + return Promise.resolve(); } - setRemoteDescription(sdp) { + setRemoteDescription(sdp, streamId) { + this.isProcessingRemoteSdp = true; this.remoteDescription = new SessionDescription(sdp, this.mediaConfiguration); - return this.wrtc.setRemoteDescription(this.remoteDescription.connectionDescription); - } - - processOffer(sdp) { - const sdpInfo = SemanticSdp.SDPInfo.processString(sdp); - return this.setRemoteDescription(sdpInfo); - } - - processAnswer(sdp) { - const sdpInfo = SemanticSdp.SDPInfo.processString(sdp); - return this.setRemoteDescription(sdpInfo); + this.wrtc.setRemoteDescription(this.remoteDescription.connectionDescription, streamId); } addRemoteCandidate(candidate) { this.wrtc.addRemoteCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate); } - onSignalingMessage(msg) { - if (msg.type === 'offer') { - let onEvent; - if (this.trickleIce) { - onEvent = this.onInitialized; - } else { - onEvent = this.onGathered; - } - return this.processOffer(msg.sdp) - .then(() => onEvent) - .then(() => { - this.sendAnswer(); - }).catch(() => { - log.error('message: Error processing offer/answer in connection, connectionId:', this.id); - }); - } else if (msg.type === 'offer-noanswer') { - return this.processOffer(msg.sdp).catch(() => { - log.error('message: Error processing offer/noanswer in connection, connectionId:', this.id); - }); - } else if (msg.type === 'answer') { - return this.processAnswer(msg.sdp).catch(() => { - log.error('message: Error processing answer in connection, connectionId:', this.id); - }); - } else if (msg.type === 'candidate') { - this.addRemoteCandidate(msg.candidate); - return Promise.resolve(); - } else if (msg.type === 'updatestream') { - if (msg.sdp) { - return this.processOffer(msg.sdp).catch(() => { - log.error('message: Error processing updatestream in connection, connectionId:', this.id); - }); - } - } - return Promise.resolve(); - } - getMediaStream(id) { return this.mediaStreams.get(id); } @@ -342,7 +261,6 @@ class Connection extends events.EventEmitter { mediaStream.close(); }); this.wrtc.close(); - this.removeAllListeners(); delete this.mediaStreams; delete this.wrtc; } diff --git a/erizo_controller/erizoJS/models/Helpers.js b/erizo_controller/erizoJS/models/Helpers.js index fb0b5c2d87..4fb10efaba 100644 --- a/erizo_controller/erizoJS/models/Helpers.js +++ b/erizo_controller/erizoJS/models/Helpers.js @@ -15,17 +15,13 @@ exports.getMediaConfiguration = (mediaConfiguration = 'default') => { exports.getErizoStreamId = (clientId, streamId) => `${clientId}_${streamId}`; -exports.retryWithPromise = (fn, timeout, retries = 3) => +exports.retryWithPromise = (fn, timeout) => new Promise((resolve, reject) => { - if (retries < 0) { - reject('max-retries'); - return; - } fn().then(resolve) .catch((error) => { if (error === 'retry') { setTimeout(() => { - exports.retryWithPromise(fn, timeout, retries - 1).then(resolve, reject); + exports.retryWithPromise(fn, timeout).then(resolve, reject); }, timeout); } else { // For now we're resolving the promise instead of rejecting it since diff --git a/erizo_controller/erizoJS/models/Node.js b/erizo_controller/erizoJS/models/Node.js index 6833d3bd30..0e18e6a181 100644 --- a/erizo_controller/erizoJS/models/Node.js +++ b/erizo_controller/erizoJS/models/Node.js @@ -40,12 +40,11 @@ class Node extends EventEmitter { this.emit(type, message); } - initMediaStream(force = false) { + initMediaStream() { if (!this.mediaStream) { return; } const mediaStream = this.mediaStream; - mediaStream.init(force); if (mediaStream.minVideoBW) { let monitorMinVideoBw = {}; if (mediaStream.scheme) { diff --git a/erizo_controller/erizoJS/models/Publisher.js b/erizo_controller/erizoJS/models/Publisher.js index 749e629cf3..8b3cba900b 100644 --- a/erizo_controller/erizoJS/models/Publisher.js +++ b/erizo_controller/erizoJS/models/Publisher.js @@ -8,6 +8,7 @@ const Subscriber = require('./Subscriber').Subscriber; const addon = require('./../../../erizoAPI/build/Release/addon'); const logger = require('./../../common/logger').logger; const Helpers = require('./Helpers'); +const SemanticSdp = require('./../../common/semanticSdp/SemanticSdp'); // Logger const log = logger.getLogger('Publisher'); @@ -150,8 +151,28 @@ class Source extends NodeClass { this.setSlideShow(message.enabled, clientId); } - onStreamMessage(msg) { - if (msg.type === 'updatestream') { + onSignalingMessage(msg) { + const connection = this.connection; + if (!connection) { + return; + } + if (msg.type === 'offer') { + const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); + connection.setRemoteDescription(sdp, this.streamId); + if (msg.config && msg.config.maxVideoBW) { + this.mediaStream.setMaxVideoBW(msg.config.maxVideoBW); + } + this.disableDefaultHandlers(); + } else if (msg.type === 'candidate') { + connection.addRemoteCandidate(msg.candidate); + } else if (msg.type === 'updatestream') { + if (msg.sdp) { + const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); + connection.setRemoteDescription(sdp, this.streamId); + if (this.mediaStream) { + this.mediaStream.setMaxVideoBW(); + } + } if (msg.config) { if (msg.config.minVideoBW) { log.debug('message: updating minVideoBW for publisher,' + @@ -376,11 +397,15 @@ class Publisher extends Source { this.connection = connection; this.connection.mediaConfiguration = options.mediaConfiguration; - this.promise = this.connection.addMediaStream(streamId, options, true); + this.connection.addMediaStream(streamId, options, true); + this._connectionListener = this._emitStatusEvent.bind(this); + connection.on('status_event', this._connectionListener); this.mediaStream = this.connection.getMediaStream(streamId); this.minVideoBW = options.minVideoBW; this.scheme = options.scheme; + this.ready = false; + this.connectionReady = connection.ready; this.mediaStream.setAudioReceiver(this.muxer); this.mediaStream.setVideoReceiver(this.muxer); @@ -390,8 +415,37 @@ class Publisher extends Source { this.muteStream({ video: muteVideo, audio: muteAudio }); } + _emitStatusEvent(evt, status, streamId) { + log.debug('onStatusEvent in publisher', evt.type, this.streamId, streamId); + const isGlobalStatus = streamId === undefined || streamId === ''; + const isNotMe = !isGlobalStatus && (`${streamId}`) !== (`${this.streamId}`); + if (isNotMe) { + log.debug('onStatusEvent dropped in publisher', streamId, this.streamId); + return; + } + if (evt.type === 'ready') { + if (this.connectionReady) { + return; + } + this.connectionReady = true; + if (!(this.ready && this.connectionReady)) { + log.debug('ready event dropped in publisher', this.ready, this.connectionReady); + return; + } + } + + if (evt.type === 'answer' || evt.type === 'offer') { + if (!this.ready && this.connectionReady) { + this.emit('status_event', { type: 'ready' }); + } + this.ready = true; + } + this.emit('status_event', evt, status); + } + close() { const removeMediaStreamPromise = this.connection.removeMediaStream(this.mediaStream.id); + this.connection.removeListener('status_event', this._connectionListener); if (this.mediaStream.monitorInterval) { clearInterval(this.mediaStream.monitorInterval); } diff --git a/erizo_controller/erizoJS/models/Subscriber.js b/erizo_controller/erizoJS/models/Subscriber.js index 7bbcab2ace..4befdfa7d7 100644 --- a/erizo_controller/erizoJS/models/Subscriber.js +++ b/erizo_controller/erizoJS/models/Subscriber.js @@ -3,6 +3,7 @@ const NodeClass = require('./Node').Node; const logger = require('./../../common/logger').logger; +const SemanticSdp = require('./../../common/semanticSdp/SemanticSdp'); // Logger const log = logger.getLogger('Subscriber'); @@ -12,10 +13,49 @@ class Subscriber extends NodeClass { super(clientId, streamId, options); this.connection = connection; this.connection.mediaConfiguration = options.mediaConfiguration; - this.promise = this.connection.addMediaStream(this.erizoStreamId, options, false); + this.connection.addMediaStream(this.erizoStreamId, options, false); + this._connectionListener = this._emitStatusEvent.bind(this); this._mediaStreamListener = this._onMediaStreamEvent.bind(this); + connection.on('status_event', this._connectionListener); connection.on('media_stream_event', this._mediaStreamListener); - connection.onReady.then(() => { + this.mediaStream = connection.getMediaStream(this.erizoStreamId); + this.publisher = publisher; + this.ready = false; + this.connectionReady = connection.ready; + } + + _emitStatusEvent(evt, status, streamId) { + const isGlobalStatus = streamId === undefined || streamId === ''; + const isNotMe = !isGlobalStatus && (`${streamId}`) !== (`${this.erizoStreamId}`); + if (isNotMe) { + log.debug('onStatusEvent dropped in publisher', streamId, this.erizoStreamId); + return; + } + if (evt.type === 'ready') { + if (this.connectionReady) { + return; + } + this.connectionReady = true; + if (!(this.ready && this.connectionReady)) { + log.debug('ready event dropped in publisher', this.ready, this.connectionReady); + return; + } + } + + if (evt.type === 'answer' || evt.type === 'offer') { + if (!this.ready && this.connectionReady) { + const readyEvent = { type: 'ready' }; + this._onConnectionStatusEvent(readyEvent); + this.emit('status_event', readyEvent); + } + this.ready = true; + } + this._onConnectionStatusEvent(evt); + this.emit('status_event', evt, status); + } + + _onConnectionStatusEvent(connectionEvent) { + if (connectionEvent.type === 'ready') { if (this.clientId && this.options.browser === 'bowser') { this.publisher.requestVideoKeyFrame(); } @@ -23,28 +63,13 @@ class Subscriber extends NodeClass { Number.isSafeInteger(this.options.slideShowMode)) { this.publisher.setSlideShow(this.options.slideShowMode, this.clientId); } - }); - this.mediaStream = connection.getMediaStream(this.erizoStreamId); - this.publisher = publisher; - } - - copySdpInfoFromPublisher() { - if (this.publisher && this.publisher.connection && this.publisher.connection.wrtc && - this.publisher.connection.wrtc.localDescription && this.connection && this.connection.wrtc) { - const publisherSdp = this.publisher.connection.wrtc.localDescription.connectionDescription; - this.connection.wrtc.copySdpToLocalDescription(publisherSdp); } } _onMediaStreamEvent(mediaStreamEvent) { - if (mediaStreamEvent.mediaStreamId !== this.streamId) { - return; - } if (mediaStreamEvent.type === 'slideshow_fallback_update') { this.publisher.setSlideShow(mediaStreamEvent.message !== 'false', this.clientId, true); - } else if (mediaStreamEvent.type === 'ready') { - this.disableDefaultHandlers(); } } @@ -58,8 +83,23 @@ class Subscriber extends NodeClass { }); } - onStreamMessage(msg) { - if (msg.type === 'updatestream') { + onSignalingMessage(msg, publisher) { + const connection = this.connection; + + if (msg.type === 'offer') { + const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); + connection.setRemoteDescription(sdp, this.erizoStreamId); + if (msg.config && msg.config.maxVideoBW) { + this.mediaStream.setMaxVideoBW(msg.config.maxVideoBW); + } + this.disableDefaultHandlers(); + } else if (msg.type === 'candidate') { + connection.addRemoteCandidate(msg.candidate); + } else if (msg.type === 'updatestream') { + if (msg.sdp) { + const sdp = SemanticSdp.SDPInfo.processString(msg.sdp); + connection.setRemoteDescription(sdp, this.erizoStreamId); + } if (msg.config) { if (msg.config.slideShowMode !== undefined) { this.publisher.setSlideShow(msg.config.slideShowMode, this.clientId); @@ -82,22 +122,27 @@ class Subscriber extends NodeClass { } } } else if (msg.type === 'control') { - this.publisher.processControlMessage(this.clientId, msg.action); + publisher.processControlMessage(this.clientId, msg.action); } } - close(sendOffer = true) { - log.debug(`msg: Closing subscriber, streamId:${this.streamId}`); - this.publisher = undefined; - let promise = Promise.resolve(); - if (this.connection) { - promise = this.connection.removeMediaStream(this.mediaStream.id, sendOffer); - this.connection.removeListener('media_stream_event', this._mediaStreamListener); - } - if (this.mediaStream && this.mediaStream.monitorInterval) { - clearInterval(this.mediaStream.monitorInterval); - } - return promise; + close() { + return new Promise((resolve) => { + log.debug(`msg: Closing subscriber, streamId:${this.streamId}`); + this.publisher = undefined; + if (this.mediaStream && this.mediaStream.monitorInterval) { + clearInterval(this.mediaStream.monitorInterval); + } + if (this.connection) { + this.connection.removeMediaStream(this.mediaStream.id).then(() => { + this.connection.removeListener('status_event', this._connectionListener); + this.connection.removeListener('media_stream_event', this._mediaStreamListener); + resolve(); + }); + return; + } + resolve(); + }); } } diff --git a/erizo_controller/test/erizoController/erizoController.js b/erizo_controller/test/erizoController/erizoController.js index 6eb1552503..2f9e43fbf3 100644 --- a/erizo_controller/test/erizoController/erizoController.js +++ b/erizo_controller/test/erizoController/erizoController.js @@ -91,8 +91,7 @@ describe('Erizo Controller / Erizo Controller', () => { // eslint-disable-next-line no-unused-vars let onReconnect; let onSendDataStream; - let onStreamMessageErizo; - let onStreamMessageP2P; + let onSignalingMessage; let onUpdateStreamAttributes; let onPublish; let onSubscribe; @@ -233,8 +232,7 @@ describe('Erizo Controller / Erizo Controller', () => { setTimeout(() => { onSendDataStream = mocks.socketInstance.on.withArgs('sendDataStream').args[0][1]; - onStreamMessageErizo = mocks.socketInstance.on.withArgs('streamMessage').args[0][1]; - onStreamMessageP2P = mocks.socketInstance.on.withArgs('streamMessageP2P').args[0][1]; + onSignalingMessage = mocks.socketInstance.on.withArgs('signaling_message').args[0][1]; onUpdateStreamAttributes = mocks.socketInstance.on .withArgs('updateStreamAttributes').args[0][1]; onPublish = mocks.socketInstance.on.withArgs('publish').args[0][1]; @@ -249,14 +247,11 @@ describe('Erizo Controller / Erizo Controller', () => { it('should listen to messages', () => { expect(mocks.socketInstance.on.withArgs('sendDataStream').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('streamMessage').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('streamMessageP2P').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('connectionMessage').callCount).to.equal(1); + expect(mocks.socketInstance.on.withArgs('signaling_message').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('updateStreamAttributes').callCount) .to.equal(1); expect(mocks.socketInstance.on.withArgs('publish').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('subscribe').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('autoSubscribe').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('startRecorder').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('stopRecorder').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('unpublish').callCount).to.equal(1); @@ -342,8 +337,7 @@ describe('Erizo Controller / Erizo Controller', () => { setTimeout(() => { onSendDataStream = mocks.socketInstance.on.withArgs('sendDataStream').args[0][1]; - onStreamMessageErizo = mocks.socketInstance.on.withArgs('streamMessage').args[0][1]; - onStreamMessageP2P = mocks.socketInstance.on.withArgs('streamMessageP2P').args[0][1]; + onSignalingMessage = mocks.socketInstance.on.withArgs('signaling_message').args[0][1]; onUpdateStreamAttributes = mocks.socketInstance.on .withArgs('updateStreamAttributes').args[0][1]; onPublish = mocks.socketInstance.on.withArgs('publish').args[0][1]; @@ -358,14 +352,11 @@ describe('Erizo Controller / Erizo Controller', () => { it('should listen to messages', () => { expect(mocks.socketInstance.on.withArgs('sendDataStream').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('streamMessage').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('streamMessageP2P').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('connectionMessage').callCount).to.equal(1); + expect(mocks.socketInstance.on.withArgs('signaling_message').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('updateStreamAttributes').callCount) .to.equal(1); expect(mocks.socketInstance.on.withArgs('publish').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('autoSubscribe').callCount).to.equal(1); - expect(mocks.socketInstance.on.withArgs('unsubscribe').callCount).to.equal(1); + expect(mocks.socketInstance.on.withArgs('subscribe').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('startRecorder').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('stopRecorder').callCount).to.equal(1); expect(mocks.socketInstance.on.withArgs('unpublish').callCount).to.equal(1); @@ -515,140 +506,6 @@ describe('Erizo Controller / Erizo Controller', () => { .callCount).to.equal(1); }); - describe('Subscriber Connection', () => { - // eslint-disable-next-line no-unused-vars - let onSubscriberReconnect; - let onSubscriberAutoSubscribe; - let onSubscriberTokenCallback; - let onSubscriberToken; - let subscriberClient; - const arbitrarySubscriberSignature = 'c2lnbmF0dXJl'; // signature - const arbitrarySubscriberGoodToken = { - tokenId: 'tokenId2', - host: 'host', - signature: arbitrarySubscriberSignature, - }; - - beforeEach((done) => { - const onConnection = mocks.socketIoInstance.sockets.on.withArgs('connection').args[0][1]; - onConnection(mocks.socketInstance); - signatureMock.update.returns(signatureMock); - signatureMock.digest.returns('signature'); - onSubscriberTokenCallback = sinon.stub(); - onSubscriberToken = mocks.socketInstance.on.withArgs('token').args[1][1]; - onSubscriberToken({ token: arbitrarySubscriberGoodToken }, - onSubscriberTokenCallback); - - callback = amqperMock.callRpc - .withArgs('nuve', 'deleteToken', arbitrarySubscriberGoodToken.tokenId) - .args[0][3].callback; - - setTimeout(() => { - callback({ host: 'host', room: 'roomId', userName: 'user2' }); - setTimeout(() => { - onSubscriberAutoSubscribe = mocks.socketInstance.on - .withArgs('autoSubscribe').args[1][1]; - room.forEachClient((aClient) => { - if (aClient.token.userName === 'user2') { - subscriberClient = aClient; - } - }); - subscriberClient.user.permissions[Permission.SUBSCRIBE] = true; - done(); - }, 0); - }, 0); - }); - - describe('on AutoSubscription', () => { - let data; - const streams = []; - let streamId; - - beforeEach(() => { - subscriberClient.user.permissions = {}; - subscriberClient.user.permissions[Permission.SUBSCRIBE] = true; - subscriberClient.user.permissions[Permission.PUBLISH] = true; - - const aOptions = { - audio: true, - video: true, - screen: true, - data: true, - attributes: { type: 'publisher' } }; - const aSdp = ''; - const publishCallback = sinon.stub(); - - onPublish(aOptions, aSdp, publishCallback); - - streamId = publishCallback.args[0][0]; - - data = { - selectors: { '/attributes/type': 'publisher' }, - negativeSelectors: {}, - options: { audio: true, video: true }, - }; - streams.push(streamId); - }); - - it('should call callback', () => { - const subscribeCallback = sinon.stub(); - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(subscribeCallback.callCount).to.equal(1); - }); - - it('should fail if user is not authorized to subscribe', () => { - const subscribeCallback = sinon.stub(); - subscriberClient.user.permissions[Permission.SUBSCRIBE] = false; - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(subscribeCallback.withArgs(null, 'Unauthorized').callCount).to.equal(1); - }); - - it('should call RoomController if any stream meets selector', () => { - const subscribeCallback = sinon.stub(); - data.selectors = { '/attributes/type': 'publisher' }; - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(mocks.roomControllerInstance.addMultipleSubscribers.callCount) - .to.equal(1); - expect(mocks.roomControllerInstance.removeMultipleSubscribers.callCount) - .to.equal(0); - }); - - it('should call RoomController any time a stream meets selector', () => { - const subscribeCallback = sinon.stub(); - data.selectors = { '/attributes/type': 'publisher' }; - onSubscriberAutoSubscribe(data, subscribeCallback); - onSubscriberAutoSubscribe(data, subscribeCallback); - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(mocks.roomControllerInstance.addMultipleSubscribers.callCount) - .to.equal(3); - expect(mocks.roomControllerInstance.removeMultipleSubscribers.callCount) - .to.equal(0); - }); - - it('should not call RoomController if no stream meets a selector', () => { - const subscribeCallback = sinon.stub(); - data.selectors = { '/attributes/type': 'subscriber' }; - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(mocks.roomControllerInstance.addMultipleSubscribers.callCount) - .to.equal(0); - expect(mocks.roomControllerInstance.removeMultipleSubscribers.callCount) - .to.equal(1); - }); - - it('should call RoomController to remove multiple subscribers', () => { - const subscribeCallback = sinon.stub(); - data.selectors = { '/attributes/type': 'publisher' }; - onSubscriberAutoSubscribe(data, subscribeCallback); - data.selectors = { '/attributes/type': 'subscriber' }; - onSubscriberAutoSubscribe(data, subscribeCallback); - expect(mocks.roomControllerInstance.addMultipleSubscribers.callCount) - .to.equal(1); - expect(mocks.roomControllerInstance.removeMultipleSubscribers.callCount) - .to.equal(1); - }); - }); - }); - describe('on Subscribe', () => { let subscriberOptions; let subscriberSdp; @@ -754,6 +611,18 @@ describe('Erizo Controller / Erizo Controller', () => { expect(mocks.socketInstance.emit.withArgs('connection_failed').callCount) .to.equal(1); }); + + it('should send signaling event to the socket when receiving ready stat', + () => { + const signMes = { type: 'ready' }; + mocks.roomControllerInstance.addSubscriber.callsArgWith(3, signMes); + + onSubscribe(subscriberOptions, subscriberSdp, subscribeCallback); + + expect(mocks.socketInstance.emit.withArgs('signaling_message_erizo').callCount) + .to.equal(1); + }); + it('should send signaling event to the socket when receiving bandwidth alerts', () => { const signMes = { type: 'bandwidthAlert' }; @@ -761,7 +630,7 @@ describe('Erizo Controller / Erizo Controller', () => { onSubscribe(subscriberOptions, subscriberSdp, subscribeCallback); - expect(mocks.socketInstance.emit.withArgs('stream_message_erizo') + expect(mocks.socketInstance.emit.withArgs('signaling_message_erizo') .callCount).to.equal(1); }); @@ -825,20 +694,20 @@ describe('Erizo Controller / Erizo Controller', () => { room.p2p = true; arbitraryMessage.peerSocket = client.id; - onStreamMessageP2P(arbitraryMessage); + onSignalingMessage(arbitraryMessage); - expect(mocks.socketInstance.emit.withArgs('stream_message_p2p').callCount).to.equal(1); + expect(mocks.socketInstance.emit.withArgs('signaling_message_peer') + .callCount).to.equal(1); }); it('should send other signaling messages', () => { room.p2p = false; room.controller = mocks.roomControllerInstance; - onStreamMessageErizo(arbitraryMessage); + onSignalingMessage(arbitraryMessage); - expect(mocks.roomControllerInstance.processStreamMessageFromClient - .withArgs(arbitraryMessage.erizoId, client.id, - arbitraryMessage.streamId).callCount).to.equal(1); + expect(mocks.roomControllerInstance.processSignaling + .withArgs(client.id, arbitraryMessage.streamId).callCount).to.equal(1); }); }); diff --git a/erizo_controller/test/erizoController/roomController.js b/erizo_controller/test/erizoController/roomController.js index 4f11cf7596..69698b7ae6 100644 --- a/erizo_controller/test/erizoController/roomController.js +++ b/erizo_controller/test/erizoController/roomController.js @@ -43,13 +43,12 @@ describe('Erizo Controller / Room Controller', () => { expect(controller.addEventListener).not.to.be.undefined; expect(controller.addExternalInput).not.to.be.undefined; expect(controller.addExternalOutput).not.to.be.undefined; + expect(controller.processSignaling).not.to.be.undefined; expect(controller.addPublisher).not.to.be.undefined; expect(controller.addSubscriber).not.to.be.undefined; expect(controller.removePublisher).not.to.be.undefined; expect(controller.removeSubscriber).not.to.be.undefined; expect(controller.removeSubscriptions).not.to.be.undefined; - expect(controller.processConnectionMessageFromClient).not.to.be.undefined; - expect(controller.processStreamMessageFromClient).not.to.be.undefined; }); describe('External Input', () => { @@ -218,22 +217,13 @@ describe('Erizo Controller / Room Controller', () => { kArbitraryPubOptions, sinon.stub()); }); - it('should call Erizo\'s processConnectionMessage', () => { + it('should call Erizo\'s processSignaling', () => { const kArbitraryMsg = 'message'; - controller.processConnectionMessageFromClient(null, kArbitraryStreamId, kArbitraryMsg); + controller.processSignaling(null, kArbitraryStreamId, kArbitraryMsg); expect(amqperMock.callRpc.callCount).to.equal(2); - expect(amqperMock.callRpc.args[1][1]).to.equal('processConnectionMessage'); - }); - - it('should call Erizo\'s processStreamMessage', () => { - const kArbitraryMsg = 'message'; - - controller.processStreamMessageFromClient(null, kArbitraryStreamId, kArbitraryMsg); - - expect(amqperMock.callRpc.callCount).to.equal(2); - expect(amqperMock.callRpc.args[1][1]).to.equal('processStreamMessage'); + expect(amqperMock.callRpc.args[1][1]).to.equal('processSignaling'); }); }); @@ -262,7 +252,7 @@ describe('Erizo Controller / Room Controller', () => { expect(callback.callCount).to.equal(1); }); - it('should return error on Subscriber timeout', () => { + it('should return error on Publisher timeout', () => { const callback = sinon.stub(); controller.addSubscriber(kArbitraryClientId, kArbitraryStreamId, @@ -288,7 +278,7 @@ describe('Erizo Controller / Room Controller', () => { expect(callback.args[0][0]).to.equal('Error: null clientId'); }); - it('should fail if Subscriber does not exist', () => { + it('should fail if Publisher does not exist', () => { const kArbitraryUnknownId = 'unknownId'; const callback = sinon.stub(); @@ -334,147 +324,4 @@ describe('Erizo Controller / Room Controller', () => { }); }); }); - - describe('Add Multiple Subscribers', () => { - const kArbitraryClientId = 'id1'; - const kArbitraryOptions = {}; - const kArbitraryStreamId = 'id2'; - const kArbitraryPubOptions = {}; - - beforeEach(() => { - ecchInstanceMock.getErizoJS.callsArgWith(2, 'erizoId'); - controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, - kArbitraryPubOptions, sinon.stub()); - }); - - it('should call Erizo\'s addMultipleSubscribers', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - expect(amqperMock.callRpc.callCount).to.equal(2); - expect(amqperMock.callRpc.args[1][1]).to.equal('addMultipleSubscribers'); - - amqperMock.callRpc.args[1][3].callback({ type: 'initializing' }); - - expect(callback.callCount).to.equal(1); - }); - - it('should call Erizo\'s addMultipleSubscribers only once', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - - expect(amqperMock.callRpc.callCount).to.equal(2); - expect(amqperMock.callRpc.args[1][1]).to.equal('addMultipleSubscribers'); - - amqperMock.callRpc.args[1][3].callback({ type: 'multiple-initializing', streamIds: [kArbitraryStreamId] }); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - expect(amqperMock.callRpc.callCount).to.equal(2); - - expect(callback.callCount).to.equal(1); - }); - - it('should call Erizo\'s addMultipleSubscribers multiple times if previous failed', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - - expect(amqperMock.callRpc.callCount).to.equal(2); - expect(amqperMock.callRpc.args[1][1]).to.equal('addMultipleSubscribers'); - - amqperMock.callRpc.args[1][3].callback({ type: 'multiple-initializing', streamIds: [] }); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - expect(amqperMock.callRpc.callCount).to.equal(3); - expect(amqperMock.callRpc.args[2][1]).to.equal('addMultipleSubscribers'); - - expect(callback.callCount).to.equal(1); - }); - - it('should call Erizo\'s removeMultipleSubscribers', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - amqperMock.callRpc.args[1][3].callback({ type: 'multiple-initializing', streamIds: [kArbitraryStreamId] }); - - controller.removeMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], callback); - expect(amqperMock.callRpc.callCount).to.equal(3); - expect(amqperMock.callRpc.args[2][1]).to.equal('removeMultipleSubscribers'); - - expect(callback.callCount).to.equal(1); - }); - - it('should call Erizo\'s removeMultipleSubscribers only once', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, callback); - amqperMock.callRpc.args[1][3].callback({ type: 'multiple-initializing', streamIds: [kArbitraryStreamId] }); - - controller.removeMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], callback); - - expect(amqperMock.callRpc.callCount).to.equal(3); - expect(amqperMock.callRpc.args[2][1]).to.equal('removeMultipleSubscribers'); - amqperMock.callRpc.args[2][3].callback({ type: 'offer', streamIds: [kArbitraryStreamId] }); - - controller.removeMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], callback); - expect(amqperMock.callRpc.callCount).to.equal(3); - - expect(callback.callCount).to.equal(2); - }); - - it('should fail if clientId is null', () => { - const callback = sinon.stub(); - - controller.addMultipleSubscribers(null, [kArbitraryStreamId], kArbitraryOptions, callback); - expect(amqperMock.callRpc.callCount).to.equal(1); - expect(callback.args[0][0]).to.equal('Error: null clientId'); - }); - - it('should fail if Subscriber does not exist', () => { - const kArbitraryUnknownId = 'unknownId'; - const callback = sinon.stub(); - - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryUnknownId], - kArbitraryOptions, callback); - expect(amqperMock.callRpc.callCount).to.equal(1); - }); - - describe('And Remove', () => { - beforeEach(() => { - controller.addMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId], - kArbitraryOptions, sinon.stub()); - - amqperMock.callRpc.args[1][3].callback({ type: 'multiple-initializing', streamIds: [kArbitraryStreamId] }); - }); - - it('should call Erizo\'s removeMultipleSubscribers', () => { - controller.removeMultipleSubscribers(kArbitraryClientId, [kArbitraryStreamId]); - - expect(amqperMock.callRpc.callCount).to.equal(3); - expect(amqperMock.callRpc.args[2][1]).to.equal('removeMultipleSubscribers'); - }); - - it('should fail if clientId does not exist', () => { - const kArbitraryUnknownId = 'unknownId'; - controller.removeMultipleSubscribers(kArbitraryUnknownId, [kArbitraryStreamId]); - - expect(amqperMock.callRpc.callCount).to.equal(2); - }); - - it('should fail if subscriberId does not exist', () => { - const kArbitraryUnknownId = 'unknownId'; - controller.removeMultipleSubscribers(kArbitraryClientId, [kArbitraryUnknownId]); - - expect(amqperMock.callRpc.callCount).to.equal(2); - }); - }); - }); }); diff --git a/erizo_controller/test/erizoJS/erizoJSController.js b/erizo_controller/test/erizoJS/erizoJSController.js index 565c8a35c6..35a54ab503 100644 --- a/erizo_controller/test/erizoJS/erizoJSController.js +++ b/erizo_controller/test/erizoJS/erizoJSController.js @@ -56,8 +56,7 @@ describe('Erizo JS Controller', () => { expect(controller.addExternalInput).not.to.be.undefined; expect(controller.addExternalOutput).not.to.be.undefined; expect(controller.removeExternalOutput).not.to.be.undefined; - expect(controller.processConnectionMessage).not.to.be.undefined; - expect(controller.processStreamMessage).not.to.be.undefined; + expect(controller.processSignaling).not.to.be.undefined; expect(controller.addPublisher).not.to.be.undefined; expect(controller.addSubscriber).not.to.be.undefined; expect(controller.removePublisher).not.to.be.undefined; @@ -73,7 +72,6 @@ describe('Erizo JS Controller', () => { let callback; const kArbitraryStreamId = 'pubStreamId1'; const kArbitraryClientId = 'pubClientid1'; - const kArbitraryErizoControllerId = 'erizoControllerId1'; beforeEach(() => { callback = sinon.stub(); @@ -97,18 +95,15 @@ describe('Erizo JS Controller', () => { expect(process.exit.callCount).to.equal(0); }); it('should exit when the max active time is up and there is no new activity', () => { - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); clock.tick(kActiveUptimeLimitMs + kCheckUptimeIntervalMs); expect(process.exit.callCount).to.equal(1); }); it('should not exit when the max active time is up but there is new activity', () => { const kArbitraryTimeMargin = kMaxTimeSinceLastOperationMs - 1000; - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); clock.tick(kActiveUptimeLimitMs - kArbitraryTimeMargin); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); clock.tick(kArbitraryTimeMargin + kCheckUptimeIntervalMs); expect(process.exit.callCount).to.equal(0); }); @@ -116,7 +111,6 @@ describe('Erizo JS Controller', () => { describe('Add External Input', () => { let callback; - const kArbitraryErizoControllerId = 'erizoControllerId1'; const kArbitraryStreamId = 'streamId1'; const kArbitraryUrl = 'url1'; @@ -126,8 +120,7 @@ describe('Erizo JS Controller', () => { it('should succeed creating OneToManyProcessor and ExternalInput', () => { mocks.ExternalInput.init.returns(1); - controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + controller.addExternalInput(kArbitraryStreamId, kArbitraryUrl, callback); expect(erizoApiMock.OneToManyProcessor.callCount).to.equal(1); expect(erizoApiMock.ExternalInput.args[0][0]).to.equal(kArbitraryUrl); @@ -142,8 +135,7 @@ describe('Erizo JS Controller', () => { it('should fail if ExternalInput is not intialized', () => { mocks.ExternalInput.init.returns(-1); - controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + controller.addExternalInput(kArbitraryStreamId, kArbitraryUrl, callback); expect(callback.callCount).to.equal(1); expect(callback.args[0]).to.deep.equal(['callback', -1]); @@ -151,10 +143,8 @@ describe('Erizo JS Controller', () => { it('should fail if it already exists', () => { const secondCallback = sinon.stub(); - controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); - controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + controller.addExternalInput(kArbitraryStreamId, kArbitraryUrl, callback); + controller.addExternalInput(kArbitraryStreamId, kArbitraryUrl, callback); expect(callback.callCount).to.equal(1); expect(secondCallback.callCount).to.equal(0); @@ -166,12 +156,10 @@ describe('Erizo JS Controller', () => { const kArbitraryEoOptions = {}; const kArbitraryEiId = 'ei_id1'; const kArbitraryEiUrl = 'ei_url1'; - const kArbitraryErizoControllerId = 'erizoControllerId1'; beforeEach(() => { const eiCallback = () => {}; - controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryEiId, - kArbitraryEiUrl, eiCallback); + controller.addExternalInput(kArbitraryEiId, kArbitraryEiUrl, eiCallback); }); it('should succeed creating ExternalOutput', () => { @@ -213,243 +201,153 @@ describe('Erizo JS Controller', () => { let callback; const kArbitraryStreamId = 'pubStreamId1'; const kArbitraryClientId = 'pubClientid1'; - const kArbitraryErizoControllerId = 'erizoControllerId1'; beforeEach(() => { callback = sinon.stub(); - mocks.WebRtcConnection.setRemoteDescription.returns(Promise.resolve()); global.config.erizo = {}; global.config.erizoController = { report: { connection_events: true, rtcp_stats: true } }; }); - it('should succeed creating OneToManyProcessor and WebRtcConnection', (done) => { + it('should succeed creating OneToManyProcessor and WebRtcConnection', () => { mocks.WebRtcConnection.init.returns(1); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - setTimeout(() => { - expect(erizoApiMock.OneToManyProcessor.callCount).to.equal(1); - expect(erizoApiMock.WebRtcConnection.args[0][2]).to.contain(kArbitraryClientId); - expect(erizoApiMock.WebRtcConnection.callCount).to.equal(1); - expect(mocks.MediaStream.setAudioReceiver.args[0][0]).to.equal(mocks.OneToManyProcessor); - expect(mocks.MediaStream.setVideoReceiver.args[0][0]).to.equal(mocks.OneToManyProcessor); - expect(mocks.OneToManyProcessor.setPublisher.args[0][0]).to - .equal(mocks.MediaStream); - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + + expect(erizoApiMock.OneToManyProcessor.callCount).to.equal(1); + expect(erizoApiMock.WebRtcConnection.args[0][2]).to.contain(kArbitraryClientId); + expect(erizoApiMock.WebRtcConnection.callCount).to.equal(1); + expect(mocks.MediaStream.setAudioReceiver.args[0][0]).to.equal(mocks.OneToManyProcessor); + expect(mocks.MediaStream.setVideoReceiver.args[0][0]).to.equal(mocks.OneToManyProcessor); + expect(mocks.OneToManyProcessor.setPublisher.args[0][0]).to + .equal(mocks.MediaStream); + expect(callback.callCount).to.equal(1); + expect(callback.args[0]).to.deep.equal(['callback', { type: 'initializing' }]); }); - it('should fail if Publishers exists with no Subscriber', (done) => { + it('should fail if Publishers exists with no Subscriber', () => { mocks.WebRtcConnection.init.returns(1); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + + expect(callback.callCount).to.equal(1); }); - it('should fail if it already exists and it has subscribers', (done) => { + it('should fail if it already exists and it has subscribers', () => { const kArbitrarySubClientId = 'id2'; const secondCallback = sinon.stub(); const subCallback = sinon.stub(); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryClientId, {}, subCallback); - - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, secondCallback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(secondCallback.callCount).to.equal(0); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryClientId, {}, subCallback); + + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, secondCallback); + + expect(callback.callCount).to.equal(1); + expect(secondCallback.callCount).to.equal(0); }); - it('should succeed sending offer event', (done) => { + it('should succeed sending offer event', () => { mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 103, ''); // CONN_GATHERED - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, { createOffer: { audio: true, video: true, bundle: true } }, callback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('offer'); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, { createOffer: + { audio: true, video: true, bundle: true } }, callback); + + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0].length).to.equal(2); + expect(callback.args[0][0]).to.equal('callback'); + expect(callback.args[0][1].type).to.equal('offer'); }); - it('should succeed sending offer event from SDP in Tricke ICE', (done) => { - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, kArbitraryStreamId, - { trickleIce: true, createOffer: { audio: true, video: true, bundle: true } }, callback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('offer'); - done(); - }, 0); + it('should succeed sending answer event from SDP in Tricke ICE', () => { + mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 202, ''); // CONN_SDP + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, + { trickleIce: true }, callback); + + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0].length).to.equal(2); + expect(callback.args[0][0]).to.equal('callback'); + expect(callback.args[0][1].type).to.equal('answer'); }); - it('should succeed sending answer event', (done) => { + it('should succeed sending answer event', () => { mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 103, ''); // CONN_GATHERED - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, { type: 'offer', sdp: '' }); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('answer'); - done(); - }, 0); - }); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); - it('should succeed sending answer event from SDP in Tricke ICE', (done) => { - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, { trickleIce: true }, callback); - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, { type: 'offer', sdp: '' }); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', { type: 'initializing', - connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('answer'); - done(); - }, 0); + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0].length).to.equal(2); + expect(callback.args[0][0]).to.equal('callback'); + expect(callback.args[0][1].type).to.equal('answer'); }); - it('should succeed sending candidate event', (done) => { + it('should succeed sending candidate event', () => { mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 201, ''); // CONN_CANDIDATE - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('candidate'); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0]).to.deep.equal(['callback', { candidate: '', type: 'candidate' }]); }); - it('should succeed sending failed event', (done) => { + it('should succeed sending candidate event', () => { mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 500, ''); // CONN_FAILED - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - setTimeout(() => { - expect(callback.callCount).to.equal(1); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(1); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('failed'); - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0]).to.deep.equal(['callback', { sdp: '', type: 'failed' }]); }); it('should succeed sending ready event', () => { const stub = mocks.WebRtcConnection.init; stub.returns(1); - let initCallback; - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + const initCallback = stub.getCall(0).args[0]; + initCallback(103, ''); // CONN_GATHERED + initCallback(104, ''); // CONN_SDP - return Promise.resolve().then(() => { - initCallback = stub.getCall(0).args[0]; - initCallback(103, ''); // CONN_GATHERED - initCallback(104, ''); // CONN_READY - }).then(() => - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, { type: 'offer', sdp: '' })) - .then(() => { - expect(callback.callCount).to.equal(2); - expect(callback.args[0]).to.deep.equal(['callback', - { type: 'initializing', connectionId: `${kArbitraryClientId}_1` }]); - expect(amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent').callCount).to.equal(2); - const call = amqperMock.callRpc.withArgs(`erizoController_${kArbitraryErizoControllerId}`, - 'connectionStatusEvent'); - expect(call.args[0][2][3].type).to.equal('ready'); - expect(call.args[1][2][3].type).to.equal('answer'); - }); + expect(callback.callCount).to.equal(3); + expect(callback.args[0]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[1][1].type).to.deep.equal('answer'); + expect(callback.args[2]).to.deep.equal(['callback', { type: 'ready' }]); }); it('should succeed sending started event', () => { mocks.WebRtcConnection.init.returns(1).callsArgWith(0, 101); // CONN_INITIAL - mocks.WebRtcConnection.addMediaStream.returns(Promise.resolve()); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - return Promise.resolve().then(() => {}).then(() => { - expect(callback.callCount).to.equal(2); - expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing', - connectionId: `${kArbitraryClientId}_1` }]); - expect(callback.args[0]).to.deep.equal(['callback', { type: 'started' }]); - }); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); + + expect(callback.callCount).to.equal(2); + expect(callback.args[1]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(callback.args[0]).to.deep.equal(['callback', { type: 'started' }]); }); describe('Process Signaling Message', () => { - beforeEach((done) => { + beforeEach(() => { mocks.WebRtcConnection.init.onFirstCall().returns(1); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); - setTimeout(() => { - done(); - }, 0); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); }); it('should set remote sdp when received', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, - { type: 'offer', sdp: '', config: {} }); + controller.processSignaling(undefined, kArbitraryStreamId, + { type: 'offer', sdp: '', config: {} }); expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should set candidate when received', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, { - type: 'candidate', - candidate: {} }); + controller.processSignaling(kArbitraryClientId, kArbitraryStreamId, { + type: 'candidate', + candidate: {} }); expect(mocks.WebRtcConnection.addRemoteCandidate.callCount).to.equal(1); }); it('should update sdp', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitraryClientId, - `${kArbitraryClientId}_1`, { - type: 'updatestream', - sdp: 'sdp' }); + controller.processSignaling(kArbitraryClientId, kArbitraryStreamId, { + type: 'updatestream', + sdp: 'sdp' }); expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); @@ -461,110 +359,94 @@ describe('Erizo JS Controller', () => { beforeEach(() => { subCallback = sinon.stub(); mocks.WebRtcConnection.init.onFirstCall().returns(1); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); }); it('should succeed creating WebRtcConnection and adding sub to muxer', () => { mocks.WebRtcConnection.init.returns(1); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, {}, subCallback); - return Promise.resolve().then(() => {}).then(() => { - expect(erizoApiMock.WebRtcConnection.callCount).to.equal(2); - expect(erizoApiMock.WebRtcConnection.args[1][2]).to.contain(kArbitrarySubClientId); - expect(mocks.OneToManyProcessor.addSubscriber.callCount).to.equal(1); - expect(subCallback.callCount).to.equal(1); - }); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryStreamId, {}, subCallback); + + expect(erizoApiMock.WebRtcConnection.callCount).to.equal(2); + expect(erizoApiMock.WebRtcConnection.args[1][2]).to.contain(kArbitrarySubClientId); + expect(mocks.OneToManyProcessor.addSubscriber.callCount).to.equal(1); + expect(subCallback.callCount).to.equal(1); }); it('should fail when we subscribe to an unknown publisher', () => { const kArbitraryUnknownId = 'unknownId'; mocks.WebRtcConnection.init.returns(1); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryUnknownId, {}, subCallback); - return Promise.resolve().then(() => { - expect(erizoApiMock.WebRtcConnection.callCount).to.equal(1); - expect(mocks.OneToManyProcessor.addSubscriber.callCount).to.equal(0); - expect(subCallback.callCount).to.equal(0); - }); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryUnknownId, {}, subCallback); + + expect(erizoApiMock.WebRtcConnection.callCount).to.equal(1); + expect(mocks.OneToManyProcessor.addSubscriber.callCount).to.equal(0); + expect(subCallback.callCount).to.equal(0); }); it('should set Slide Show Mode', () => { mocks.WebRtcConnection.init.onSecondCall().returns(1).callsArgWith(0, 104, ''); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, { slideShowMode: true }, subCallback); - let initCallback; - return Promise.resolve().then(() => { - initCallback = mocks.WebRtcConnection.init.getCall(1).args[0]; - initCallback(103, ''); // CONN_GATHERED - }).then(() => { - initCallback(104, ''); // CONN_READY - }).then(() => { - expect(subCallback.callCount).to.equal(2); - expect(subCallback.args[0]).to.deep.equal(['callback', { type: 'ready' }]); - expect(subCallback.args[1]).to.deep.equal(['callback', { type: 'initializing', - connectionId: `${kArbitrarySubClientId}_1` }]); - expect(mocks.MediaStream.setSlideShowMode.callCount).to.equal(1); - }); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryStreamId, + { slideShowMode: true }, subCallback); + + const initCallback = mocks.WebRtcConnection.init.getCall(1).args[0]; + initCallback(103, ''); // CONN_GATHERED + initCallback(104, ''); // CONN_READY + + expect(subCallback.callCount).to.equal(3); + expect(subCallback.args[0]).to.deep.equal(['callback', { type: 'initializing' }]); + expect(subCallback.args[1]).to.deep.equal(['callback', { type: 'ready' }]); + expect(mocks.MediaStream.setSlideShowMode.callCount).to.equal(1); }); describe('Process Signaling Message', () => { beforeEach(() => { mocks.WebRtcConnection.init.onSecondCall().returns(1); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, {}, subCallback); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryStreamId, {}, subCallback); }); it('should set remote sdp when received', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - `${kArbitrarySubClientId}_1`, { - type: 'offer', - sdp: '', - config: {} }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { type: 'offer', + sdp: '', + config: {} }); expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should set candidate when received', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - `${kArbitrarySubClientId}_1`, { - type: 'candidate', - candidate: {} }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'candidate', + candidate: {} }); expect(mocks.WebRtcConnection.addRemoteCandidate.callCount).to.equal(1); }); it('should update sdp', () => { - controller.processConnectionMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - `${kArbitrarySubClientId}_1`, { - type: 'updatestream', - sdp: 'aaa' }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'updatestream', + sdp: 'aaa' }); expect(mocks.WebRtcConnection.setRemoteDescription.callCount).to.equal(1); }); it('should mute and unmute subscriber stream', () => { - controller.processStreamMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, { - type: 'updatestream', - config: { - muteStream: { - audio: true, - video: false, - }, - } }); - - controller.processStreamMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, { - type: 'updatestream', - config: { - muteStream: { - audio: false, - video: false, - }, - } }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'updatestream', + config: { + muteStream: { + audio: true, + video: false, + }, + } }); + + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'updatestream', + config: { + muteStream: { + audio: false, + video: false, + }, + } }); expect(mocks.MediaStream.muteStream.callCount).to.equal(3); expect(mocks.MediaStream.muteStream.args[1]).to.deep.equal([false, true]); @@ -572,25 +454,23 @@ describe('Erizo JS Controller', () => { }); it('should mute and unmute publisher stream', () => { - controller.processStreamMessage(kArbitraryErizoControllerId, undefined, - kArbitraryStreamId, { - type: 'updatestream', - config: { - muteStream: { - audio: true, - video: false, - }, - } }); - - controller.processStreamMessage(kArbitraryErizoControllerId, undefined, - kArbitraryStreamId, { - type: 'updatestream', - config: { - muteStream: { - audio: false, - video: false, - }, - } }); + controller.processSignaling(undefined, kArbitraryStreamId, { + type: 'updatestream', + config: { + muteStream: { + audio: true, + video: false, + }, + } }); + + controller.processSignaling(undefined, kArbitraryStreamId, { + type: 'updatestream', + config: { + muteStream: { + audio: false, + video: false, + }, + } }); expect(mocks.MediaStream.muteStream.callCount).to.equal(3); expect(mocks.MediaStream.muteStream.args[1]).to.deep.equal([false, true]); @@ -598,24 +478,22 @@ describe('Erizo JS Controller', () => { }); it('should set slide show mode to true', () => { - controller.processStreamMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, { - type: 'updatestream', - config: { - slideShowMode: true, - } }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'updatestream', + config: { + slideShowMode: true, + } }); expect(mocks.MediaStream.setSlideShowMode.callCount).to.equal(1); expect(mocks.MediaStream.setSlideShowMode.args[0][0]).to.be.true; }); it('should set slide show mode to false', () => { - controller.processStreamMessage(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, { - type: 'updatestream', - config: { - slideShowMode: false, - } }); + controller.processSignaling(kArbitrarySubClientId, kArbitraryStreamId, { + type: 'updatestream', + config: { + slideShowMode: false, + } }); expect(mocks.MediaStream.setSlideShowMode.args[0][0]).to.be.false; }); @@ -624,8 +502,7 @@ describe('Erizo JS Controller', () => { describe('Remove Subscriber', () => { beforeEach(() => { mocks.WebRtcConnection.init.onSecondCall().returns(1); - controller.addSubscriber(kArbitraryErizoControllerId, kArbitrarySubClientId, - kArbitraryStreamId, {}, subCallback); + controller.addSubscriber(kArbitrarySubClientId, kArbitraryStreamId, {}, subCallback); }); it('should succeed removing the mediaStream', () => { const testPromise = controller.removeSubscriber(kArbitrarySubClientId, kArbitraryStreamId) @@ -667,8 +544,7 @@ describe('Erizo JS Controller', () => { beforeEach(() => { mocks.OneToManyProcessor.close.callsArg(0); mocks.WebRtcConnection.init.onFirstCall().returns(1); - controller.addPublisher(kArbitraryErizoControllerId, kArbitraryClientId, - kArbitraryStreamId, {}, callback); + controller.addPublisher(kArbitraryClientId, kArbitraryStreamId, {}, callback); }); it('should succeed closing WebRtcConnection and OneToManyProcessor', () => { diff --git a/erizo_controller/test/utils.js b/erizo_controller/test/utils.js index 9bf24769cc..501525ad2d 100644 --- a/erizo_controller/test/utils.js +++ b/erizo_controller/test/utils.js @@ -7,8 +7,6 @@ const mock = require('mock-require'); // eslint-disable-next-line import/no-extraneous-dependencies const sinon = require('sinon'); -const goodCrypto = require('crypto'); - module.exports.start = (mockObject) => { mock(mockObject.mockName, mockObject); return mockObject; @@ -85,7 +83,7 @@ module.exports.reset = () => { module.exports.crypto = createMock('crypto', { createHmac: sinon.stub().returns(module.exports.signature), - randomBytes: () => goodCrypto.randomBytes(16), + randomBytes: sinon.stub().returns(new Buffer(16)), }); module.exports.http = createMock('http', { @@ -163,7 +161,7 @@ module.exports.reset = () => { setRemoteDescription: sinon.stub(), getLocalDescription: sinon.stub().returns(module.exports.ConnectionDescription), addRemoteCandidate: sinon.stub(), - addMediaStream: sinon.stub().returns(Promise.resolve()), + addMediaStream: sinon.stub(), removeMediaStream: sinon.stub(), }; @@ -172,7 +170,6 @@ module.exports.reset = () => { scheme: '', periodicPlis: '', close: sinon.stub(), - init: sinon.stub(), setAudioReceiver: sinon.stub(), setVideoReceiver: sinon.stub(), setMaxVideoBW: sinon.stub(), @@ -208,11 +205,8 @@ module.exports.reset = () => { addEventListener: sinon.stub(), addExternalInput: sinon.stub(), addExternalOutput: sinon.stub(), - processStreamMessageFromClient: sinon.stub(), - processConnectionMessageFromClient: sinon.stub(), + processSignaling: sinon.stub(), addPublisher: sinon.stub(), - addMultipleSubscribers: sinon.stub(), - removeMultipleSubscribers: sinon.stub(), addSubscriber: sinon.stub(), removePublisher: sinon.stub(), removeSubscriber: sinon.stub(), diff --git a/extras/basic_example/basicServer.js b/extras/basic_example/basicServer.js index 9ebfbf5b6a..09d2442f3c 100644 --- a/extras/basic_example/basicServer.js +++ b/extras/basic_example/basicServer.js @@ -132,9 +132,6 @@ const cleanExampleRooms = (callback) => { deleteRoomsIfEmpty(roomsToCheck, () => { callback('done'); }); - }, (err) => { - console.log('Error cleaning example rooms', err); - setTimeout(cleanExampleRooms.bind(this, callback), 3000); }); }; diff --git a/extras/basic_example/public/script.js b/extras/basic_example/public/script.js index 6c56106f5f..048a90f395 100644 --- a/extras/basic_example/public/script.js +++ b/extras/basic_example/public/script.js @@ -70,13 +70,11 @@ const startBasicExample = () => { const mediaConfiguration = getParameterByName('mediaConfiguration') || 'default'; const onlySubscribe = getParameterByName('onlySubscribe'); const onlyPublish = getParameterByName('onlyPublish'); - const autoSubscribe = getParameterByName('autoSubscribe'); console.log('Selected Room', roomName, 'of type', roomType); const config = { audio: true, video: !audioOnly, data: true, screen, - attributes: {}, videoSize: [640, 480, 640, 480], videoFrameRate: [10, 20] }; // If we want screen sharing we have to put our Chrome extension id. @@ -113,9 +111,6 @@ const startBasicExample = () => { room = Erizo.Room({ token }); const subscribeToStreams = (streams) => { - if (autoSubscribe) { - return; - } if (onlyPublish) { return; } @@ -139,9 +134,6 @@ const startBasicExample = () => { if (!onlySubscribe) { room.publish(localStream, options); } - if (autoSubscribe) { - room.autoSubscribe({ '/attributes/type': 'publisher' }, {}, { audio: true, video: true, data: false }, () => {}); - } subscribeToStreams(roomEvent.streams); }); @@ -158,9 +150,6 @@ const startBasicExample = () => { room.addEventListener('stream-added', (streamEvent) => { const streams = []; streams.push(streamEvent.stream); - if (localStream) { - localStream.setAttributes({ type: 'publisher' }); - } subscribeToStreams(streams); document.getElementById('recordButton').disabled = false; }); diff --git a/package.json b/package.json index 72416d97e0..ae77531b29 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,6 @@ "lintBasicExample": "./node_modules/.bin/eslint extras/basic_example/basicServer.js extras/basic_example/public", "lintNuve": "./node_modules/.bin/eslint nuve/", "lintSpine": "./node_modules/.bin/eslint spine/", - "lint": "npm run lintErizoController && npm run lintBasicExample && npm run lintNuve && npm run lintSpine", - "lintErizoAPI": "./erizo/utils/cpplint.py --filter=-legal/copyright,-build/include --linelength=120 ./erizoAPI/*.cc *.h", - "buildErizoAPI": "export ERIZO_HOME=$(pwd)/erizo/ && echo $ERIZO_HOME && cd ./erizoAPI/ && env JOBS=4 ./build.sh" + "lint": "npm run lintErizoController && npm run lintBasicExample && npm run lintNuve && npm run lintSpine" } } diff --git a/scripts/checkNvm.sh b/scripts/checkNvm.sh index 378e36b04f..890e4d4e72 100755 --- a/scripts/checkNvm.sh +++ b/scripts/checkNvm.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -oldstate="$(set +o); set -$-" # POSIXly store all set options. - set +e check_readlink() { @@ -34,4 +32,3 @@ if [ ! $? == 0 ]; then fi fi -eval "$oldstate" # restore all options stored. \ No newline at end of file diff --git a/scripts/installErizo.sh b/scripts/installErizo.sh index ebd76e189f..07c581044d 100755 --- a/scripts/installErizo.sh +++ b/scripts/installErizo.sh @@ -73,7 +73,7 @@ install_erizo_api(){ cd $ROOT/erizoAPI . $NVM_CHECK nvm use - npm install nan@2.13.1 + npm install nan@2.3.2 $FAST_BUILD ./build.sh check_result $? cd $CURRENT_DIR