From 2c383dbe592db838c7cbeeec40bf8d7ef3690a98 Mon Sep 17 00:00:00 2001 From: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> Date: Tue, 21 Feb 2017 13:54:57 -0500 Subject: [PATCH] manager: decouple interface from implementation This patch uses a pimpl idiom to decouple Manager class interface (public API) from its implementation (private members). A back-pointer is also used by private implementation. It's a first step to reduce the complexity and dependency of this fat class. But this patch tries to keep the task atomic. No behavioral changes! But the public API has been slightly modified to arrange the pimpl code. Change-Id: I01f15bab693397bc4c5a0f73393906110008488f Reviewed-by: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> --- src/manager.cpp | 1195 +++++++++++++++++++++++++++-------------------- src/manager.h | 239 ++-------- 2 files changed, 730 insertions(+), 704 deletions(-) diff --git a/src/manager.cpp b/src/manager.cpp index b2e533a6c2..010c9ccd3e 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 65afd43186..66b818de4b 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 -- GitLab