diff --git a/src/manager.cpp b/src/manager.cpp index b2e533a6c2e794d84e9a72d5fae45d326d431e27..010c9ccd3e95d3b89c4de42d2b69ca89cdccc561 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -79,17 +79,26 @@ using random_device = dht::crypto::random_device; #include "video/sinkclient.h" #include <cerrno> -#include <algorithm> #include <ctime> #include <cstdlib> #include <iostream> #include <fstream> #include <sstream> +#include <algorithm> #include <memory> #include <mutex> +#include <list> +#include <random> + namespace ring { +/** To store conference objects by conference ids */ +using ConferenceMap = std::map<std::string, std::shared_ptr<Conference>>; + +/** To store uniquely a list of Call ids */ +using CallIDSet = std::set<std::string>; + static constexpr int ICE_INIT_TIMEOUT {10}; std::atomic_bool Manager::initialized = {false}; @@ -220,40 +229,165 @@ setGnuTlsLogLevel() gnutls_global_set_log_function(tls_print_logs); } -Manager& -Manager::instance() +//============================================================================== + +struct Manager::ManagerPimpl { - // Meyers singleton - static Manager instance_; + explicit ManagerPimpl(Manager& base); - // This will give a warning that can be ignored the first time instance() - // is called...subsequent warnings are more serious - if (not Manager::initialized) - RING_WARN("Not initialized"); + bool parseConfiguration(); - return instance_; -} + /* + * Play one tone + * @return false if the driver is uninitialize + */ + void playATone(Tone::TONEID toneId); -void -Manager::setAutoAnswer(bool enable) -{ - autoAnswer_ = enable; -} + int getCurrentDeviceIndex(DeviceType type); + + /** + * Process remaining participant given a conference and the current call id. + * Mainly called when a participant is detached or hagned up + * @param current call id + * @param conference pointer + */ + void processRemainingParticipants(Conference &conf); + + /** + * Create config directory in home user and return configuration file path + */ + std::string retrieveConfigPath() const; + + void unsetCurrentCall(); + + void switchCall(const std::string& id); + void switchCall(const std::shared_ptr<Call>& call); + + /** + * Add incoming callid to the waiting list + * @param id std::string to add + */ + void addWaitingCall(const std::string& id); + + /** + * Remove incoming callid to the waiting list + * @param id std::string to remove + */ + void removeWaitingCall(const std::string& id); + + void loadAccount(const YAML::Node &item, int &errorCount, + const std::string &accountOrder); + + + void sendTextMessageToConference(const Conference& conf, + const std::map<std::string, std::string>& messages, + const std::string& from) const noexcept; + + + + void bindCallToConference(Call& call, Conference& conf); + + template <class T> + std::shared_ptr<T> findAccount(const std::function<bool(const std::shared_ptr<T>&)>&); + + Manager& base_; // pimpl back-pointer + + std::atomic_bool autoAnswer_ {false}; + + /** Application wide tone controler */ + ToneControl toneCtrl_; + + /** Current Call ID */ + std::string currentCall_; + + /** Protected current call access */ + std::mutex currentCallMutex_; + + /** Audio layer */ + std::shared_ptr<AudioLayer> audiodriver_{nullptr}; + + // Main thread + std::unique_ptr<DTMF> dtmfKey_; + + /** Buffer to generate DTMF */ + AudioBuffer dtmfBuf_; + + // To handle volume control + // short speakerVolume_; + // short micVolume_; + // End of sound variable + + /** + * Mutex used to protect audio layer + */ + std::mutex audioLayerMutex_; + + /** + * Waiting Call Vectors + */ + CallIDSet waitingCalls_; + + /** + * Protect waiting call list, access by many voip/audio threads + */ + std::mutex waitingCallsMutex_; + + /** + * Path of the ConfigFile + */ + std::string path_; + + /** + * Instance of the RingBufferPool for the whole application + * + * In order to send signal to other parts of the application, one must pass through the RingBufferMananger. + * Audio instances must be registered into the RingBufferMananger and bound together via the Manager. + * + */ + std::unique_ptr<RingBufferPool> ringbufferpool_; + + std::unique_ptr<PluginManager> pluginManager_; + + std::map<uintptr_t, Manager::EventHandler> eventHandlerMap_; + + decltype(eventHandlerMap_)::iterator nextEventHandler_; + + std::list<std::function<bool()>> pendingTaskList_; + std::multimap<std::chrono::steady_clock::time_point, std::shared_ptr<Manager::Runnable>> scheduledTasks_; + std::mutex scheduledTasksMutex_; + + // Map containing conference pointers + ConferenceMap conferenceMap_; + + std::atomic_bool finished_ {false}; + + std::mt19937_64 rand_; + + /* ICE support */ + std::unique_ptr<IceTransportFactory> ice_tf_; + + /* Sink ID mapping */ + std::map<std::string, std::weak_ptr<video::SinkClient>> sinkMap_; -Manager::Manager() : - pluginManager_(new PluginManager) - , preferences(), voipPreferences(), - hookPreference(), audioPreference(), shortcutPreferences() #ifdef RING_VIDEO - , videoPreferences() + std::unique_ptr<VideoManager> videoManager_; #endif - , toneCtrl_(preferences) - , currentCallMutex_(), dtmfKey_(), dtmfBuf_(0, AudioFormat::MONO()) +}; + +Manager::ManagerPimpl::ManagerPimpl(Manager& base) + : base_(base) + , pluginManager_(new PluginManager) + , toneCtrl_(base.preferences) + , currentCallMutex_() + , dtmfKey_() + , dtmfBuf_(0, AudioFormat::MONO()) , audioLayerMutex_() - , waitingCalls_(), waitingCallsMutex_(), path_() + , waitingCalls_() + , waitingCallsMutex_() + , path_() , ringbufferpool_(new RingBufferPool) - , callFactory(), conferenceMap_() - , accountFactory_(), ice_tf_() + , conferenceMap_() + , ice_tf_() #ifdef RING_VIDEO , videoManager_(new VideoManager) #endif @@ -268,17 +402,14 @@ Manager::Manager() : ring::libav_utils::ring_avcodec_init(); } -Manager::~Manager() -{} - bool -Manager::parseConfiguration() +Manager::ManagerPimpl::parseConfiguration() { bool result = true; try { YAML::Node parsedFile = YAML::LoadFile(path_); - const int error_count = loadAccountMap(parsedFile); + const int error_count = base_.loadAccountMap(parsedFile); if (error_count > 0) { RING_WARN("Errors while parsing %s", path_.c_str()); @@ -288,7 +419,257 @@ Manager::parseConfiguration() RING_WARN("Could not open configuration file"); } - return result; + return result; +} + +/** + * Multi Thread + */ +void +Manager::ManagerPimpl::playATone(Tone::TONEID toneId) +{ + if (not base_.voipPreferences.getPlayTones()) + return; + + { + std::lock_guard<std::mutex> lock(audioLayerMutex_); + + if (not audiodriver_) { + RING_ERR("Audio layer not initialized"); + return; + } + + audiodriver_->flushUrgent(); + audiodriver_->startStream(); + } + + toneCtrl_.play(toneId); +} + +int +Manager::ManagerPimpl::getCurrentDeviceIndex(DeviceType type) +{ + if (not audiodriver_) + return -1; + switch (type) { + case DeviceType::PLAYBACK: + return audiodriver_->getIndexPlayback(); + case DeviceType::RINGTONE: + return audiodriver_->getIndexRingtone(); + case DeviceType::CAPTURE: + return audiodriver_->getIndexCapture(); + default: + return -1; + } +} + +void +Manager::ManagerPimpl::processRemainingParticipants(Conference &conf) +{ + const std::string current_call_id(base_.getCurrentCallId()); + ParticipantSet participants(conf.getParticipantList()); + const size_t n = participants.size(); + RING_DBG("Process remaining %zu participant(s) from conference %s", + n, conf.getConfID().c_str()); + + if (n > 1) { + // Reset ringbuffer's readpointers + for (const auto &p : participants) + base_.getRingBufferPool().flush(p); + + base_.getRingBufferPool().flush(RingBufferPool::DEFAULT_ID); + } else if (n == 1) { + // this call is the last participant, hence + // the conference is over + ParticipantSet::iterator p = participants.begin(); + + if (auto call = base_.getCallFromCallID(*p)) { + call->setConfId(""); + // if we are not listening to this conference + if (current_call_id != conf.getConfID()) + base_.onHoldCall(call->getCallId()); + else + switchCall(call); + } + + RING_DBG("No remaining participants, remove conference"); + base_.removeConference(conf.getConfID()); + } else { + RING_DBG("No remaining participants, remove conference"); + base_.removeConference(conf.getConfID()); + unsetCurrentCall(); + } +} + +/** + * Initialization: Main Thread + */ +std::string +Manager::ManagerPimpl::retrieveConfigPath() const +{ + static const char * const PROGNAME = "dring"; + return fileutils::get_config_dir() + DIR_SEPARATOR_STR + PROGNAME + ".yml"; +} + +void +Manager::ManagerPimpl::unsetCurrentCall() +{ + currentCall_ = ""; +} + +void +Manager::ManagerPimpl::switchCall(const std::string& id) +{ + std::lock_guard<std::mutex> m(currentCallMutex_); + RING_DBG("----- Switch current call id to '%s' -----", not id.empty() ? id.c_str() : "none"); + currentCall_ = id; +} + +void +Manager::ManagerPimpl::switchCall(const std::shared_ptr<Call>& call) +{ + switchCall(call->getCallId()); +} + +void +Manager::ManagerPimpl::addWaitingCall(const std::string& id) +{ + std::lock_guard<std::mutex> m(waitingCallsMutex_); + waitingCalls_.insert(id); +} + +void +Manager::ManagerPimpl::removeWaitingCall(const std::string& id) +{ + std::lock_guard<std::mutex> m(waitingCallsMutex_); + waitingCalls_.erase(id); +} + +void +Manager::ManagerPimpl::loadAccount(const YAML::Node &node, int &errorCount, + const std::string &accountOrder) +{ + using yaml_utils::parseValue; + + std::string accountType; + parseValue(node, "type", accountType); + + std::string accountid; + parseValue(node, "id", accountid); + + std::string accountAlias; + parseValue(node, "alias", accountAlias); + const auto inAccountOrder = [&](const std::string & id) { + return accountOrder.find(id + "/") != std::string::npos; + }; + + if (!accountid.empty() and !accountAlias.empty()) { + if (not inAccountOrder(accountid)) { + RING_WARN("Dropping account %s, which is not in account order", accountid.c_str()); + } else if (base_.accountFactory.isSupportedType(accountType.c_str())) { + if (auto a = base_.accountFactory.createAccount(accountType.c_str(), accountid)) { + a->unserialize(node); + } else { + RING_ERR("Failed to create account type \"%s\"", accountType.c_str()); + ++errorCount; + } + } else { + RING_WARN("Ignoring unknown account type \"%s\"", accountType.c_str()); + } + } +} + +//THREAD=VoIP +void +Manager::ManagerPimpl::sendTextMessageToConference(const Conference& conf, + const std::map<std::string, std::string>& messages, + const std::string& from) const noexcept +{ + ParticipantSet participants(conf.getParticipantList()); + for (const auto& call_id: participants) { + try { + auto call = base_.getCallFromCallID(call_id); + if (not call) + throw std::runtime_error("no associated call"); + call->sendTextMessage(messages, from); + } catch (const std::exception& e) { + RING_ERR("Failed to send message to conference participant %s: %s", + call_id.c_str(), e.what()); + } + } +} + +void +Manager::ManagerPimpl::bindCallToConference(Call& call, Conference& conf) +{ + const auto& call_id = call.getCallId(); + const auto& conf_id = conf.getConfID(); + const auto& state = call.getStateStr(); + + // ensure that calls are only in one conference at a time + if (base_.isConferenceParticipant(call_id)) + base_.detachParticipant(call_id); + + RING_DBG("[call:%s] bind to conference %s (callState=%s)", + call_id.c_str(), conf_id.c_str(), state.c_str()); + + base_.getRingBufferPool().unBindAll(call_id); + + conf.add(call_id); + call.setConfId(conf_id); + + if (state == "HOLD") { + conf.bindParticipant(call_id); + base_.offHoldCall(call_id); + } else if (state == "INCOMING") { + conf.bindParticipant(call_id); + base_.answerCall(call_id); + } else if (state == "CURRENT") { + conf.bindParticipant(call_id); + } else if (state == "INACTIVE") { + conf.bindParticipant(call_id); + base_.answerCall(call_id); + } else + RING_WARN("[call:%s] call state %s not recognized for conference", call_id.c_str()); +} + +//============================================================================== + +Manager& +Manager::instance() +{ + // Meyers singleton + static Manager instance; + + // This will give a warning that can be ignored the first time instance() + // is called...subsequent warnings are more serious + if (not Manager::initialized) + RING_WARN("Not initialized"); + + return instance; +} + +Manager::Manager() + : preferences() + , voipPreferences() + , hookPreference() + , audioPreference() + , shortcutPreferences() +#ifdef RING_VIDEO + , videoPreferences() +#endif + , callFactory() + , accountFactory() + , pimpl_ (new ManagerPimpl(*this)) +{} + +Manager::~Manager() +{} + +void +Manager::setAutoAnswer(bool enable) +{ + pimpl_->autoAnswer_ = enable; } void @@ -297,7 +678,7 @@ Manager::init(const std::string &config_file) // FIXME: this is no good initialized = true; -#define PJSIP_TRY(ret) do { \ +#define PJSIP_TRY(ret) do { \ if (ret != PJ_SUCCESS) \ throw std::runtime_error(#ret " failed"); \ } while (0) @@ -319,18 +700,18 @@ Manager::init(const std::string &config_file) setDhtLogLevel(); - ice_tf_.reset(new IceTransportFactory()); + pimpl_->ice_tf_.reset(new IceTransportFactory()); - path_ = config_file.empty() ? retrieveConfigPath() : config_file; - RING_DBG("Configuration file path: %s", path_.c_str()); + pimpl_->path_ = config_file.empty() ? pimpl_->retrieveConfigPath() : config_file; + RING_DBG("Configuration file path: %s", pimpl_->path_.c_str()); bool no_errors = true; // manager can restart without being recreated (android) - finished_ = false; + pimpl_->finished_ = false; try { - no_errors = parseConfiguration(); + no_errors = pimpl_->parseConfiguration(); } catch (const YAML::Exception &e) { RING_ERR("%s", e.what()); no_errors = false; @@ -338,7 +719,7 @@ Manager::init(const std::string &config_file) // always back up last error-free configuration if (no_errors) { - make_backup(path_); + make_backup(pimpl_->path_); } else { // restore previous configuration RING_WARN("Restoring last working configuration"); @@ -349,8 +730,8 @@ Manager::init(const std::string &config_file) try { // remove accounts from broken configuration removeAccounts(); - restore_backup(path_); - parseConfiguration(); + restore_backup(pimpl_->path_); + pimpl_->parseConfiguration(); } catch (const YAML::Exception &e) { RING_ERR("%s", e.what()); RING_WARN("Restoring backup failed"); @@ -360,11 +741,11 @@ Manager::init(const std::string &config_file) initAudioDriver(); { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (audiodriver_) { - toneCtrl_.setSampleRate(audiodriver_->getSampleRate()); - dtmfKey_.reset(new DTMF(getRingBufferPool().getInternalSamplingRate())); + if (pimpl_->audiodriver_) { + pimpl_->toneCtrl_.setSampleRate(pimpl_->audiodriver_->getSampleRate()); + pimpl_->dtmfKey_.reset(new DTMF(getRingBufferPool().getInternalSamplingRate())); } } @@ -375,7 +756,7 @@ void Manager::finish() noexcept { bool expected = false; - if (not finished_.compare_exchange_strong(expected, true)) + if (not pimpl_->finished_.compare_exchange_strong(expected, true)) return; try { @@ -397,15 +778,15 @@ Manager::finish() noexcept // Disconnect accounts, close link stacks and free allocated ressources unregisterAccounts(); - accountFactory_.clear(); + accountFactory.clear(); { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - audiodriver_.reset(); + pimpl_->audiodriver_.reset(); } - ice_tf_.reset(); + pimpl_->ice_tf_.reset(); pj_shutdown(); ThreadPool::instance().join(); } catch (const VoipLinkException &err) { @@ -416,56 +797,34 @@ Manager::finish() noexcept bool Manager::isCurrentCall(const Call& call) const { - return currentCall_ == call.getCallId(); + return pimpl_->currentCall_ == call.getCallId(); } bool Manager::hasCurrentCall() const { - return not currentCall_.empty(); + return not pimpl_->currentCall_.empty(); } std::shared_ptr<Call> Manager::getCurrentCall() const { - return getCallFromCallID(currentCall_); + return getCallFromCallID(pimpl_->currentCall_); } const std::string Manager::getCurrentCallId() const { - return currentCall_; -} - -void -Manager::unsetCurrentCall() -{ - currentCall_ = ""; -} - -void -Manager::switchCall(const std::string& id) -{ - std::lock_guard<std::mutex> m(currentCallMutex_); - RING_DBG("----- Switch current call id to '%s' -----", not id.empty() ? id.c_str() : "none"); - currentCall_ = id; + return pimpl_->currentCall_; } void -Manager::switchCall(const std::shared_ptr<Call>& call) -{ - switchCall(call->getCallId()); -} - -template <class T> -inline std::shared_ptr<T> -Manager::findAccount(const std::function<bool(const std::shared_ptr<T>&)>& pred) +Manager::unregisterAccounts() { - for (const auto& account : getAllAccounts<T>()) { - if (pred(account)) - return account; + for (const auto& account : getAllAccounts()) { + if (account->isEnabled()) + account->doUnregister(); } - return {}; } /////////////////////////////////////////////////////////////////////////////// @@ -511,7 +870,7 @@ Manager::outgoingCall(const std::string& preferred_account_id, detachLocalParticipant(); } - switchCall(call); + pimpl_->switchCall(call); call->setConfId(conf_id); return call_id; @@ -558,13 +917,13 @@ Manager::answerCall(const std::string& call_id) } // if it was waiting, it's waiting no more - removeWaitingCall(call_id); + pimpl_->removeWaitingCall(call_id); // if we dragged this call into a conference already if (isConferenceParticipant(call_id)) - switchCall(call->getConfId()); + pimpl_->switchCall(call->getConfId()); else - switchCall(call); + pimpl_->switchCall(call); addAudio(*call); @@ -579,9 +938,9 @@ void Manager::checkAudio() { if (getCallList().empty()) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - if (audiodriver_) - audiodriver_->stopStream(); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + if (pimpl_->audiodriver_) + pimpl_->audiodriver_->stopStream(); } } @@ -610,7 +969,7 @@ Manager::hangupCall(const std::string& callId) } else { // we are not participating in a conference, current call switched to "" if (not isConference(currentCallId) and isCurrentCall(*call)) - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); } try { @@ -629,9 +988,9 @@ Manager::hangupConference(const std::string& id) { RING_DBG("Hangup conference %s", id.c_str()); - ConferenceMap::iterator iter_conf = conferenceMap_.find(id); + ConferenceMap::iterator iter_conf = pimpl_->conferenceMap_.find(id); - if (iter_conf != conferenceMap_.end()) { + if (iter_conf != pimpl_->conferenceMap_.end()) { auto conf = iter_conf->second; if (conf) { @@ -645,7 +1004,7 @@ Manager::hangupConference(const std::string& id) } } - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); return true; } @@ -677,12 +1036,12 @@ Manager::onHoldCall(const std::string& callId) if (result) { // Remove call from the queue if it was still there - removeWaitingCall(callId); + pimpl_->removeWaitingCall(callId); // keeps current call id if the action is not holding this call // or a new outgoing call. This could happen in case of a conference if (current_call_id == callId) - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); } return result; @@ -722,9 +1081,9 @@ Manager::offHoldCall(const std::string& callId) if (result) { if (isConferenceParticipant(callId)) - switchCall(call->getConfId()); + pimpl_->switchCall(call->getConfId()); else - switchCall(call); + pimpl_->switchCall(call); addAudio(*call); } @@ -752,7 +1111,7 @@ Manager::transferCall(const std::string& callId, const std::string& to) if (isConferenceParticipant(callId)) { removeParticipant(callId); } else if (not isConference(getCurrentCallId())) - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); if (auto call = getCallFromCallID(callId)) call->transfer(to); @@ -760,7 +1119,7 @@ Manager::transferCall(const std::string& callId, const std::string& to) return false; // remove waiting call in case we make transfer without even answer - removeWaitingCall(callId); + pimpl_->removeWaitingCall(callId); return true; } @@ -798,15 +1157,15 @@ Manager::refuseCall(const std::string& id) stopTone(); if (getCallList().size() <= 1) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - audiodriver_->stopStream(); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + pimpl_->audiodriver_->stopStream(); } call->refuse(); checkAudio(); - removeWaitingCall(id); + pimpl_->removeWaitingCall(id); // Disconnect streams removeAudio(*call); @@ -818,12 +1177,12 @@ void Manager::removeConference(const std::string& conference_id) { RING_DBG("Remove conference %s", conference_id.c_str()); - RING_DBG("number of participants: %zu", conferenceMap_.size()); - ConferenceMap::iterator iter = conferenceMap_.find(conference_id); + RING_DBG("number of participants: %zu", pimpl_->conferenceMap_.size()); + ConferenceMap::iterator iter = pimpl_->conferenceMap_.find(conference_id); std::shared_ptr<Conference> conf; - if (iter != conferenceMap_.end()) + if (iter != pimpl_->conferenceMap_.end()) conf = iter->second; if (not conf) { @@ -847,7 +1206,7 @@ Manager::removeConference(const std::string& conference_id) getRingBufferPool().bindCallID(*iter_p, RingBufferPool::DEFAULT_ID); // Then remove the conference from the conference map - if (conferenceMap_.erase(conference_id)) + if (pimpl_->conferenceMap_.erase(conference_id)) RING_DBG("Conference %s removed successfully", conference_id.c_str()); else RING_ERR("Cannot remove conference: %s", conference_id.c_str()); @@ -860,9 +1219,9 @@ Manager::getConferenceFromCallID(const std::string& call_id) if (!call) return nullptr; - ConferenceMap::const_iterator iter(conferenceMap_.find(call->getConfId())); + ConferenceMap::const_iterator iter(pimpl_->conferenceMap_.find(call->getConfId())); - if (iter != conferenceMap_.end()) + if (iter != pimpl_->conferenceMap_.end()) return iter->second; else return nullptr; @@ -871,9 +1230,9 @@ Manager::getConferenceFromCallID(const std::string& call_id) bool Manager::holdConference(const std::string& id) { - ConferenceMap::iterator iter_conf = conferenceMap_.find(id); + ConferenceMap::iterator iter_conf = pimpl_->conferenceMap_.find(id); - if (iter_conf == conferenceMap_.end()) + if (iter_conf == pimpl_->conferenceMap_.end()) return false; auto conf = iter_conf->second; @@ -885,7 +1244,7 @@ Manager::holdConference(const std::string& id) ParticipantSet participants(conf->getParticipantList()); for (const auto &item : participants) { - switchCall(getCallFromCallID(item)); + pimpl_->switchCall(getCallFromCallID(item)); onHoldCall(item); } @@ -899,9 +1258,9 @@ Manager::holdConference(const std::string& id) bool Manager::unHoldConference(const std::string& id) { - ConferenceMap::iterator iter_conf = conferenceMap_.find(id); + ConferenceMap::iterator iter_conf = pimpl_->conferenceMap_.find(id); - if (iter_conf == conferenceMap_.end() or iter_conf->second == 0) + if (iter_conf == pimpl_->conferenceMap_.end() or iter_conf->second == 0) return false; auto conf = iter_conf->second; @@ -917,7 +1276,7 @@ Manager::unHoldConference(const std::string& id) // if one call is currently recording, the conference is in state recording isRec |= call->isRecording(); - switchCall(call); + pimpl_->switchCall(call); offHoldCall(item); } } @@ -932,7 +1291,7 @@ Manager::unHoldConference(const std::string& id) bool Manager::isConference(const std::string& id) const { - return conferenceMap_.find(id) != conferenceMap_.end(); + return pimpl_->conferenceMap_.find(id) != pimpl_->conferenceMap_.end(); } bool @@ -948,8 +1307,8 @@ Manager::addParticipant(const std::string& callId, { RING_DBG("Add participant %s to %s", callId.c_str(), conferenceId.c_str()); - auto iter = conferenceMap_.find(conferenceId); - if (iter == conferenceMap_.end() or iter->second == nullptr) { + auto iter = pimpl_->conferenceMap_.find(conferenceId); + if (iter == pimpl_->conferenceMap_.end() or iter->second == nullptr) { RING_ERR("Conference id is not valid"); return false; } @@ -971,15 +1330,15 @@ Manager::addParticipant(const std::string& callId, onHoldCall(current_call_id); } - bindCallToConference(*call, *iter->second); + pimpl_->bindCallToConference(*call, *iter->second); // TODO: remove this ugly hack => There should be different calls when double clicking // a conference to add main participant to it, or (in this case) adding a participant // toconference - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); addMainParticipant(conferenceId); - switchCall(conferenceId); + pimpl_->switchCall(conferenceId); addAudio(*call); return true; @@ -998,11 +1357,11 @@ Manager::addMainParticipant(const std::string& conference_id) } { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - ConferenceMap::const_iterator iter = conferenceMap_.find(conference_id); + ConferenceMap::const_iterator iter = pimpl_->conferenceMap_.find(conference_id); - if (iter == conferenceMap_.end() or iter->second == 0) + if (iter == pimpl_->conferenceMap_.end() or iter->second == 0) return false; auto conf = iter->second; @@ -1027,7 +1386,7 @@ Manager::addMainParticipant(const std::string& conference_id) emitSignal<DRing::CallSignal::ConferenceChanged>(conference_id, conf->getStateStr()); } - switchCall(conference_id); + pimpl_->switchCall(conference_id); return true; } @@ -1037,40 +1396,6 @@ Manager::getCallFromCallID(const std::string& callID) const return callFactory.getCall(callID); } -void -Manager::bindCallToConference(Call& call, Conference& conf) -{ - const auto& call_id = call.getCallId(); - const auto& conf_id = conf.getConfID(); - const auto& state = call.getStateStr(); - - // ensure that calls are only in one conference at a time - if (isConferenceParticipant(call_id)) - detachParticipant(call_id); - - RING_DBG("[call:%s] bind to conference %s (callState=%s)", - call_id.c_str(), conf_id.c_str(), state.c_str()); - - getRingBufferPool().unBindAll(call_id); - - conf.add(call_id); - call.setConfId(conf_id); - - if (state == "HOLD") { - conf.bindParticipant(call_id); - offHoldCall(call_id); - } else if (state == "INCOMING") { - conf.bindParticipant(call_id); - answerCall(call_id); - } else if (state == "CURRENT") { - conf.bindParticipant(call_id); - } else if (state == "INACTIVE") { - conf.bindParticipant(call_id); - answerCall(call_id); - } else - RING_WARN("[call:%s] call state %s not recognized for conference", call_id.c_str()); -} - bool Manager::joinParticipant(const std::string& callId1, const std::string& callId2) { @@ -1105,17 +1430,17 @@ Manager::joinParticipant(const std::string& callId1, const std::string& callId2) auto conf = std::make_shared<Conference>(); // Bind calls according to their state - bindCallToConference(*call1, *conf); - bindCallToConference(*call2, *conf); + pimpl_->bindCallToConference(*call1, *conf); + pimpl_->bindCallToConference(*call2, *conf); // Switch current call id to this conference - switchCall(conf->getConfID()); + pimpl_->switchCall(conf->getConfID()); conf->setState(Conference::ACTIVE_ATTACHED); // set recording sampling rate - conf->setRecordingAudioFormat(ringbufferpool_->getInternalAudioFormat()); + conf->setRecordingAudioFormat(pimpl_->ringbufferpool_->getInternalAudioFormat()); - conferenceMap_.insert(std::make_pair(conf->getConfID(), conf)); + pimpl_->conferenceMap_.insert(std::make_pair(conf->getConfID(), conf)); emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID()); return true; } @@ -1138,7 +1463,7 @@ Manager::createConfFromParticipantList(const std::vector< std::string > &partici std::string tostr(numberaccount.substr(0, numberaccount.find(","))); std::string account(numberaccount.substr(numberaccount.find(",") + 1, numberaccount.size())); - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); // Create call auto call_id = outgoingCall(account, tostr, conf->getConfID()); @@ -1154,9 +1479,9 @@ Manager::createConfFromParticipantList(const std::vector< std::string > &partici // Create the conference if and only if at least 2 calls have been successfully created if (successCounter >= 2) { - conferenceMap_[conf->getConfID()] = conf; + pimpl_->conferenceMap_[conf->getConfID()] = conf; emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID()); - conf->setRecordingAudioFormat(ringbufferpool_->getInternalAudioFormat()); + conf->setRecordingAudioFormat(pimpl_->ringbufferpool_->getInternalAudioFormat()); } } @@ -1171,8 +1496,8 @@ Manager::detachLocalParticipant() return false; } - auto iter = conferenceMap_.find(current_call_id); - if (iter == conferenceMap_.end() or iter->second == nullptr) { + auto iter = pimpl_->conferenceMap_.find(current_call_id); + if (iter == pimpl_->conferenceMap_.end() or iter->second == nullptr) { RING_ERR("Conference is NULL"); return false; } @@ -1193,7 +1518,7 @@ Manager::detachLocalParticipant() emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr()); - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); } bool @@ -1233,10 +1558,10 @@ Manager::removeParticipant(const std::string& call_id) return; } - ConferenceMap::const_iterator iter = conferenceMap_.find(call->getConfId()); + ConferenceMap::const_iterator iter = pimpl_->conferenceMap_.find(call->getConfId()); auto conf = iter->second; - if (iter == conferenceMap_.end() or conf == 0) { + if (iter == pimpl_->conferenceMap_.end() or conf == 0) { RING_ERR("No conference with id %s, cannot remove participant", call->getConfId().c_str()); return; } @@ -1248,62 +1573,24 @@ Manager::removeParticipant(const std::string& call_id) emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr()); - processRemainingParticipants(*conf); -} - -void -Manager::processRemainingParticipants(Conference &conf) -{ - const std::string current_call_id(getCurrentCallId()); - ParticipantSet participants(conf.getParticipantList()); - const size_t n = participants.size(); - RING_DBG("Process remaining %zu participant(s) from conference %s", - n, conf.getConfID().c_str()); - - if (n > 1) { - // Reset ringbuffer's readpointers - for (const auto &p : participants) - getRingBufferPool().flush(p); - - getRingBufferPool().flush(RingBufferPool::DEFAULT_ID); - } else if (n == 1) { - // this call is the last participant, hence - // the conference is over - ParticipantSet::iterator p = participants.begin(); - - if (auto call = getCallFromCallID(*p)) { - call->setConfId(""); - // if we are not listening to this conference - if (current_call_id != conf.getConfID()) - onHoldCall(call->getCallId()); - else - switchCall(call); - } - - RING_DBG("No remaining participants, remove conference"); - removeConference(conf.getConfID()); - } else { - RING_DBG("No remaining participants, remove conference"); - removeConference(conf.getConfID()); - unsetCurrentCall(); - } + pimpl_->processRemainingParticipants(*conf); } bool Manager::joinConference(const std::string& conf_id1, const std::string& conf_id2) { - if (conferenceMap_.find(conf_id1) == conferenceMap_.end()) { + if (pimpl_->conferenceMap_.find(conf_id1) == pimpl_->conferenceMap_.end()) { RING_ERR("Not a valid conference ID: %s", conf_id1.c_str()); return false; } - if (conferenceMap_.find(conf_id2) == conferenceMap_.end()) { + if (pimpl_->conferenceMap_.find(conf_id2) == pimpl_->conferenceMap_.end()) { RING_ERR("Not a valid conference ID: %s", conf_id2.c_str()); return false; } - auto conf = conferenceMap_.find(conf_id1)->second; + auto conf = pimpl_->conferenceMap_.find(conf_id1)->second; ParticipantSet participants(conf->getParticipantList()); for (const auto &p : participants) @@ -1321,8 +1608,8 @@ Manager::addAudio(Call& call) RING_DBG("[conf:%s] Attach local audio", call_id.c_str()); // bind to conference participant - ConferenceMap::iterator iter = conferenceMap_.find(call_id); - if (iter != conferenceMap_.end() and iter->second) { + ConferenceMap::iterator iter = pimpl_->conferenceMap_.find(call_id); + if (iter != pimpl_->conferenceMap_.end() and iter->second) { auto conf = iter->second; conf->bindParticipant(call_id); } @@ -1332,13 +1619,13 @@ Manager::addAudio(Call& call) // bind to main getRingBufferPool().bindCallID(call_id, RingBufferPool::DEFAULT_ID); - std::lock_guard<std::mutex> lock(audioLayerMutex_); - if (!audiodriver_) { + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + if (!pimpl_->audiodriver_) { RING_ERR("Audio driver not initialized"); return; } - audiodriver_->flushUrgent(); - audiodriver_->flushMain(); + pimpl_->audiodriver_->flushUrgent(); + pimpl_->audiodriver_->flushMain(); } startAudioDriverStream(); } @@ -1355,27 +1642,27 @@ Manager::removeAudio(Call& call) void Manager::registerEventHandler(uintptr_t handlerId, EventHandler handler) { - eventHandlerMap_[handlerId] = handler; + pimpl_->eventHandlerMap_[handlerId] = handler; } // Not thread-safe, SHOULD be called in same thread that run pollEvents() void Manager::unregisterEventHandler(uintptr_t handlerId) { - auto iter = eventHandlerMap_.find(handlerId); - if (iter != eventHandlerMap_.end()) { - if (iter == nextEventHandler_) - nextEventHandler_ = eventHandlerMap_.erase(iter); + auto iter = pimpl_->eventHandlerMap_.find(handlerId); + if (iter != pimpl_->eventHandlerMap_.end()) { + if (iter == pimpl_->nextEventHandler_) + pimpl_->nextEventHandler_ = pimpl_->eventHandlerMap_.erase(iter); else - eventHandlerMap_.erase(iter); + pimpl_->eventHandlerMap_.erase(iter); } } void Manager::addTask(const std::function<bool()>&& task) { - std::lock_guard<std::mutex> lock(scheduledTasksMutex_); - pendingTaskList_.emplace_back(std::move(task)); + std::lock_guard<std::mutex> lock(pimpl_->scheduledTasksMutex_); + pimpl_->pendingTaskList_.emplace_back(std::move(task)); } std::shared_ptr<Manager::Runnable> @@ -1386,13 +1673,12 @@ Manager::scheduleTask(const std::function<void()>&& task, std::chrono::steady_cl return runnable; } - void Manager::scheduleTask(const std::shared_ptr<Runnable>& task, std::chrono::steady_clock::time_point when) { - std::lock_guard<std::mutex> lock(scheduledTasksMutex_); - scheduledTasks_.emplace(when, task); - RING_DBG("Task scheduled. Next in %" PRId64, std::chrono::duration_cast<std::chrono::seconds>(scheduledTasks_.begin()->first - std::chrono::steady_clock::now()).count()); + std::lock_guard<std::mutex> lock(pimpl_->scheduledTasksMutex_); + pimpl_->scheduledTasks_.emplace(when, task); + RING_DBG("Task scheduled. Next in %" PRId64, std::chrono::duration_cast<std::chrono::seconds>(pimpl_->scheduledTasks_.begin()->first - std::chrono::steady_clock::now()).count()); } // Must be invoked periodically by a timer from the main event loop @@ -1400,51 +1686,51 @@ void Manager::pollEvents() { //-- Handlers { - auto iter = eventHandlerMap_.begin(); - while (iter != eventHandlerMap_.end()) { - if (finished_) + auto iter = pimpl_->eventHandlerMap_.begin(); + while (iter != pimpl_->eventHandlerMap_.end()) { + if (pimpl_->finished_) return; // WARN: following callback can do anything and typically // calls (un)registerEventHandler. // Think twice before modify this code. - nextEventHandler_ = std::next(iter); + pimpl_->nextEventHandler_ = std::next(iter); try { iter->second(); } catch (const std::exception& e) { RING_ERR("MainLoop exception (handler): %s", e.what()); } - iter = nextEventHandler_; + iter = pimpl_->nextEventHandler_; } } //-- Scheduled tasks { auto now = std::chrono::steady_clock::now(); - std::lock_guard<std::mutex> lock(scheduledTasksMutex_); - while (not scheduledTasks_.empty() && scheduledTasks_.begin()->first <= now) { - auto f = scheduledTasks_.begin(); + std::lock_guard<std::mutex> lock(pimpl_->scheduledTasksMutex_); + while (not pimpl_->scheduledTasks_.empty() && pimpl_->scheduledTasks_.begin()->first <= now) { + auto f = pimpl_->scheduledTasks_.begin(); auto task = std::move(f->second->cb); if (task) - pendingTaskList_.emplace_back([task](){ + pimpl_->pendingTaskList_.emplace_back([task](){ task(); return false; }); - scheduledTasks_.erase(f); + pimpl_->scheduledTasks_.erase(f); } } //-- Tasks { - decltype(pendingTaskList_) tmpList; + decltype(pimpl_->pendingTaskList_) tmpList; { - std::lock_guard<std::mutex> lock(scheduledTasksMutex_); - std::swap(pendingTaskList_, tmpList); + std::lock_guard<std::mutex> lock(pimpl_->scheduledTasksMutex_); + std::swap(pimpl_->pendingTaskList_, tmpList); } auto iter = std::begin(tmpList); while (iter != tmpList.cend()) { - if (finished_) + if (pimpl_->finished_) return; auto next = std::next(iter); @@ -1460,8 +1746,8 @@ void Manager::pollEvents() iter = next; } { - std::lock_guard<std::mutex> lock(scheduledTasksMutex_); - pendingTaskList_.splice(std::end(pendingTaskList_), tmpList); + std::lock_guard<std::mutex> lock(pimpl_->scheduledTasksMutex_); + pimpl_->pendingTaskList_.splice(std::end(pimpl_->pendingTaskList_), tmpList); } } } @@ -1470,13 +1756,13 @@ void Manager::pollEvents() void Manager::saveConfig() { - RING_DBG("Saving Configuration to XDG directory %s", path_.c_str()); + RING_DBG("Saving Configuration to XDG directory %s", pimpl_->path_.c_str()); - if (audiodriver_) { - audioPreference.setVolumemic(audiodriver_->getCaptureGain()); - audioPreference.setVolumespkr(audiodriver_->getPlaybackGain()); - audioPreference.setCaptureMuted(audiodriver_->isCaptureMuted()); - audioPreference.setPlaybackMuted(audiodriver_->isPlaybackMuted()); + if (pimpl_->audiodriver_) { + audioPreference.setVolumemic(pimpl_->audiodriver_->getCaptureGain()); + audioPreference.setVolumespkr(pimpl_->audiodriver_->getPlaybackGain()); + audioPreference.setCaptureMuted(pimpl_->audiodriver_->isCaptureMuted()); + audioPreference.setPlaybackMuted(pimpl_->audiodriver_->isPlaybackMuted()); } try { @@ -1486,7 +1772,7 @@ Manager::saveConfig() out << YAML::BeginMap << YAML::Key << "accounts"; out << YAML::Value << YAML::BeginSeq; - for (const auto& account : accountFactory_.getAllAccounts()) { + for (const auto& account : accountFactory.getAllAccounts()) { account->serialize(out); } out << YAML::EndSeq; @@ -1502,7 +1788,7 @@ Manager::saveConfig() #endif shortcutPreferences.serialize(out); - std::ofstream fout(path_); + std::ofstream fout(pimpl_->path_); fout << out.c_str(); } catch (const YAML::Exception &e) { RING_ERR("%s", e.what()); @@ -1530,16 +1816,16 @@ Manager::playDtmf(char code) return; } - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); // fast return, no sound, so no dtmf - if (not audiodriver_ or not dtmfKey_) { + if (not pimpl_->audiodriver_ or not pimpl_->dtmfKey_) { RING_DBG("No audio layer..."); return; } - audiodriver_->startStream(); - if (not audiodriver_->waitForStart(std::chrono::seconds(1))) { + pimpl_->audiodriver_->startStream(); + if (not pimpl_->audiodriver_->waitForStart(std::chrono::seconds(1))) { RING_ERR("Failed to start audio layer..."); return; } @@ -1548,20 +1834,20 @@ Manager::playDtmf(char code) // size (n sampling) = time_ms * sampling/s // --------------------- // ms/s - int size = (int)((pulselen * (float) audiodriver_->getSampleRate()) / 1000); - dtmfBuf_.resize(size); + int size = (int)((pulselen * (float) pimpl_->audiodriver_->getSampleRate()) / 1000); + pimpl_->dtmfBuf_.resize(size); // Handle dtmf - dtmfKey_->startTone(code); + pimpl_->dtmfKey_->startTone(code); // copy the sound - if (dtmfKey_->generateDTMF(*dtmfBuf_.getChannel(0))) { + if (pimpl_->dtmfKey_->generateDTMF(*pimpl_->dtmfBuf_.getChannel(0))) { // Put buffer to urgentRingBuffer // put the size in bytes... // so size * 1 channel (mono) * sizeof (bytes for the data) // audiolayer->flushUrgent(); - audiodriver_->putUrgent(dtmfBuf_); + pimpl_->audiodriver_->putUrgent(pimpl_->dtmfBuf_); } // TODO Cache the DTMF @@ -1571,22 +1857,8 @@ Manager::playDtmf(char code) bool Manager::incomingCallsWaiting() { - std::lock_guard<std::mutex> m(waitingCallsMutex_); - return not waitingCalls_.empty(); -} - -void -Manager::addWaitingCall(const std::string& id) -{ - std::lock_guard<std::mutex> m(waitingCallsMutex_); - waitingCalls_.insert(id); -} - -void -Manager::removeWaitingCall(const std::string& id) -{ - std::lock_guard<std::mutex> m(waitingCallsMutex_); - waitingCalls_.erase(id); + std::lock_guard<std::mutex> m(pimpl_->waitingCallsMutex_); + return not pimpl_->waitingCalls_.empty(); } /////////////////////////////////////////////////////////////////////////////// @@ -1618,7 +1890,7 @@ Manager::incomingCall(Call &call, const std::string& accountId) playRingtone(accountId); } - addWaitingCall(callID); + pimpl_->addWaitingCall(callID); std::string number(call.getPeerNumber()); @@ -1626,30 +1898,10 @@ Manager::incomingCall(Call &call, const std::string& accountId) emitSignal<DRing::CallSignal::IncomingCall>(accountId, callID, call.getPeerDisplayName() + " " + from); - if (autoAnswer_) + if (pimpl_->autoAnswer_) runOnMainThread([this, callID]{ answerCall(callID); }); } -//THREAD=VoIP -void -Manager::sendTextMessageToConference(const Conference& conf, - const std::map<std::string, std::string>& messages, - const std::string& from) const noexcept -{ - ParticipantSet participants(conf.getParticipantList()); - for (const auto& call_id: participants) { - try { - auto call = getCallFromCallID(call_id); - if (not call) - throw std::runtime_error("no associated call"); - call->sendTextMessage(messages, from); - } catch (const std::exception& e) { - RING_ERR("Failed to send message to conference participant %s: %s", - call_id.c_str(), e.what()); - } - } -} - void Manager::incomingMessage(const std::string& callID, const std::string& from, @@ -1663,7 +1915,7 @@ Manager::incomingMessage(const std::string& callID, } RING_DBG("Is a conference, send incoming message to everyone"); - sendTextMessageToConference(*conf, messages, from); + pimpl_->sendTextMessageToConference(*conf, messages, from); // in case of a conference we must notify client using conference id emitSignal<DRing::CallSignal::IncomingMessage>(conf->getConfID(), from, messages); @@ -1678,14 +1930,14 @@ Manager::sendCallTextMessage(const std::string& callID, bool /*isMixed TODO: use it */) { if (isConference(callID)) { - const auto& it = conferenceMap_.find(callID); - if (it == conferenceMap_.cend() or not it->second) { + const auto& it = pimpl_->conferenceMap_.find(callID); + if (it == pimpl_->conferenceMap_.cend() or not it->second) { RING_ERR("no conference associated to ID %s", callID.c_str()); return; } RING_DBG("Is a conference, send instant message to everyone"); - sendTextMessageToConference(*it->second, messages, from); + pimpl_->sendTextMessageToConference(*it->second, messages, from); } else if (isConferenceParticipant(callID)) { auto conf = getConferenceFromCallID(callID); @@ -1695,7 +1947,7 @@ Manager::sendCallTextMessage(const std::string& callID, } RING_DBG("Call is participant in a conference, send instant message to everyone"); - sendTextMessageToConference(*conf, messages, from); + pimpl_->sendTextMessageToConference(*conf, messages, from); } else { auto call = getCallFromCallID(callID); @@ -1725,10 +1977,10 @@ Manager::peerAnsweredCall(Call& call) addAudio(call); - if (audiodriver_) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - audiodriver_->flushMain(); - audiodriver_->flushUrgent(); + if (pimpl_->audiodriver_) { + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + pimpl_->audiodriver_->flushMain(); + pimpl_->audiodriver_->flushUrgent(); } if (audioPreference.getIsAlwaysRecording()) @@ -1756,13 +2008,13 @@ Manager::peerHungupCall(Call& call) removeParticipant(call_id); } else if (isCurrentCall(call)) { stopTone(); - unsetCurrentCall(); + pimpl_->unsetCurrentCall(); } call.peerHungup(); checkAudio(); - removeWaitingCall(call_id); + pimpl_->removeWaitingCall(call_id); if (not incomingCallsWaiting()) stopTone(); @@ -1776,12 +2028,12 @@ Manager::callBusy(Call& call) RING_DBG("[call:%s] Busy", call.getCallId().c_str()); if (isCurrentCall(call)) { - playATone(Tone::TONE_BUSY); - unsetCurrentCall(); + pimpl_->playATone(Tone::TONE_BUSY); + pimpl_->unsetCurrentCall(); } checkAudio(); - removeWaitingCall(call.getCallId()); + pimpl_->removeWaitingCall(call.getCallId()); } //THREAD=VoIP @@ -1792,8 +2044,8 @@ Manager::callFailure(Call& call) RING_DBG("[call:%s] Failed", call.getCallId().c_str()); if (isCurrentCall(call)) { - playATone(Tone::TONE_BUSY); - unsetCurrentCall(); + pimpl_->playATone(Tone::TONE_BUSY); + pimpl_->unsetCurrentCall(); } if (isConferenceParticipant(call_id)) { @@ -1803,7 +2055,7 @@ Manager::callFailure(Call& call) } checkAudio(); - removeWaitingCall(call_id); + pimpl_->removeWaitingCall(call_id); if (not incomingCallsWaiting()) stopTone(); removeAudio(call); @@ -1817,30 +2069,6 @@ Manager::startVoiceMessageNotification(const std::string& accountId, emitSignal<DRing::CallSignal::VoiceMailNotify>(accountId, nb_msg); } -/** - * Multi Thread - */ -void -Manager::playATone(Tone::TONEID toneId) -{ - if (not voipPreferences.getPlayTones()) - return; - - { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - - if (not audiodriver_) { - RING_ERR("Audio layer not initialized"); - return; - } - - audiodriver_->flushUrgent(); - audiodriver_->startStream(); - } - - toneCtrl_.play(toneId); -} - /** * Multi Thread */ @@ -1850,7 +2078,7 @@ Manager::stopTone() if (not voipPreferences.getPlayTones()) return; - toneCtrl_.stop(); + pimpl_->toneCtrl_.stop(); } /** @@ -1859,7 +2087,7 @@ Manager::stopTone() void Manager::playTone() { - playATone(Tone::TONE_DIALTONE); + pimpl_->playATone(Tone::TONE_DIALTONE); } /** @@ -1868,7 +2096,7 @@ Manager::playTone() void Manager::playToneWithMessage() { - playATone(Tone::TONE_CONGESTION); + pimpl_->playATone(Tone::TONE_CONGESTION); } /** @@ -1877,7 +2105,7 @@ Manager::playToneWithMessage() void Manager::congestion() { - playATone(Tone::TONE_CONGESTION); + pimpl_->playATone(Tone::TONE_CONGESTION); } /** @@ -1886,7 +2114,7 @@ Manager::congestion() void Manager::ringback() { - playATone(Tone::TONE_RINGTONE); + pimpl_->playATone(Tone::TONE_RINGTONE); } /** @@ -1916,44 +2144,31 @@ Manager::playRingtone(const std::string& accountID) } { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) { + if (not pimpl_->audiodriver_) { RING_ERR("no audio layer in ringtone"); return; } // start audio if not started AND flush all buffers (main and urgent) - audiodriver_->startStream(); - toneCtrl_.setSampleRate(audiodriver_->getSampleRate()); + pimpl_->audiodriver_->startStream(); + pimpl_->toneCtrl_.setSampleRate(pimpl_->audiodriver_->getSampleRate()); } - if (not toneCtrl_.setAudioFile(ringchoice)) + if (not pimpl_->toneCtrl_.setAudioFile(ringchoice)) ringback(); } AudioLoop* Manager::getTelephoneTone() { - return toneCtrl_.getTelephoneTone(); + return pimpl_->toneCtrl_.getTelephoneTone(); } AudioLoop* Manager::getTelephoneFile() { - return toneCtrl_.getTelephoneFile(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Private functions -/////////////////////////////////////////////////////////////////////////////// -/** - * Initialization: Main Thread - */ -std::string -Manager::retrieveConfigPath() const -{ - static const char * const PROGNAME = "dring"; - return fileutils::get_config_dir() + DIR_SEPARATOR_STR + PROGNAME + ".yml"; + return pimpl_->toneCtrl_.getTelephoneFile(); } /** @@ -1962,63 +2177,46 @@ Manager::retrieveConfigPath() const void Manager::setAudioPlugin(const std::string& audioPlugin) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); audioPreference.setAlsaPlugin(audioPlugin); - bool wasStarted = audiodriver_->isStarted(); + bool wasStarted = pimpl_->audiodriver_->isStarted(); // Recreate audio driver with new settings - audiodriver_.reset(audioPreference.createAudioLayer()); + pimpl_->audiodriver_.reset(audioPreference.createAudioLayer()); - if (audiodriver_ and wasStarted) - audiodriver_->startStream(); + if (pimpl_->audiodriver_ and wasStarted) + pimpl_->audiodriver_->startStream(); else RING_ERR("No audio layer created, possibly built without audio support"); } -int -Manager::getCurrentDeviceIndex(DeviceType type) -{ - if (not audiodriver_) - return -1; - switch (type) { - case DeviceType::PLAYBACK: - return audiodriver_->getIndexPlayback(); - case DeviceType::RINGTONE: - return audiodriver_->getIndexRingtone(); - case DeviceType::CAPTURE: - return audiodriver_->getIndexCapture(); - default: - return -1; - } -} - /** * Set audio output device */ void Manager::setAudioDevice(int index, DeviceType type) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) { + if (not pimpl_->audiodriver_) { RING_ERR("Audio driver not initialized"); return ; } - if (getCurrentDeviceIndex(type) == index) { + if (pimpl_->getCurrentDeviceIndex(type) == index) { RING_WARN("Audio device already selected ; doing nothing."); return; } - const bool wasStarted = audiodriver_->isStarted(); - audiodriver_->updatePreference(audioPreference, index, type); + const bool wasStarted = pimpl_->audiodriver_->isStarted(); + pimpl_->audiodriver_->updatePreference(audioPreference, index, type); // Recreate audio driver with new settings - audiodriver_.reset(audioPreference.createAudioLayer()); + pimpl_->audiodriver_.reset(audioPreference.createAudioLayer()); - if (audiodriver_ and wasStarted) - audiodriver_->startStream(); + if (pimpl_->audiodriver_ and wasStarted) + pimpl_->audiodriver_->startStream(); } /** @@ -2027,8 +2225,8 @@ Manager::setAudioDevice(int index, DeviceType type) std::vector<std::string> Manager::getAudioOutputDeviceList() { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - return audiodriver_->getPlaybackDeviceList(); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + return pimpl_->audiodriver_->getPlaybackDeviceList(); } /** @@ -2037,8 +2235,8 @@ Manager::getAudioOutputDeviceList() std::vector<std::string> Manager::getAudioInputDeviceList() { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - return audiodriver_->getCaptureDeviceList(); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + return pimpl_->audiodriver_->getCaptureDeviceList(); } /** @@ -2047,16 +2245,16 @@ Manager::getAudioInputDeviceList() std::vector<std::string> Manager::getCurrentAudioDevicesIndex() { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); std::vector<std::string> v; std::stringstream ssi, sso, ssr; - sso << audiodriver_->getIndexPlayback(); + sso << pimpl_->audiodriver_->getIndexPlayback(); v.push_back(sso.str()); - ssi << audiodriver_->getIndexCapture(); + ssi << pimpl_->audiodriver_->getIndexCapture(); v.push_back(ssi.str()); - ssr << audiodriver_->getIndexRingtone(); + ssr << pimpl_->audiodriver_->getIndexRingtone(); v.push_back(ssr.str()); return v; @@ -2117,8 +2315,8 @@ Manager::toggleRecordingCall(const std::string& id) { std::shared_ptr<Recordable> rec; - ConferenceMap::const_iterator it(conferenceMap_.find(id)); - if (it == conferenceMap_.end()) { + ConferenceMap::const_iterator it(pimpl_->conferenceMap_.find(id)); + if (it == pimpl_->conferenceMap_.end()) { RING_DBG("toggle recording for call %s", id.c_str()); rec = getCallFromCallID(id); } else { @@ -2157,24 +2355,24 @@ Manager::startRecordedFilePlayback(const std::string& filepath) RING_DBG("Start recorded file playback %s", filepath.c_str()); { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) { + if (not pimpl_->audiodriver_) { RING_ERR("No audio layer in start recorded file playback"); return false; } - audiodriver_->startStream(); - toneCtrl_.setSampleRate(audiodriver_->getSampleRate()); + pimpl_->audiodriver_->startStream(); + pimpl_->toneCtrl_.setSampleRate(pimpl_->audiodriver_->getSampleRate()); } - return toneCtrl_.setAudioFile(filepath); + return pimpl_->toneCtrl_.setAudioFile(filepath); } void Manager::recordingPlaybackSeek(const double value) { - toneCtrl_.seek(value); + pimpl_->toneCtrl_.seek(value); } void @@ -2185,7 +2383,7 @@ Manager::stopRecordedFilePlayback(const std::string& filepath) RING_DBG("Stop recorded file playback %s", filepath.c_str()); checkAudio(); - toneCtrl_.stopAudioFile(); + pimpl_->toneCtrl_.stopAudioFile(); } void @@ -2206,9 +2404,9 @@ bool Manager::setAudioManager(const std::string &api) { { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) + if (not pimpl_->audiodriver_) return false; if (api == audioPreference.getAudioApi()) { @@ -2218,14 +2416,14 @@ Manager::setAudioManager(const std::string &api) } { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - bool wasStarted = audiodriver_->isStarted(); + bool wasStarted = pimpl_->audiodriver_->isStarted(); audioPreference.setAudioApi(api); - audiodriver_.reset(audioPreference.createAudioLayer()); + pimpl_->audiodriver_.reset(audioPreference.createAudioLayer()); - if (audiodriver_ and wasStarted) - audiodriver_->startStream(); + if (pimpl_->audiodriver_ and wasStarted) + pimpl_->audiodriver_->startStream(); } saveConfig(); @@ -2243,27 +2441,27 @@ Manager::getAudioManager() const int Manager::getAudioInputDeviceIndex(const std::string &name) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) { + if (not pimpl_->audiodriver_) { RING_ERR("Audio layer not initialized"); return 0; } - return audiodriver_->getAudioDeviceIndex(name, DeviceType::CAPTURE); + return pimpl_->audiodriver_->getAudioDeviceIndex(name, DeviceType::CAPTURE); } int Manager::getAudioOutputDeviceIndex(const std::string &name) { - std::lock_guard<std::mutex> lock(audioLayerMutex_); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); - if (not audiodriver_) { + if (not pimpl_->audiodriver_) { RING_ERR("Audio layer not initialized"); return 0; } - return audiodriver_->getAudioDeviceIndex(name, DeviceType::PLAYBACK); + return pimpl_->audiodriver_->getAudioDeviceIndex(name, DeviceType::PLAYBACK); } std::string @@ -2302,8 +2500,8 @@ Manager::setAGCState(bool state) void Manager::initAudioDriver() { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - audiodriver_.reset(audioPreference.createAudioLayer()); + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + pimpl_->audiodriver_.reset(audioPreference.createAudioLayer()); } AudioFormat @@ -2315,7 +2513,7 @@ Manager::hardwareAudioFormatChanged(AudioFormat format) AudioFormat Manager::audioFormatUsed(AudioFormat format) { - AudioFormat currentFormat = ringbufferpool_->getInternalAudioFormat(); + AudioFormat currentFormat = pimpl_->ringbufferpool_->getInternalAudioFormat(); format.nb_channels = std::max(currentFormat.nb_channels, std::min(format.nb_channels, 2u)); // max 2 channels. format.sample_rate = std::max(currentFormat.sample_rate, format.sample_rate); @@ -2325,9 +2523,9 @@ Manager::audioFormatUsed(AudioFormat format) RING_DBG("Audio format changed: %s -> %s", currentFormat.toString().c_str(), format.toString().c_str()); - ringbufferpool_->setInternalAudioFormat(format); - toneCtrl_.setSampleRate(format.sample_rate); - dtmfKey_.reset(new DTMF(format.sample_rate)); + pimpl_->ringbufferpool_->setInternalAudioFormat(format); + pimpl_->toneCtrl_.setSampleRate(format.sample_rate); + pimpl_->dtmfKey_.reset(new DTMF(format.sample_rate)); return format; } @@ -2385,7 +2583,6 @@ Manager::getVolatileAccountDetails(const std::string& accountID) const } } - // method to reduce the if/else mess. // Even better, switch to XML ! @@ -2462,7 +2659,7 @@ Manager::getNewAccountId() do { std::ostringstream accId; - accId << std::hex << rand_acc_id(rand_); + accId << std::hex << rand_acc_id(pimpl_->rand_); newAccountID = accId.str(); } while (std::find(accountList.begin(), accountList.end(), newAccountID) != accountList.end()); @@ -2485,7 +2682,7 @@ Manager::addAccount(const std::map<std::string, std::string>& details, const std RING_DBG("Adding account %s", newAccountID.c_str()); - auto newAccount = accountFactory_.createAccount(accountType, newAccountID); + auto newAccount = accountFactory.createAccount(accountType, newAccountID); if (!newAccount) { RING_ERR("Unknown %s param when calling addAccount(): %s", Conf::CONFIG_ACCOUNT_TYPE, accountType); @@ -2505,12 +2702,6 @@ Manager::addAccount(const std::map<std::string, std::string>& details, const std return newAccountID; } -void Manager::removeAccounts() -{ - for (const auto &acc : getAccountList()) - removeAccount(acc); -} - void Manager::removeAccount(const std::string& accountID, bool flush) { // Get it down and dying @@ -2518,7 +2709,7 @@ void Manager::removeAccount(const std::string& accountID, bool flush) remAccount->doUnregister(); if (flush) remAccount->flush(); - accountFactory_.removeAccount(*remAccount); + accountFactory.removeAccount(*remAccount); } preferences.removeAccount(accountID); @@ -2528,10 +2719,11 @@ void Manager::removeAccount(const std::string& accountID, bool flush) emitSignal<DRing::ConfigurationSignal::AccountsChanged>(); } -bool -Manager::isValidCall(const std::string& callID) +void +Manager::removeAccounts() { - return static_cast<bool>(getCallFromCallID(callID)); + for (const auto &acc : getAccountList()) + removeAccount(acc); } std::string @@ -2543,8 +2735,8 @@ Manager::getNewCallID() // generate something like s7ea037947eb9fb2f do { random_id.clear(); - random_id << rand_call_id(rand_); - } while (isValidCall(random_id.str())); + random_id << rand_call_id(pimpl_->rand_); + } while (callFactory.hasCall(random_id.str())); return random_id.str(); } @@ -2555,42 +2747,8 @@ Manager::loadAccountOrder() const return split_string(preferences.getAccountOrder(), '/'); } -void -Manager::loadAccount(const YAML::Node &node, int &errorCount, - const std::string &accountOrder) -{ - using yaml_utils::parseValue; - - std::string accountType; - parseValue(node, "type", accountType); - - std::string accountid; - parseValue(node, "id", accountid); - - std::string accountAlias; - parseValue(node, "alias", accountAlias); - const auto inAccountOrder = [&](const std::string & id) { - return accountOrder.find(id + "/") != std::string::npos; - }; - - if (!accountid.empty() and !accountAlias.empty()) { - if (not inAccountOrder(accountid)) { - RING_WARN("Dropping account %s, which is not in account order", accountid.c_str()); - } else if (accountFactory_.isSupportedType(accountType.c_str())) { - if (auto a = accountFactory_.createAccount(accountType.c_str(), accountid)) { - a->unserialize(node); - } else { - RING_ERR("Failed to create account type \"%s\"", accountType.c_str()); - ++errorCount; - } - } else { - RING_WARN("Ignoring unknown account type \"%s\"", accountType.c_str()); - } - } -} - int -Manager::loadAccountMap(const YAML::Node &node) +Manager::loadAccountMap(const YAML::Node& node) { // build preferences preferences.unserialize(node); @@ -2615,7 +2773,7 @@ Manager::loadAccountMap(const YAML::Node &node) const auto &accountList = node["accounts"]; for (auto &a : accountList) { - loadAccount(a, errorCount, accountOrder); + pimpl_->loadAccount(a, errorCount, accountOrder); } return errorCount; @@ -2644,9 +2802,9 @@ Manager::getConferenceDetails( const std::string& confID) const { std::map<std::string, std::string> conf_details; - ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID); + ConferenceMap::const_iterator iter_conf = pimpl_->conferenceMap_.find(confID); - if (iter_conf != conferenceMap_.end()) { + if (iter_conf != pimpl_->conferenceMap_.end()) { conf_details["CONFID"] = confID; conf_details["CONF_STATE"] = iter_conf->second->getStateStr(); } @@ -2658,7 +2816,7 @@ std::vector<std::string> Manager::getConferenceList() const { std::vector<std::string> v; - map_utils::vectorFromMapKeys(conferenceMap_, v); + map_utils::vectorFromMapKeys(pimpl_->conferenceMap_, v); return v; } @@ -2666,9 +2824,9 @@ std::vector<std::string> Manager::getDisplayNames(const std::string& confID) const { std::vector<std::string> v; - ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID); + ConferenceMap::const_iterator iter_conf = pimpl_->conferenceMap_.find(confID); - if (iter_conf != conferenceMap_.end()) { + if (iter_conf != pimpl_->conferenceMap_.end()) { return iter_conf->second->getDisplayNames(); } else { RING_WARN("Did not find conference %s", confID.c_str()); @@ -2681,9 +2839,9 @@ std::vector<std::string> Manager::getParticipantList(const std::string& confID) const { std::vector<std::string> v; - ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID); + ConferenceMap::const_iterator iter_conf = pimpl_->conferenceMap_.find(confID); - if (iter_conf != conferenceMap_.end()) { + if (iter_conf != pimpl_->conferenceMap_.end()) { const ParticipantSet participants(iter_conf->second->getParticipantList()); std::copy(participants.begin(), participants.end(), std::back_inserter(v));; } else @@ -2705,12 +2863,12 @@ Manager::getConferenceId(const std::string& callID) void Manager::startAudioDriverStream() { - std::lock_guard<std::mutex> lock(audioLayerMutex_); - if (!audiodriver_) { + std::lock_guard<std::mutex> lock(pimpl_->audioLayerMutex_); + if (!pimpl_->audiodriver_) { RING_ERR("Audio driver not initialized"); return; } - audiodriver_->startStream(); + pimpl_->audiodriver_->startStream(); } void @@ -2731,15 +2889,6 @@ Manager::registerAccounts() } } -void -Manager::unregisterAccounts() -{ - for (const auto& account : getAllAccounts()) { - if (account->isEnabled()) - account->doUnregister(); - } -} - void Manager::sendRegister(const std::string& accountID, bool enable) { @@ -2775,7 +2924,7 @@ Manager::sendTextMessage(const std::string& accountID, const std::string& to, int Manager::getMessageStatus(uint64_t id) { - const auto& allAccounts = accountFactory_.getAllAccounts(); + const auto& allAccounts = accountFactory.getAllAccounts(); for (auto acc : allAccounts) { auto status = acc->getMessageStatus(id); if (status != im::MessageStatus::UNKNOWN) { @@ -2815,7 +2964,7 @@ Manager::setAccountActive(const std::string& accountID, bool active) std::shared_ptr<AudioLayer> Manager::getAudioDriver() { - return audiodriver_; + return pimpl_->audiodriver_; } std::shared_ptr<Call> @@ -2859,23 +3008,23 @@ Manager::newOutgoingCall(const std::string& toUrl, const std::string& preferredA std::shared_ptr<video::SinkClient> Manager::createSinkClient(const std::string& id, bool mixer) { - const auto& iter = sinkMap_.find(id); - if (iter != std::end(sinkMap_)) { + const auto& iter = pimpl_->sinkMap_.find(id); + if (iter != std::end(pimpl_->sinkMap_)) { if (auto sink = iter->second.lock()) return sink; - sinkMap_.erase(iter); // remove expired weak_ptr + pimpl_->sinkMap_.erase(iter); // remove expired weak_ptr } auto sink = std::make_shared<video::SinkClient>(id, mixer); - sinkMap_.emplace(id, sink); + pimpl_->sinkMap_.emplace(id, sink); return sink; } std::shared_ptr<video::SinkClient> Manager::getSinkClient(const std::string& id) { - const auto& iter = sinkMap_.find(id); - if (iter != std::end(sinkMap_)) + const auto& iter = pimpl_->sinkMap_.find(id); + if (iter != std::end(pimpl_->sinkMap_)) if (auto sink = iter->second.lock()) return sink; return nullptr; @@ -2896,4 +3045,30 @@ Manager::setDecodingAccelerated(bool isAccelerated) #endif // RING_ACCEL #endif // RING_VIDEO +RingBufferPool& +Manager::getRingBufferPool() +{ + return *pimpl_->ringbufferpool_; +} + +bool +Manager::hasAccount(const std::string& accountID) +{ + return accountFactory.hasAccount(accountID); +} + +IceTransportFactory& +Manager::getIceTransportFactory() +{ + return *pimpl_->ice_tf_; +} + +#ifdef RING_VIDEO +VideoManager& +Manager::getVideoManager() const +{ + return *pimpl_->videoManager_; +} +#endif + } // namespace ring diff --git a/src/manager.h b/src/manager.h index 65afd431860d302ed37c0c8259c14fd5bb82e98b..66b818de4b08a8544e8b22725d6f060e4d8d766b 100644 --- a/src/manager.h +++ b/src/manager.h @@ -30,51 +30,33 @@ #include "config.h" #endif +#include "account_factory.h" +#include "call_factory.h" +#include "preferences.h" +#include "audio/audiolayer.h" + #include <string> #include <vector> -#include <list> #include <map> #include <memory> -#include <mutex> -#include <random> #include <atomic> #include <functional> #include "conference.h" -#include "account_factory.h" -#include "call_factory.h" - #include "audio/audiolayer.h" #include "audio/tonecontrol.h" - -#include "preferences.h" -#include "noncopyable.h" - namespace ring { namespace video { class SinkClient; } -class PluginManager; -class DTMF; class RingBufferPool; class VideoManager; -/** To store conference objects by conference ids */ -typedef std::map<std::string, std::shared_ptr<Conference> > ConferenceMap; - -typedef std::set<std::string> CallIDSet; - /** Manager (controller) of Ring daemon */ class Manager { - private: - std::unique_ptr<PluginManager> pluginManager_; - public: - Manager(); - ~Manager(); - static Manager& instance(); void setAutoAnswer(bool enable); @@ -506,6 +488,8 @@ class Manager { */ void removeAccount(const std::string& accountID, bool flush=false); + void removeAccounts(); + /** * Set input audio plugin * @param audioPlugin The audio plugin @@ -513,10 +497,10 @@ class Manager { void setAudioPlugin(const std::string& audioPlugin); /** - * Set audio device - * @param index The index of the soundcard - * @param the type of stream, either PLAYBACK, CAPTURE, RINGTONE - */ + * Set audio device + * @param index The index of the soundcard + * @param the type of stream, either PLAYBACK, CAPTURE, RINGTONE + */ void setAudioDevice(int index, DeviceType streamType); /** @@ -743,120 +727,21 @@ class Manager { */ std::vector<std::string> loadAccountOrder() const; + /** + * Load the account map from configuration + */ + int loadAccountMap(const YAML::Node& node); + /** * Get the Call referred by callID. If the Call does not exist, return * empty std::shared_ptr<Call> instance */ std::shared_ptr<Call> getCallFromCallID(const std::string &callID) const; - private: - std::atomic_bool autoAnswer_ {false}; - - void removeAccounts(); - - bool parseConfiguration(); - - /** - * Process remaining participant given a conference and the current call id. - * Mainly called when a participant is detached or hagned up - * @param current call id - * @param conference pointer - */ - void processRemainingParticipants(Conference &conf); - - /** - * Create config directory in home user and return configuration file path - */ - std::string retrieveConfigPath() const; - - void unsetCurrentCall(); - - void switchCall(const std::string& id); - void switchCall(const std::shared_ptr<Call>& call); - - /** Application wide tone controler */ - ToneControl toneCtrl_; - - /* - * Play one tone - * @return false if the driver is uninitialize - */ - void playATone(Tone::TONEID toneId); - - int getCurrentDeviceIndex(DeviceType type); - - /** Current Call ID */ - std::string currentCall_; - - /** Protected current call access */ - std::mutex currentCallMutex_; - - /** Audio layer */ - std::shared_ptr<AudioLayer> audiodriver_{nullptr}; - - // Main thread - std::unique_ptr<DTMF> dtmfKey_; - - /** Buffer to generate DTMF */ - AudioBuffer dtmfBuf_; - - // To handle volume control - // short speakerVolume_; - // short micVolume_; - // End of sound variable - - /** - * Mutex used to protect audio layer - */ - std::mutex audioLayerMutex_; - - /** - * Waiting Call Vectors - */ - CallIDSet waitingCalls_; - - /** - * Protect waiting call list, access by many voip/audio threads - */ - std::mutex waitingCallsMutex_; - - /** - * Add incoming callid to the waiting list - * @param id std::string to add - */ - void addWaitingCall(const std::string& id); - - /** - * Remove incoming callid to the waiting list - * @param id std::string to remove - */ - void removeWaitingCall(const std::string& id); - - /** - * Path of the ConfigFile - */ - std::string path_; - - /** - * Load the account map from configuration - */ - int loadAccountMap(const YAML::Node &node); - - /** - * Instance of the RingBufferPool for the whole application - * - * In order to send signal to other parts of the application, one must pass through the RingBufferMananger. - * Audio instances must be registered into the RingBufferMananger and bound together via the Manager. - * - */ - std::unique_ptr<RingBufferPool> ringbufferpool_; - - public: - /** * Return a pointer to the instance of the RingBufferPool */ - RingBufferPool& getRingBufferPool() { return *ringbufferpool_; } + RingBufferPool& getRingBufferPool(); /** * Tell if there is a current call processed @@ -871,7 +756,7 @@ class Manager { */ template <class T=Account> std::shared_ptr<T> getAccount(const std::string& accountID) const { - return accountFactory_.getAccount<T>(accountID); + return accountFactory.getAccount<T>(accountID); } /** @@ -885,11 +770,11 @@ class Manager { // If no order has been set, load the default one ie according to the creation date. if (account_order.empty()) { - for (const auto &account : accountFactory_.getAllAccounts<T>()) + for (const auto &account : accountFactory.getAllAccounts<T>()) accountList.emplace_back(account); } else { for (const auto& id : account_order) { - if (auto acc = accountFactory_.getAccount<T>(id)) + if (auto acc = accountFactory.getAccount<T>(id)) accountList.push_back(acc); } } @@ -898,19 +783,32 @@ class Manager { template <class T=Account> bool accountCount() const { - return accountFactory_.accountCount<T>(); + return accountFactory.accountCount<T>(); } - // only used by test framework - bool hasAccount(const std::string& accountID) { - return accountFactory_.hasAccount(accountID); + template <class T> + inline std::shared_ptr<T> + findAccount(const std::function<bool(const std::shared_ptr<T>&)>& pred) { + for (const auto& account : getAllAccounts<T>()) { + if (pred(account)) + return account; + } + return {}; } + // only used by test framework + bool hasAccount(const std::string& accountID); + /** * Send registration for all enabled accounts */ void registerAccounts(); + /** + * Send unregister for all enabled accounts + */ + void unregisterAccounts(); + /** * Suspends Ring's audio processing if no calls remain, allowing * other applications to resume audio. @@ -952,7 +850,7 @@ class Manager { */ void unregisterEventHandler(uintptr_t handlerId); - IceTransportFactory& getIceTransportFactory() { return *ice_tf_; } + IceTransportFactory& getIceTransportFactory(); void addTask(const std::function<bool()>&& task); @@ -982,7 +880,7 @@ class Manager { */ std::shared_ptr<video::SinkClient> getSinkClient(const std::string& id); - VideoManager& getVideoManager() const { return *videoManager_; } + VideoManager& getVideoManager() const; #ifdef RING_ACCEL bool getDecodingAccelerated() const; @@ -992,61 +890,14 @@ class Manager { #endif // RING_VIDEO std::atomic<unsigned> dhtLogLevel {0}; // default = disable + AccountFactory accountFactory; - private: - NON_COPYABLE(Manager); - - std::map<uintptr_t, EventHandler> eventHandlerMap_; - decltype(eventHandlerMap_)::iterator nextEventHandler_; - - std::list<std::function<bool()>> pendingTaskList_; - std::multimap<std::chrono::steady_clock::time_point, std::shared_ptr<Runnable>> scheduledTasks_; - std::mutex scheduledTasksMutex_; - - /** - * Test if call is a valid call, i.e. have been created and stored in - * call-account map - * @param callID the std::string to be tested - * @return true if call is created and present in the call-account map - */ - bool isValidCall(const std::string& callID); - - /** - * Send unregister for all enabled accounts - */ - void unregisterAccounts(); - - - // Map containing conference pointers - ConferenceMap conferenceMap_; - - std::atomic_bool finished_ {false}; - - AccountFactory accountFactory_; - - std::mt19937_64 rand_; - - void loadAccount(const YAML::Node &item, int &errorCount, - const std::string &accountOrder); - - /* ICE support */ - std::unique_ptr<IceTransportFactory> ice_tf_; - - /* Sink ID mapping */ - std::map<std::string, std::weak_ptr<video::SinkClient>> sinkMap_; - - void sendTextMessageToConference(const Conference& conf, - const std::map<std::string, std::string>& messages, - const std::string& from) const noexcept; - -#ifdef RING_VIDEO - std::unique_ptr<VideoManager> videoManager_; -#endif - - void bindCallToConference(Call& call, Conference& conf); +private: + Manager(); + ~Manager(); - template <class T> - std::shared_ptr<T> findAccount(const std::function<bool(const std::shared_ptr<T>&)>&); + struct ManagerPimpl; + std::unique_ptr<ManagerPimpl> pimpl_; }; // Helper to install a callback to be called once by the main event loop