34#include <opendht/rng.h>
80#include <dhtnet/ice_transport_factory.h>
81#include <dhtnet/ice_transport.h>
82#include <dhtnet/upnp/upnp_context.h>
84#include <libavutil/ffversion.h>
86#include <opendht/thread_pool.h>
88#include <asio/io_context.hpp>
89#include <asio/executor_work_guard.hpp>
95#include <sys/resource.h>
99#include <CoreFoundation/CoreFoundation.h>
115#error "Define the JAMI_DATADIR macro as the data installation prefix of the package"
128bool Manager::isIOSExtension = {
false};
170 if (
not std::filesystem::is_directory(
new_dir)) {
188 std::filesystem::remove_all(
old_dir,
ec);
224 auto msg = std::string_view(data, len);
302 const std::map<std::string, std::string>&
messages,
303 const std::string& from)
const noexcept;
312 std::shared_ptr<T>
findAccount(
const std::function<
bool(
const std::shared_ptr<T>&)>&);
392 std::shared_ptr<dhtnet::IceTransportFactory>
ice_tf_;
395 std::map<std::string, std::weak_ptr<video::SinkClient>>
sinkMap_;
406 std::map<git_smart_subtransport*, std::unique_ptr<P2PSubTransport>>
gitTransports_ {};
413 , toneCtrl_(base.preferences)
429 std::ifstream
file(path_);
435 JAMI_WARN(
"Error while parsing %s", path_.c_str());
438 }
catch (
const YAML::BadFile&
e) {
439 JAMI_WARN(
"Unable to open configuration file");
452 if (
not base_.voipPreferences.getPlayTones())
455 std::lock_guard lock(audioLayerMutex_);
456 if (
not audiodriver_) {
457 JAMI_ERR(
"Uninitialized audio layer");
461 auto oldGuard = std::move(toneDeviceGuard_);
463 audiodriver_->flushUrgent();
470 if (
not audiodriver_)
474 return audiodriver_->getIndexPlayback();
476 return audiodriver_->getIndexRingtone();
478 return audiodriver_->getIndexCapture();
490 JAMI_DEBUG(
"Process remaining {} participant(s) from conference {}",
n,
conf.getConfId());
495 if (
auto call = base_.getCallFromCallID(p)) {
499 base_.getRingBufferPool().flush(
media.first);
506 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(
conf.getAccount())) {
508 if (
auto cm = acc->convModule(
true)) {
509 if (acc->isRendezVous() ||
cm->isHosting(
"",
conf.getConfId())) {
521 if (
auto call = base_.getCallFromCallID(*p)) {
535 JAMI_DBG(
"No remaining participants, remove conference");
539 JAMI_DBG(
"No remaining participants, remove conference");
566 std::lock_guard
m(currentCallMutex_);
567 JAMI_DBG(
"----- Switch current call ID to '%s' -----",
not id.empty() ?
id.
c_str() :
"none");
574 std::lock_guard
m(waitingCallsMutex_);
576 if (audiodriver_
and waitingCalls_.empty()
and not currentCall_.empty())
577 audiodriver_->playIncomingCallNotification(
true);
578 waitingCalls_.insert(
id);
584 std::lock_guard
m(waitingCallsMutex_);
585 waitingCalls_.erase(
id);
586 if (audiodriver_
and waitingCalls_.empty())
587 audiodriver_->playIncomingCallNotification(
false);
604 auto config =
a->buildConfig();
605 config->unserialize(node);
606 a->setConfig(std::move(config));
617 const std::map<std::string, std::string>&
messages,
618 const std::string& from)
const noexcept
621 for (
const auto& callId :
subcalls) {
623 auto call = base_.getCallFromCallID(callId);
625 throw std::runtime_error(
"No associated call");
626 call->sendTextMessage(
messages, from);
627 }
catch (
const std::exception&
e) {
628 JAMI_ERR(
"Failed to send message to conference participant %s: %s",
638 pimpl_->bindCallToConference(call,
conf);
650 base_.detachParticipant(callId);
652 JAMI_DEBUG(
"[call:{}] Bind to conference {} (callState={})", callId,
confId, state);
657 base_.getRingBufferPool().unBindAll(
media.first);
660 conf.addSubCall(callId);
662 if (state ==
"HOLD") {
664 }
else if (state ==
"INCOMING") {
665 base_.answerCall(call);
666 }
else if (state ==
"CURRENT") {
667 }
else if (state ==
"INACTIVE") {
668 base_.answerCall(call);
670 JAMI_WARNING(
"[call:{}] Call state {} unrecognized for conference", callId, state);
706 pimpl_ = std::make_unique<ManagerPimpl>(*
this);
709Manager::~Manager() {}
714 pimpl_->autoAnswer_ = enable;
727 JAMI_ERROR(
"Unable to initialize git transport: {}", error ? error->message :
"(unknown)");
741#define PJSIP_TRY(ret) \
743 if ((ret) != PJ_SUCCESS) \
744 throw std::runtime_error(#ret " failed"); \
761 JAMI_LOG(
"Using OpenDHT version: {:s}", dht::version());
770 pimpl_->sipLink_ = std::make_unique<SIPVoIPLink>();
776 pimpl_->ice_tf_ = std::make_shared<dhtnet::IceTransportFactory>(Logger::dhtLogger());
779 JAMI_LOG(
"Configuration file path: {}", pimpl_->path_);
782 pimpl_->jami_plugin_manager = std::make_unique<JamiPluginManager>();
788 pimpl_->finished_ =
false;
792 JAMI_DBG(
"LIBJAMI_FLAG_NO_AUTOLOAD is set, accounts will neither be loaded nor backed up");
795 no_errors = pimpl_->parseConfiguration();
796 }
catch (
const YAML::Exception&
e) {
812 pimpl_->parseConfiguration();
813 }
catch (
const YAML::Exception&
e) {
821 std::lock_guard lock(pimpl_->audioLayerMutex_);
822 pimpl_->initAudioDriver();
823 if (pimpl_->audiodriver_) {
824 auto format = pimpl_->audiodriver_->getFormat();
825 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
826 pimpl_->dtmfKey_.reset(
833 pimpl_->ioContextRunner_ = std::thread([context = pimpl_->ioContext_]() {
835 auto work = asio::make_work_guard(*context);
837 }
catch (
const std::exception&
ex) {
838 JAMI_ERR(
"Unexpected io_context thread exception: %s",
ex.what());
847 JAMI_DBG(
"LIBJAMI_FLAG_NO_AUTOLOAD is set, accounts and conversations will not be loaded");
855Manager::finish() noexcept
857 bool expected =
false;
858 if (not pimpl_->finished_.compare_exchange_strong(expected,
true))
863 upnpContext()->shutdown();
866 callFactory.forbid();
869 JAMI_DBG(
"End %zu remaining call(s)", callFactory.callCount());
870 for (
const auto& call : callFactory.getAllCalls())
874 for (
const auto& account : getAllAccounts<JamiAccount>()) {
875 if (account->getRegistrationState() == RegistrationState::INITIALIZING)
876 removeAccount(account->getAccountID(),
true);
882 unregisterAccounts();
883 accountFactory.clear();
886 std::lock_guard lock(pimpl_->audioLayerMutex_);
887 pimpl_->audiodriver_.reset();
890 JAMI_DBG(
"Stopping schedulers and worker threads");
893 pimpl_->scheduler_.stop();
894 dht::ThreadPool::io().join();
895 dht::ThreadPool::computation().join();
900 pimpl_->ice_tf_.reset();
905 if (pimpl_->sipLink_) {
906 pimpl_->sipLink_->shutdown();
907 pimpl_->sipLink_.reset();
911 pimpl_->gitTransports_.clear();
912 git_libgit2_shutdown();
914 if (!pimpl_->ioContext_->stopped()) {
915 pimpl_->ioContext_->stop();
917 if (pimpl_->ioContextRunner_.joinable())
918 pimpl_->ioContextRunner_.join();
921 gnutls_global_deinit();
930Manager::monitor(
bool continuous)
932 Logger::setMonitorLog(
true);
933 JAMI_DBG(
"############## START MONITORING ##############");
934 JAMI_DBG(
"Using PJSIP version: %s for %s", pj_get_version(), PJ_OS_NAME);
935 JAMI_DBG(
"Using GnuTLS version: %s", gnutls_check_version(
nullptr));
936 JAMI_DBG(
"Using OpenDHT version: %s", dht::version());
939#if defined(__ANDROID__)
942 = dhtnet::fileutils::readDirectory(
"/proc/" + std::to_string(getpid()) +
"/fd").size();
943 JAMI_DBG(
"Opened files: %lu", opened_files);
947 for (
const auto& call : callFactory.getAllCalls())
949 for (
const auto& account : getAllAccounts())
950 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account))
952 JAMI_DBG(
"############## END MONITORING ##############");
953 Logger::setMonitorLog(continuous);
956std::vector<std::map<std::string, std::string>>
957Manager::getConnectionList(
const std::string& accountId,
const std::string& conversationId)
959 std::vector<std::map<std::string, std::string>> connectionsList;
961 if (accountId.empty()) {
962 for (
const auto& account : getAllAccounts<JamiAccount>()) {
963 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
964 const auto& cnl = account->getConnectionList(conversationId);
965 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
969 auto account = getAccount(accountId);
971 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
972 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
973 const auto& cnl = acc->getConnectionList(conversationId);
974 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
980 return connectionsList;
983std::vector<std::map<std::string, std::string>>
984Manager::getChannelList(
const std::string& accountId,
const std::string& connectionId)
988 std::vector<std::map<std::string, std::string>> channelsList;
990 if (accountId.empty()) {
991 for (
const auto& account : getAllAccounts<JamiAccount>()) {
992 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
994 const auto& cnl = account->getChannelList(connectionId);
995 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1003 auto account = getAccount(accountId);
1005 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
1006 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
1007 const auto& cnl = acc->getChannelList(connectionId);
1008 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1014 return channelsList;
1018Manager::isCurrentCall(
const Call& call)
const
1020 return pimpl_->currentCall_ == call.
getCallId();
1024Manager::hasCurrentCall()
const
1026 for (
const auto& call : callFactory.getAllCalls()) {
1033std::shared_ptr<Call>
1034Manager::getCurrentCall()
const
1036 return getCallFromCallID(pimpl_->currentCall_);
1040Manager::getCurrentCallId()
const
1042 return pimpl_->currentCall_;
1046Manager::unregisterAccounts()
1048 for (
const auto& account : getAllAccounts()) {
1049 if (account->isEnabled()) {
1050 account->doUnregister(
true);
1061Manager::outgoingCall(
const std::string& account_id,
1062 const std::string& to,
1063 const std::vector<libjami::MediaMap>& mediaList)
1065 JAMI_DBG() <<
"Attempt outgoing call to '" << to <<
"'"
1066 <<
" with account '" << account_id <<
"'";
1068 std::shared_ptr<Call> call;
1071 call = newOutgoingCall(
trim(to), account_id, mediaList);
1072 }
catch (
const std::exception& e) {
1082 pimpl_->switchCall(call->getCallId());
1084 return call->getCallId();
1089Manager::answerCall(
const std::string& accountId,
1090 const std::string& callId,
1091 const std::vector<libjami::MediaMap>& mediaList)
1093 if (
auto account = getAccount(accountId)) {
1094 if (
auto call = account->getCall(callId)) {
1095 return answerCall(*call, mediaList);
1102Manager::answerCall(
Call& call,
const std::vector<libjami::MediaMap>& mediaList)
1113 pimpl_->removeWaitingCall(call.
getCallId());
1117 }
catch (
const std::runtime_error& e) {
1131 if (audioPreference.getIsAlwaysRecording()) {
1133 emitSignal<libjami::CallSignal::RecordPlaybackFilepath>(call.
getCallId(), call.
getPath());
1134 emitSignal<libjami::CallSignal::RecordingStateChanged>(call.
getCallId(), recResult);
1141Manager::hangupCall(
const std::string& accountId,
const std::string& callId)
1143 auto account = getAccount(accountId);
1148 pimpl_->removeWaitingCall(callId);
1151 auto call = account->getCall(callId);
1153 JAMI_WARN(
"Unable to hang up nonexistent call %s", callId.c_str());
1160 if (call->isConferenceParticipant()) {
1161 removeParticipant(*call);
1164 if (isCurrentCall(*call))
1165 pimpl_->unsetCurrentCall();
1179Manager::hangupConference(
const std::string& accountId,
const std::string& confId)
1181 if (
auto account = getAccount(accountId)) {
1182 if (
auto conference = account->getConference(confId)) {
1183 return pimpl_->hangupConference(*conference);
1193Manager::onHoldCall(
const std::string&,
const std::string& callId)
1199 std::string current_callId(getCurrentCallId());
1201 if (
auto call = getCallFromCallID(callId)) {
1203 result = call->
onhold([=](
bool ok) {
1205 JAMI_ERR(
"Hold failed for call %s", callId.c_str());
1210 pimpl_->removeWaitingCall(callId);
1214 if (current_callId == callId)
1215 pimpl_->unsetCurrentCall();
1222 JAMI_DBG(
"CallID %s doesn't exist in call onHold", callId.c_str());
1231Manager::offHoldCall(
const std::string&,
const std::string& callId)
1237 std::shared_ptr<Call> call = getCallFromCallID(callId);
1242 result = call->offhold([=](
bool ok) {
1244 JAMI_ERR(
"offHold failed for call %s", callId.c_str());
1248 if (
auto conf = call->getConference())
1251 pimpl_->switchCall(call->getCallId());
1265Manager::transferCall(
const std::string& accountId,
const std::string& callId,
const std::string& to)
1267 auto account = getAccount(accountId);
1270 if (
auto call = account->getCall(callId)) {
1272 removeParticipant(*call);
1278 pimpl_->removeWaitingCall(callId);
1284Manager::transferFailed()
1286 emitSignal<libjami::CallSignal::TransferFailed>();
1290Manager::transferSucceeded()
1292 emitSignal<libjami::CallSignal::TransferSucceeded>();
1297Manager::refuseCall(
const std::string& accountId,
const std::string&
id)
1299 if (
auto account = getAccount(accountId)) {
1300 if (
auto call = account->getCall(
id)) {
1303 pimpl_->removeWaitingCall(
id);
1312Manager::holdConference(
const std::string& accountId,
const std::string& confId)
1314 JAMI_INFO(
"Hold conference %s", confId.c_str());
1316 if (
const auto account = getAccount(accountId)) {
1317 if (
auto conf = account->getConference(confId)) {
1319 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId,
1329Manager::unHoldConference(
const std::string& accountId,
const std::string& confId)
1331 JAMI_DBG(
"[conf:%s] Unholding conference", confId.c_str());
1333 if (
const auto account = getAccount(accountId)) {
1334 if (
auto conf = account->getConference(confId)) {
1337 if (conf->
getState() == Conference::State::HOLD) {
1339 offHoldCall(accountId, item);
1341 pimpl_->switchCall(confId);
1342 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1343 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId,
1347 }
else if (conf->
getState() == Conference::State::ACTIVE_DETACHED) {
1348 pimpl_->addMainParticipant(*conf);
1356Manager::addSubCall(
const std::string& accountId,
1357 const std::string& callId,
1358 const std::string& account2Id,
1359 const std::string& conferenceId)
1361 auto account = getAccount(accountId);
1362 auto account2 = getAccount(account2Id);
1363 if (account && account2) {
1364 auto call = account->getCall(callId);
1365 auto conf = account2->getConference(conferenceId);
1369 if (callConf != conf)
1370 return addSubCall(*call, *conf);
1381 pimpl_->bindCallToConference(call, conference);
1384 if (conference.
getState() == Conference::State::ACTIVE_DETACHED) {
1391 pimpl_->unsetCurrentCall();
1392 pimpl_->addMainParticipant(conference);
1393 pimpl_->switchCall(conference.
getConfId());
1403 emitSignal<libjami::CallSignal::ConferenceChanged>(conf.
getAccountId(),
1415 if (subcalls.empty()) {
1417 account->removeConference(conference.
getConfId());
1419 for (
const auto& callId : subcalls) {
1420 if (
auto call = base_.getCallFromCallID(callId))
1428Manager::addMainParticipant(
const std::string& accountId,
const std::string& conferenceId)
1430 JAMI_INFO(
"Add main participant to conference %s", conferenceId.c_str());
1432 if (
auto account = getAccount(accountId)) {
1433 if (
auto conf = account->getConference(conferenceId)) {
1434 pimpl_->addMainParticipant(*conf);
1435 JAMI_DBG(
"Successfully added main participant to conference %s", conferenceId.c_str());
1438 JAMI_WARN(
"Failed to add main participant to conference %s", conferenceId.c_str());
1443std::shared_ptr<Call>
1444Manager::getCallFromCallID(
const std::string& callID)
const
1446 return callFactory.getCall(callID);
1450Manager::joinParticipant(
const std::string& accountId,
1451 const std::string& callId1,
1452 const std::string& account2Id,
1453 const std::string& callId2,
1456 JAMI_INFO(
"JoinParticipant(%s, %s, %i)", callId1.c_str(), callId2.c_str(), attached);
1457 auto account = getAccount(accountId);
1458 auto account2 = getAccount(account2Id);
1459 if (not account or not account2) {
1463 JAMI_INFO(
"Creating conference for participants %s and %s. Attach host [%s]",
1466 attached ?
"YES" :
"NO");
1468 if (callId1 == callId2) {
1469 JAMI_ERR(
"Unable to join participant %s to itself", callId1.c_str());
1474 auto call1 = account->getCall(callId1);
1476 JAMI_ERR(
"Unable to find call %s", callId1.c_str());
1481 auto call2 = account2->getCall(callId2);
1483 JAMI_ERR(
"Unable to find call %s", callId2.c_str());
1487 auto mediaAttr = call1->getMediaAttributeList();
1488 if (mediaAttr.empty())
1489 mediaAttr = call2->getMediaAttributeList();
1490 auto conf = std::make_shared<Conference>(account);
1492 account->attach(conf);
1493 emitSignal<libjami::CallSignal::ConferenceCreated>(account->getAccountID(),
"", conf->
getConfId());
1496 pimpl_->bindCallToConference(*call1, *conf);
1497 pimpl_->bindCallToConference(*call2, *conf);
1502 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1506 emitSignal<libjami::CallSignal::ConferenceChanged>(account->getAccountID(),
1514Manager::createConfFromParticipantList(
const std::string& accountId,
1515 const std::vector<std::string>& participantList)
1517 auto account = getAccount(accountId);
1524 if (participantList.size() <= 1) {
1525 JAMI_ERR(
"Participant number must be greater than or equal to 2");
1529 auto conf = std::make_shared<Conference>(account);
1532 unsigned successCounter = 0;
1533 for (
const auto& numberaccount : participantList) {
1534 std::string tostr(numberaccount.substr(0, numberaccount.find(
',')));
1535 std::string account(numberaccount.substr(numberaccount.find(
',') + 1, numberaccount.size()));
1537 pimpl_->unsetCurrentCall();
1540 auto callId = outgoingCall(account, tostr, {});
1550 if (successCounter >= 2) {
1551 account->attach(conf);
1552 emitSignal<libjami::CallSignal::ConferenceCreated>(accountId,
"", conf->
getConfId());
1557Manager::detachHost(
const std::shared_ptr<Conference>& conf)
1562 JAMI_LOG(
"Detach local participant from conference {}", conf->getConfId());
1564 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(),
1566 conf->getStateStr());
1567 pimpl_->unsetCurrentCall();
1572Manager::detachParticipant(
const std::string& callId)
1574 JAMI_DBG(
"Detach participant %s", callId.c_str());
1576 auto call = getCallFromCallID(callId);
1578 JAMI_ERR(
"Unable to find call %s", callId.c_str());
1586 removeParticipant(*call);
1591Manager::removeParticipant(
Call& call)
1597 JAMI_ERR(
"No conference, unable to remove participant");
1605 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(),
1607 conf->getStateStr());
1609 pimpl_->processRemainingParticipants(*conf);
1613Manager::joinConference(
const std::string& accountId,
1614 const std::string& confId1,
1615 const std::string& account2Id,
1616 const std::string& confId2)
1618 auto account = getAccount(accountId);
1619 auto account2 = getAccount(account2Id);
1621 JAMI_ERR(
"Unable to find account: %s", accountId.c_str());
1625 JAMI_ERR(
"Unable to find account: %s", account2Id.c_str());
1629 auto conf = account->getConference(confId1);
1631 JAMI_ERR(
"Invalid conference ID: %s", confId1.c_str());
1635 auto conf2 = account2->getConference(confId2);
1637 JAMI_ERR(
"Invalid conference ID: %s", confId2.c_str());
1641 CallIdSet subcalls(conf->getSubCalls());
1643 std::vector<std::shared_ptr<Call>> calls;
1644 calls.reserve(subcalls.size());
1648 for (
const auto& callId : subcalls) {
1650 if (
auto call = account->getCall(callId)) {
1651 conf->removeSubCall(callId);
1653 calls.emplace_back(std::move(call));
1655 JAMI_ERROR(
"Unable to find call {}", callId);
1659 account->removeConference(confId1);
1661 for (
const auto& c : calls)
1662 addSubCall(*c, *conf2);
1673 JAMI_LOG(
"Add audio to call {}", callId);
1677 for (
const auto& media : medias) {
1678 JAMI_DEBUG(
"[call:{}] Attach audio", media.first);
1679 getRingBufferPool().bindRingBuffers(media.first, RingBufferPool::DEFAULT_ID);
1682 call.
audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1684 std::lock_guard lock(pimpl_->audioLayerMutex_);
1685 if (!pimpl_->audiodriver_) {
1689 pimpl_->audiodriver_->flushUrgent();
1690 getRingBufferPool().flushAllBuffers();
1698 for (
const auto& media : medias) {
1699 JAMI_DEBUG(
"[call:{}] Remove local audio {}", callId, media.first);
1700 getRingBufferPool().unBindAll(media.first);
1707 return pimpl_->scheduler_;
1710std::shared_ptr<asio::io_context>
1711Manager::ioContext()
const
1713 return pimpl_->ioContext_;
1716std::shared_ptr<dhtnet::upnp::UPnPContext>
1717Manager::upnpContext()
const
1719 return pimpl_->upnpContext_;
1722std::shared_ptr<Task>
1723Manager::scheduleTask(std::function<
void()>&& task,
1724 std::chrono::steady_clock::time_point when,
1725 const char* filename,
1728 return pimpl_->scheduler_.
schedule(std::move(task), when, filename, linum);
1731std::shared_ptr<Task>
1732Manager::scheduleTaskIn(std::function<
void()>&& task,
1733 std::chrono::steady_clock::duration timeout,
1734 const char* filename,
1737 return pimpl_->scheduler_.scheduleIn(std::move(task), timeout, filename, linum);
1741Manager::saveConfig(
const std::shared_ptr<Account>& acc)
1743 if (
auto ringAcc = std::dynamic_pointer_cast<JamiAccount>(acc))
1744 ringAcc->saveConfig();
1750Manager::saveConfig()
1752 JAMI_LOG(
"Saving configuration to '{}'", pimpl_->path_);
1754 if (pimpl_->audiodriver_) {
1755 audioPreference.setVolumemic(pimpl_->audiodriver_->getCaptureGain());
1756 audioPreference.setVolumespkr(pimpl_->audiodriver_->getPlaybackGain());
1757 audioPreference.setCaptureMuted(pimpl_->audiodriver_->isCaptureMuted());
1758 audioPreference.setPlaybackMuted(pimpl_->audiodriver_->isPlaybackMuted());
1765 out << YAML::BeginMap << YAML::Key <<
"accounts";
1766 out << YAML::Value << YAML::BeginSeq;
1768 for (
const auto& account : accountFactory.getAllAccounts()) {
1769 if (
auto jamiAccount = std::dynamic_pointer_cast<JamiAccount>(account)) {
1770 auto accountConfig = jamiAccount->getPath() /
"config.yml";
1771 if (not std::filesystem::is_regular_file(accountConfig)) {
1772 saveConfig(jamiAccount);
1775 account->config().serialize(out);
1778 out << YAML::EndSeq;
1781 preferences.verifyAccountOrder(getAccountList());
1782 preferences.serialize(out);
1783 voipPreferences.serialize(out);
1784 audioPreference.serialize(out);
1786 videoPreferences.serialize(out);
1789 pluginPreferences.serialize(out);
1792 std::lock_guard lock(dhtnet::fileutils::getFileLock(pimpl_->path_));
1793 std::ofstream fout(pimpl_->path_);
1794 fout.write(out.c_str(), out.size());
1795 }
catch (
const YAML::Exception& e) {
1797 }
catch (
const std::runtime_error& e) {
1804Manager::playDtmf(
char code)
1808 if (not voipPreferences.getPlayDtmf()) {
1809 JAMI_DBG(
"Do not have to play a tone…");
1814 int pulselen = voipPreferences.getPulseLength();
1816 if (pulselen == 0) {
1817 JAMI_DBG(
"Pulse length is not set…");
1821 std::lock_guard lock(pimpl_->audioLayerMutex_);
1824 if (not pimpl_->audiodriver_ or not pimpl_->dtmfKey_) {
1829 std::shared_ptr<AudioDeviceGuard> audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1830 if (not pimpl_->audiodriver_->waitForStart(std::chrono::seconds(1))) {
1831 JAMI_ERR(
"Failed to start audio layer…");
1839 unsigned size = (unsigned) ((pulselen * (
long) pimpl_->audiodriver_->getSampleRate()) / 1000ul);
1840 if (!pimpl_->dtmfBuf_ or pimpl_->dtmfBuf_->getFrameSize() != size)
1841 pimpl_->dtmfBuf_ = std::make_shared<AudioFrame>(pimpl_->audiodriver_->getFormat(), size);
1844 pimpl_->dtmfKey_->startTone(code);
1847 if (pimpl_->dtmfKey_->generateDTMF(pimpl_->dtmfBuf_->pointer())) {
1853 pimpl_->audiodriver_->putUrgent(pimpl_->dtmfBuf_);
1856 scheduler().scheduleIn([audioGuard] {
JAMI_WARN(
"End of dtmf"); },
1857 std::chrono::milliseconds(pulselen));
1864Manager::incomingCallsWaiting()
1866 std::lock_guard m(pimpl_->waitingCallsMutex_);
1867 return not pimpl_->waitingCalls_.empty();
1871Manager::incomingCall(
const std::string& accountId,
Call& call)
1873 if (not accountId.empty()) {
1874 pimpl_->stripSipPrefix(call);
1877 auto const& account = getAccount(accountId);
1879 JAMI_ERROR(
"Incoming call {} on unknown account {}",
1886 pimpl_->processIncomingCall(accountId, call);
1890Manager::incomingMessage(
const std::string& accountId,
1891 const std::string& callId,
1892 const std::string& from,
1893 const std::map<std::string, std::string>& messages)
1895 auto account = getAccount(accountId);
1899 if (
auto call = account->getCall(callId)) {
1902 JAMI_DBG(
"Is a conference, send incoming message to everyone");
1905 bool sendToOtherParicipants =
true;
1906 for (
auto& message : messages) {
1907 if (message.first.find(
"x-ring/ring.profile.vcard") != std::string::npos) {
1908 sendToOtherParicipants =
false;
1911 if (sendToOtherParicipants) {
1912 pimpl_->sendTextMessageToConference(*conf, messages, from);
1916 emitSignal<libjami::CallSignal::IncomingMessage>(accountId,
1921 JAMI_ERR(
"No conference associated to ID %s", callId.c_str());
1924 emitSignal<libjami::CallSignal::IncomingMessage>(accountId, callId, from, messages);
1930Manager::sendCallTextMessage(
const std::string& accountId,
1931 const std::string& callID,
1932 const std::map<std::string, std::string>& messages,
1933 const std::string& from,
1936 auto account = getAccount(accountId);
1941 if (
auto conf = account->getConference(callID)) {
1942 JAMI_DBG(
"Is a conference, send instant message to everyone");
1943 pimpl_->sendTextMessageToConference(*conf, messages, from);
1944 }
else if (
auto call = account->getCall(callID)) {
1947 JAMI_DBG(
"Call is participant in a conference, send instant message to everyone");
1948 pimpl_->sendTextMessageToConference(*conf, messages, from);
1950 JAMI_ERR(
"No conference associated to call ID %s", callID.c_str());
1956 JAMI_ERR(
"Failed to send message to call %s: %s",
1962 JAMI_ERR(
"Failed to send message to %s: nonexistent call ID", callID.c_str());
1968Manager::peerAnsweredCall(
Call& call)
1971 JAMI_DBG(
"[call:%s] Peer answered", callId.c_str());
1974 if (isCurrentCall(call))
1979 if (pimpl_->audiodriver_) {
1980 std::lock_guard lock(pimpl_->audioLayerMutex_);
1981 getRingBufferPool().flushAllBuffers();
1982 pimpl_->audiodriver_->flushUrgent();
1985 if (audioPreference.getIsAlwaysRecording()) {
1987 emitSignal<libjami::CallSignal::RecordPlaybackFilepath>(callId, call.
getPath());
1988 emitSignal<libjami::CallSignal::RecordingStateChanged>(callId, result);
1994Manager::peerRingingCall(
Call& call)
1997 if (!hasCurrentCall())
2006 JAMI_LOG(
"[call:{}] Peer hung up", callId);
2009 removeParticipant(call);
2010 }
else if (isCurrentCall(call)) {
2012 pimpl_->unsetCurrentCall();
2017 pimpl_->removeWaitingCall(callId);
2018 if (not incomingCallsWaiting())
2030 if (isCurrentCall(call)) {
2031 pimpl_->unsetCurrentCall();
2034 pimpl_->removeWaitingCall(call.
getCallId());
2035 if (not incomingCallsWaiting())
2045 call.
isSubcall() ?
"Sub-call" :
"Parent call");
2047 if (isCurrentCall(call)) {
2048 pimpl_->unsetCurrentCall();
2054 removeParticipant(call);
2057 pimpl_->removeWaitingCall(call.
getCallId());
2058 if (not call.
isSubcall() && not incomingCallsWaiting())
2069 if (not voipPreferences.getPlayTones())
2072 pimpl_->toneCtrl_.stop();
2073 pimpl_->toneDeviceGuard_.reset();
2082 pimpl_->playATone(Tone::ToneId::DIALTONE);
2089Manager::playToneWithMessage()
2091 pimpl_->playATone(Tone::ToneId::CONGESTION);
2098Manager::congestion()
2100 pimpl_->playATone(Tone::ToneId::CONGESTION);
2109 pimpl_->playATone(Tone::ToneId::RINGTONE);
2116Manager::playRingtone(
const std::string& accountID)
2118 const auto account = getAccount(accountID);
2120 JAMI_WARN(
"Invalid account in ringtone");
2124 if (!account->getRingtoneEnabled()) {
2130 std::lock_guard lock(pimpl_->audioLayerMutex_);
2132 if (not pimpl_->audiodriver_) {
2133 JAMI_ERR(
"No audio layer in ringtone");
2137 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2138 pimpl_->toneDeviceGuard_ = startAudioStream(AudioDeviceType::RINGTONE);
2139 auto format = pimpl_->audiodriver_->getFormat();
2140 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2143 if (not pimpl_->toneCtrl_.setAudioFile(account->getRingtonePath().string()))
2147std::shared_ptr<AudioLoop>
2148Manager::getTelephoneTone()
2150 return pimpl_->toneCtrl_.getTelephoneTone();
2153std::shared_ptr<AudioLoop>
2154Manager::getTelephoneFile()
2156 return pimpl_->toneCtrl_.getTelephoneFile();
2163Manager::setAudioPlugin(
const std::string& audioPlugin)
2166 std::lock_guard lock(pimpl_->audioLayerMutex_);
2167 audioPreference.setAlsaPlugin(audioPlugin);
2168 pimpl_->audiodriver_.reset();
2169 pimpl_->initAudioDriver();
2181 std::lock_guard lock(pimpl_->audioLayerMutex_);
2183 if (not pimpl_->audiodriver_) {
2184 JAMI_ERR(
"Uninitialized audio driver");
2187 if (pimpl_->getCurrentDeviceIndex(type) == index) {
2188 JAMI_WARN(
"Audio device already selected, doing nothing.");
2192 pimpl_->audiodriver_->updatePreference(audioPreference, index, type);
2195 pimpl_->audiodriver_.reset();
2196 pimpl_->initAudioDriver();
2203std::vector<std::string>
2204Manager::getAudioOutputDeviceList()
2206 std::lock_guard lock(pimpl_->audioLayerMutex_);
2208 if (not pimpl_->audiodriver_) {
2209 JAMI_ERR(
"Uninitialized audio layer");
2213 return pimpl_->audiodriver_->getPlaybackDeviceList();
2219std::vector<std::string>
2220Manager::getAudioInputDeviceList()
2222 std::lock_guard lock(pimpl_->audioLayerMutex_);
2224 if (not pimpl_->audiodriver_) {
2225 JAMI_ERR(
"Uninitialized audio layer");
2229 return pimpl_->audiodriver_->getCaptureDeviceList();
2235std::vector<std::string>
2236Manager::getCurrentAudioDevicesIndex()
2238 std::lock_guard lock(pimpl_->audioLayerMutex_);
2239 if (not pimpl_->audiodriver_) {
2240 JAMI_ERR(
"Uninitialized audio layer");
2244 return {std::to_string(pimpl_->audiodriver_->getIndexPlayback()),
2245 std::to_string(pimpl_->audiodriver_->getIndexCapture()),
2246 std::to_string(pimpl_->audiodriver_->getIndexRingtone())};
2250Manager::startAudio()
2252 if (!pimpl_->audiodriver_)
2253 pimpl_->audiodriver_.reset(pimpl_->base_.audioPreference.createAudioLayer());
2254 constexpr std::array<AudioDeviceType, 3> TYPES {AudioDeviceType::CAPTURE,
2255 AudioDeviceType::PLAYBACK,
2256 AudioDeviceType::RINGTONE};
2257 for (
const auto& type : TYPES)
2258 if (pimpl_->audioStreamUsers_[(
unsigned) type])
2259 pimpl_->audiodriver_->startStream(type);
2267 if (streamId >= manager_.pimpl_->audioStreamUsers_.size())
2268 throw std::invalid_argument(
"Invalid audio device type");
2269 if (manager_.pimpl_->audioStreamUsers_[streamId]++ == 0) {
2271 layer->startStream(type);
2278 if (--manager_.pimpl_->audioStreamUsers_[streamId] == 0) {
2280 layer->stopStream(type_);
2300 bool result =
false;
2302 std::shared_ptr<Recordable>
rec;
2306 }
else if (
auto call =
account->getCall(
id)) {
2310 JAMI_ERR(
"Unable to find recordable instance %s",
id.
c_str());
2313 result =
rec->toggleRecording();
2326 std::lock_guard lock(pimpl_->audioLayerMutex_);
2328 if (
not pimpl_->audiodriver_) {
2329 JAMI_ERR(
"No audio layer in start recorded file playback");
2333 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2335 auto format = pimpl_->audiodriver_->getFormat();
2336 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2339 return pimpl_->toneCtrl_.setAudioFile(
filepath);
2345 pimpl_->toneCtrl_.seek(
value);
2351 JAMI_DBG(
"Stop recorded file playback");
2353 pimpl_->toneCtrl_.stopAudioFile();
2354 pimpl_->toneDeviceGuard_.reset();
2389 std::lock_guard lock(pimpl_->audioLayerMutex_);
2391 if (
not pimpl_->audiodriver_)
2395 JAMI_DBG(
"Audio manager chosen already in use. No changes made.");
2401 std::lock_guard lock(pimpl_->audioLayerMutex_);
2403 pimpl_->audiodriver_.reset();
2404 pimpl_->initAudioDriver();
2422 std::lock_guard lock(pimpl_->audioLayerMutex_);
2424 if (
not pimpl_->audiodriver_) {
2425 JAMI_ERR(
"Uninitialized audio layer");
2435 std::lock_guard lock(pimpl_->audioLayerMutex_);
2437 if (
not pimpl_->audiodriver_) {
2438 JAMI_ERR(
"Uninitialized audio layer");
2485 for (
const auto& type :
TYPES)
2536 JAMI_DEBUG(
"Incoming call {} on account {} with {} media",
2546 if (
not base_.hasCurrentCall()) {
2548#if !(defined(TARGET_OS_IOS) && TARGET_OS_IOS)
2550 base_.playRingtone(accountId);
2553 if (
account->isDenySecondCallEnabled()) {
2561 if (
account->isRendezVous()) {
2563 base_.answerCall(*incomCall);
2565 for (const auto& callId : account->getCallList()) {
2566 if (auto call = account->getCall(callId)) {
2567 if (call->getState() != Call::CallState::ACTIVE)
2569 if (call != incomCall) {
2570 if (auto conf = call->getConference()) {
2571 base_.addSubCall(*incomCall, *conf);
2573 base_.joinParticipant(account->getAccountID(),
2574 incomCall->getCallId(),
2575 account->getAccountID(),
2585 auto conf = std::make_shared<Conference>(
account);
2595 conf->getStateStr());
2597 }
else if (autoAnswer_ ||
account->isAutoAnswerEnabled()) {
2598 dht::ThreadPool::io().run(
2621 auto& mgr = Manager::instance();
2622 mgr.answerCall(*incomCall);
2623 mgr.hangupCall(accountId, currentCallID);
2649 pimpl_->ringbufferpool_->setInternalAudioFormat(format);
2659 JAMI_LOG(
"Set accounts order: {}", order);
2665std::vector<std::string>
2669 std::vector<std::string> v;
2672 v.emplace_back(
account->getAccountID());
2678std::map<std::string, std::string>
2684 return account->getAccountDetails();
2686 JAMI_ERR(
"Unable to get account details on a nonexistent accountID %s",
accountID.c_str());
2692std::map<std::string, std::string>
2698 return account->getVolatileAccountDetails();
2700 JAMI_ERR(
"Unable to get volatile account details on a nonexistent accountID %s",
2708 const std::map<std::string, std::string>&
details)
2739 return dht::crypto::getDerivedRandomEngine(rand_);
2770 JAMI_ERROR(
"Unknown {:s} param when calling addAccount(): {:s}",
2793 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(
remAccount)) {
2816std::vector<std::string_view>
2837 }
catch (
const YAML::Exception&
e) {
2838 JAMI_ERR(
"Preferences node unserialize YAML exception: %s",
e.what());
2840 }
catch (
const std::exception&
e) {
2841 JAMI_ERR(
"Preferences node unserialize standard exception: %s",
e.what());
2844 JAMI_ERR(
"Preferences node unserialize unknown exception");
2860 std::condition_variable cv;
2863 std::unique_lock
l(lock);
2864 for (
const auto&
dir :
dirs) {
2869 dht::ThreadPool::computation().run(
2871 if (std::filesystem::is_regular_file(
configFile)) {
2875 auto config =
a->buildConfig();
2877 a->setConfig(std::move(config));
2879 }
catch (
const std::exception&
e) {
2880 JAMI_ERR(
"Unable to import account %s: %s",
dir.c_str(),
e.what());
2883 std::lock_guard
l(lock);
2899std::vector<std::string>
2902 std::vector<std::string>
results;
2926 acc->setEnabled(enable);
2929 if (acc->isEnabled()) {
2932 acc->doUnregister();
2937 const std::string& to,
2938 const std::map<std::string, std::string>& payloads,
2947 auto cm = std::make_shared<JamiMessage>(
accountID, to,
false, payloads, fromPlugin);
2952 return acc->sendTextMessage(to,
"", payloads, 0,
onlyConnected);
2953 }
catch (
const std::exception&
e) {
2954 JAMI_ERR(
"Exception during text message sending: %s",
e.what());
2963 JAMI_ERROR(
"Deprecated method. Please use status from message");
2970 JAMI_ERROR(
"Deprecated method. Please use status from message");
2978 if (!acc || acc->isActive() == active)
2980 acc->setActive(active);
2981 if (acc->isEnabled()) {
2985 acc->doUnregister(shutdownConnections);
2989 accountID, acc->getVolatileAccountDetails());
2995 const std::string&
convId)
3010 account->enableAutoLoadConversations(
false);
3012 auto config =
account->buildConfig();
3014 account->setConfig(std::move(config));
3016 }
catch (
const std::runtime_error&
e) {
3017 JAMI_WARN(
"Failed to load account: %s",
e.what());
3023 JAMI_WARN(
"Unable to load account %s", accountId.c_str());
3027 if (
auto jamiAcc = std::dynamic_pointer_cast<JamiAccount>(
account)) {
3032 if (
auto convModule =
jamiAcc->convModule()) {
3033 convModule->reloadRequests();
3035 convModule->loadConversations();
3036 }
else if (!
convId.empty()) {
3043std::shared_ptr<AudioLayer>
3046 return pimpl_->audiodriver_;
3049std::shared_ptr<Call>
3051 const std::string& accountId,
3052 const std::vector<libjami::MediaMap>&
mediaList)
3056 JAMI_WARN(
"No account matches ID %s", accountId.c_str());
3061 JAMI_WARN(
"Account %s is unusable", accountId.c_str());
3069std::shared_ptr<video::SinkClient>
3070Manager::createSinkClient(
const std::string&
id,
bool mixer)
3072 const auto&
iter = pimpl_->sinkMap_.find(
id);
3073 if (
iter != std::end(pimpl_->sinkMap_)) {
3074 if (
auto sink =
iter->second.lock())
3076 pimpl_->sinkMap_.erase(
iter);
3079 auto sink = std::make_shared<video::SinkClient>(
id,
mixer);
3080 pimpl_->sinkMap_.emplace(
id,
sink);
3085Manager::createSinkClients(
3086 const std::string& callId,
3087 const ConfInfo& infos,
3088 const std::vector<std::shared_ptr<video::VideoFrameActiveWriter>>&
videoStreams,
3089 std::map<std::string, std::shared_ptr<video::SinkClient>>&
sinksMap,
3090 const std::string& accountId)
3092 std::lock_guard
lk(pimpl_->sinksMutex_);
3098 if (sinkId.empty()) {
3144std::shared_ptr<video::SinkClient>
3145Manager::getSinkClient(
const std::string&
id)
3147 const auto&
iter = pimpl_->sinkMap_.find(
id);
3148 if (
iter != std::end(pimpl_->sinkMap_))
3149 if (
auto sink =
iter->second.lock())
3158 return *pimpl_->ringbufferpool_;
3167const std::shared_ptr<dhtnet::IceTransportFactory>&
3170 return pimpl_->ice_tf_;
3176 return pimpl_->videoManager_.get();
3179std::vector<libjami::Message>
3190 return *pimpl_->sipLink_;
3195Manager::getJamiPluginManager()
const
3197 return *pimpl_->jami_plugin_manager;
3201std::shared_ptr<dhtnet::ChannelSocket>
3203 std::string_view deviceId,
3204 std::string_view conversationId)
3207 if (
auto convModule = acc->convModule(
true))
3208 return convModule->gitSocket(deviceId, conversationId);
3212std::map<std::string, std::string>
3216 return acc->getNearbyPeers();
3225 JAMI_ERR(
"Failed to change default moderator, account %s not found",
accountID.c_str());
3230 acc->addDefaultModerator(
peerURI);
3232 acc->removeDefaultModerator(
peerURI);
3236std::vector<std::string>
3241 JAMI_ERR(
"Failed to get default moderators, account %s not found",
accountID.c_str());
3245 auto set = acc->getDefaultModerators();
3246 return std::vector<std::string>(set.begin(), set.end());
3262 JAMI_ERR(
"Failed to get local moderators, account %s not found",
accountID.c_str());
3265 return acc->isLocalModeratorsEnabled();
3280 JAMI_ERR(
"Failed to get all moderators, account %s not found",
accountID.c_str());
3283 return acc->isAllModerators();
3289 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3290 pimpl_->gitTransports_[
tr] = std::move(
sub);
3296 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3297 pimpl_->gitTransports_.erase(
tr);
3300dhtnet::tls::CertificateStore&
3306 throw std::runtime_error(
"No account found");
Interface to protocol account (ex: SIPAccount) It can be enable on loading or activate after.
Account specific keys/constants that must be shared in daemon and clients.
static const std::string_view DEFAULT_ACCOUNT_TYPE
bool hasAccount(std::string_view id) const
void removeAccount(Account &account)
std::shared_ptr< Account > createAccount(std::string_view accountType, const std::string &id)
const std::string & getAudioApi() const
bool getIsAlwaysRecording() const
AudioLayer * createAudioLayer()
void setIsAlwaysRecording(bool rec)
void setNoiseReduce(const std::string &enabled)
void setAudioApi(const std::string &api)
bool isAGCEnabled() const
void setAGCState(bool enabled)
void unserialize(const YAML::Node &in) override
const std::string & getNoiseReduce() const
const std::string & getAlsaPlugin() const
std::vector< std::shared_ptr< Call > > getAllCalls() const
Return all calls.
virtual bool onhold(OnReadyCb &&cb)=0
Put a call on hold.
const std::string & getCallId() const
Return a reference on the call id.
virtual void transfer(const std::string &to)=0
Transfer a call to specified URI.
virtual void sendTextMessage(const std::map< std::string, std::string > &messages, const std::string &from)=0
Send a message to a call identified by its callid.
virtual void answer()=0
Answer the call.
std::string getStateStr() const
virtual void monitor() const =0
bool isConferenceParticipant() const
ConnectionState getConnectionState() const
Get the connection state of the call (protected by mutex)
std::shared_ptr< Conference > getConference() const
Return a reference on the conference id.
bool isSubcall() const
Return true if this call instance is a subcall (internal call for multi-device handling)
virtual void refuse()=0
Refuse incoming call.
virtual void peerHungup()
Peer has hung up a call.
std::string getAccountId() const
virtual bool toggleRecording()
This method must be implemented for this interface as calls and conferences have different behavior.
std::unique_ptr< AudioDeviceGuard > audioGuard
std::weak_ptr< Account > getAccount() const
virtual std::map< std::string, bool > getAudioStreams() const =0
void attachHost(const std::vector< libjami::MediaMap > &mediaList={})
Attach host.
std::string getAccountId() const
const std::string & getConfId() const
Return the conference id.
void detachHost()
Detach local audio/video from the conference.
void setState(State state)
Set conference state.
void addSubCall(const std::string &callId)
Add a new subcall to the conference.
std::shared_ptr< Account > getAccount() const
static constexpr const char * getStateStr(State state)
Return a string description of the conference state.
CallIdSet getSubCalls() const
Get the participant list for this conference.
State getState() const
Return the current conference state.
Ring Account is build on top of SIPAccountBase and uses DHT to handle call connectivity.
static constexpr auto ACCOUNT_TYPE
This class provides an interface to functions exposed to the Plugin System interface for lrc and clie...
Level-driven logging class that support printf and C++ stream logging fashions.
Manager (controller) of daemon.
void setRingingTimeout(int timeout)
Set ringing timeout (number of seconds after which a call will enter BUSY state if not answered).
std::map< std::string, std::string > getAccountDetails(const std::string &accountID) const
Retrieve details about a given account.
void enableLocalModerators(const std::string &accountID, bool state)
void loadAccountAndConversation(const std::string &accountId, bool loadAll, const std::string &convId)
std::vector< std::shared_ptr< T > > getAllAccounts() const
Get a list of account pointers of type T (baseclass Account)
std::vector< std::string > getDefaultModerators(const std::string &accountID)
std::vector< std::string > getAccountList() const
Get account list.
bool isLocalModeratorsEnabled(const std::string &accountID)
void setAccountsOrder(const std::string &order)
Set the account order in the config file.
bool startRecordedFilePlayback(const std::string &)
Start playback fo a recorded file if and only if audio layer is not already started.
void setAllModerators(const std::string &accountID, bool allModerators)
std::vector< std::string_view > loadAccountOrder() const
Load the accounts order set by the user from the jamirc config file.
void setAutoAnswer(bool enable)
void insertGitTransport(git_smart_subtransport *tr, std::unique_ptr< P2PSubTransport > &&sub)
std::shared_ptr< Call > newOutgoingCall(std::string_view toUrl, const std::string &accountId, const std::vector< libjami::MediaMap > &mediaList)
Create a new outgoing call.
static LIBJAMI_TEST_EXPORT Manager & instance()
void setAccountActive(const std::string &accountID, bool active, bool shutdownConnections)
void eraseGitTransport(git_smart_subtransport *tr)
void registerAccounts()
Send registration for all enabled accounts.
void setHistoryLimit(int days)
Set the maximum number of days to keep in the history.
std::map< std::string, std::string > getVolatileAccountDetails(const std::string &accountID) const
Retrieve volatile details such as recent registration errors.
std::shared_ptr< T > getAccount(std::string_view accountId) const
Get an account pointer, looks for account of type T.
AudioFormat audioFormatUsed(AudioFormat format)
Should be called by any component dealing with an external audio source, indicating the format used s...
bool getIsAlwaysRecording() const
Get is always recording functionality.
void setAccountDetails(const std::string &accountID, const std::map< std::string, std::string > &details)
Save the details of an existing account, given the account ID This will load the configuration map wi...
std::string addAccount(const std::map< std::string, std::string > &details, const std::string &accountId={})
Add a new account, and give it a new account ID automatically.
void saveConfig()
Save config to file.
std::vector< std::string > getCallList() const
Get list of calls (internal subcalls are filter-out)
void setNoiseSuppressState(const std::string &state)
Set the noise reduction engine state in the current audio layer.
bool toggleRecordingCall(const std::string &accountId, const std::string &id)
Set recording on / off Start recording.
void bindCallToConference(Call &call, Conference &conf)
SIPVoIPLink & sipVoIPLink() const
static std::atomic_bool initialized
VideoManager * getVideoManager() const
void recordingPlaybackSeek(const double value)
AccountFactory accountFactory
std::string getAudioManager() const
Get the audio manager.
std::shared_ptr< dhtnet::ChannelSocket > gitSocket(std::string_view accountId, std::string_view deviceId, std::string_view conversationId)
Return current git socket used for a conversation.
void setDefaultModerator(const std::string &accountID, const std::string &peerURI, bool state)
void stopRecordedFilePlayback()
Stop playback of recorded file.
std::map< std::string, std::string > getNearbyPeers(const std::string &accountID)
Preferences preferences
General preferences configuration.
bool setAudioManager(const std::string &api)
Set the audio manager.
RingBufferPool & getRingBufferPool()
Return a pointer to the instance of the RingBufferPool.
const std::shared_ptr< dhtnet::IceTransportFactory > & getIceTransportFactory()
int getRingingTimeout() const
Get ringing timeout (number of seconds after which a call will enter BUSY state if not answered).
int getAudioInputDeviceIndex(const std::string &name)
Get index of an audio device.
static bool syncOnRegister
std::vector< libjami::Message > getLastMessages(const std::string &accountID, const uint64_t &base_timestamp)
VoipPreference voipPreferences
Voip related preferences.
dhtnet::tls::CertificateStore & certStore(const std::string &accountId) const
bool hasAccount(const std::string &accountID)
uint64_t sendTextMessage(const std::string &accountID, const std::string &to, const std::map< std::string, std::string > &payloads, bool fromPlugin=false, bool onlyConnected=false)
int getHistoryLimit() const
Get the maximum number of days to keep in the history.
bool isAllModerators(const std::string &accountID)
std::string getCurrentAudioOutputPlugin() const
Get current alsa plugin.
std::mt19937_64 getSeededRandomEngine()
std::shared_ptr< AudioLayer > getAudioDriver()
Accessor to audiodriver.
void setIsAlwaysRecording(bool isAlwaysRec)
Set is always recording functionality, every calls will then be set in RECORDING mode once answered.
std::string getNoiseSuppressState() const
Get the noise reduction engine state from the current audio layer.
void removeAccount(const std::string &accountID, bool flush=false)
Delete an existing account, unregister VoIPLink associated, and purge from configuration.
bool isAGCEnabled() const
void setAGCState(bool enabled)
void init(const std::filesystem::path &config_file, libjami::InitFlag flags)
Initialisation of thread (sound) and map.
void sendRegister(const std::string &accountId, bool enable)
ConfigurationManager - Send registration request.
std::unique_ptr< AudioDeviceGuard > startAudioStream(AudioDeviceType stream)
std::string getNewAccountId()
Return a new random accountid that is not present in the list.
AudioPreference audioPreference
Audio preferences.
int getAudioOutputDeviceIndex(const std::string &name)
AudioFormat hardwareAudioFormatChanged(AudioFormat format)
Callback called when the audio layer initialised with its preferred format.
int getMessageStatus(uint64_t id) const
std::size_t accountCount() const
int loadAccountMap(const YAML::Node &node)
Load the account map from configuration.
void setAccountOrder(const std::string &ord)
void setRingingTimeout(int timeout)
void setHistoryLimit(int lim)
void removeAccount(const std::string &acc)
void unserialize(const YAML::Node &in) override
int getHistoryLimit() const
int getRingingTimeout() const
const std::string & getAccountOrder() const
void addAccount(const std::string &acc)
virtual std::string getPath() const
Return the file path for this recording.
static const char *const DEFAULT_ID
std::shared_ptr< Task > schedule(std::function< void()> &&job, time_point t, const char *filename=CURRENT_FILENAME(), uint32_t linum=CURRENT_LINE())
Schedule job to be run at time t.
ToneId
The different kind of tones.
void unserialize(const YAML::Node &in) override
DMTF library to generate a dtmf sample.
int p2p_transport_cb(git_transport **out, git_remote *owner, void *)
Setup the transport callback.
#define JAMI_ERROR(formatstr,...)
#define JAMI_XERR(formatstr,...)
#define JAMI_XDBG(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
#define JAMI_XWARN(formatstr,...)
static const char *const CONFIG_ACCOUNT_TYPE
const std::filesystem::path & get_data_dir()
const std::filesystem::path & get_config_dir()
const std::filesystem::path & get_cache_dir()
void parseValue(const YAML::Node &node, const char *key, T &value)
bool parseValueOptional(const YAML::Node &node, const char *key, T &value)
std::set< std::string > CallIdSet
void emitSignal(Args... args)
std::string to_hex_string(uint64_t id)
static void setGnuTlsLogLevel()
Set gnutls's log level based on the JAMI_LOG_TLS environment variable.
std::string_view string_remove_suffix(std::string_view str, char separator)
void check_rename(const std::filesystem::path &old_dir, const std::filesystem::path &new_dir)
static void make_backup(const std::filesystem::path &path)
static void copy_over(const std::filesystem::path &srcPath, const std::filesystem::path &destPath)
static constexpr std::string_view ACCOUNT_TYPE_JAMI
static void setSipLogLevel()
Set pjsip's log level based on the JAMI_LOG_SIP environment variable.
std::string_view trim(std::string_view s)
std::set< std::string > CallIDSet
To store uniquely a list of Call ids.
static constexpr std::string_view ACCOUNT_TYPE_SIP
static void restore_backup(const std::filesystem::path &path)
static unsigned setDhtLogLevel()
Set OpenDHT's log level based on the JAMI_LOG_DHT environment variable.
static constexpr const char * PACKAGE_OLD
std::vector< std::string_view > split_string(std::string_view str, char delim)
void string_replace(std::string &str, const std::string &from, const std::string &to)
static void runOnMainThread(Callback &&cb)
static constexpr char CURRENT[]
@ LIBJAMI_FLAG_NO_AUTOLOAD
@ LIBJAMI_FLAG_NO_LOCAL_AUDIO
@ LIBJAMI_FLAG_NO_LOCAL_VIDEO
Specific VoIPLink for SIP (SIP core for incoming and outgoing events).
bool localModeratorsEnabled
bool allModeratorsEnabled
std::unique_ptr< DTMF > dtmfKey_
std::shared_ptr< AudioFrame > dtmfBuf_
Buffer to generate DTMF.
static void stripSipPrefix(Call &incomCall)
std::mutex waitingCallsMutex_
Protect waiting call list, access by many VoIP/audio threads.
std::shared_ptr< dhtnet::IceTransportFactory > ice_tf_
void processRemainingParticipants(Conference &conf)
Process remaining participant given a conference and the current call ID.
std::atomic_bool finished_
ManagerPimpl(Manager &base)
std::mutex gitTransportsMtx_
bool hangupConference(Conference &conf)
void bindCallToConference(Call &call, Conference &conf)
std::map< git_smart_subtransport *, std::unique_ptr< P2PSubTransport > > gitTransports_
std::unique_ptr< VideoManager > videoManager_
std::map< std::string, std::weak_ptr< video::SinkClient > > sinkMap_
std::thread ioContextRunner_
void processIncomingCall(const std::string &accountId, Call &incomCall)
std::mutex sinksMutex_
Protected sinks access.
void addMainParticipant(Conference &conf)
std::filesystem::path retrieveConfigPath() const
Create config directory in home user and return configuration file path.
std::mutex currentCallMutex_
Protected current call access.
std::shared_ptr< dhtnet::upnp::UPnPContext > upnpContext_
std::mutex audioLayerMutex_
Mutex used to protect audio layer.
void loadAccount(const YAML::Node &item, int &errorCount)
ToneControl toneCtrl_
Application wide tone controller.
void playATone(Tone::ToneId toneId)
Multi Thread.
bool parseConfiguration()
std::shared_ptr< T > findAccount(const std::function< bool(const std::shared_ptr< T > &)> &)
std::unique_ptr< SIPVoIPLink > sipLink_
std::array< std::atomic_uint, 3 > audioStreamUsers_
int getCurrentDeviceIndex(AudioDeviceType type)
std::unique_ptr< AudioDeviceGuard > toneDeviceGuard_
std::shared_ptr< asio::io_context > ioContext_
std::atomic_bool autoAnswer_
void removeWaitingCall(const std::string &id)
Remove incoming callid to the waiting list.
void switchCall(const std::string &id)
ScheduledExecutor scheduler_
Main scheduler.
void initAudioDriver()
Initialization: Main Thread.
void sendTextMessageToConference(const Conference &conf, const std::map< std::string, std::string > &messages, const std::string &from) const noexcept
std::unique_ptr< RingBufferPool > ringbufferpool_
Instance of the RingBufferPool for the whole application.
std::string currentCall_
Current Call ID.
std::filesystem::path path_
Path of the ConfigFile.
std::shared_ptr< AudioLayer > audiodriver_
Audio layer.
CallIDSet waitingCalls_
Waiting Call Vectors.
void addWaitingCall(const std::string &id)
Add incoming callid to the waiting list.