35#include <opendht/rng.h>
73#include <dhtnet/ice_transport_factory.h>
74#include <dhtnet/ice_transport.h>
75#include <dhtnet/upnp/upnp_context.h>
77#include <libavutil/ffversion.h>
79#include <opendht/thread_pool.h>
81#include <asio/io_context.hpp>
82#include <asio/executor_work_guard.hpp>
88#include <sys/resource.h>
92#include <CoreFoundation/CoreFoundation.h>
107#error "Define the JAMI_DATADIR macro as the data installation prefix of the package"
120bool Manager::isIOSExtension = {
false};
162 if (
not std::filesystem::is_directory(
new_dir)) {
180 std::filesystem::remove_all(
old_dir,
ec);
222 auto msg = std::string_view(data, len);
299 const std::map<std::string, std::string>&
messages,
300 const std::string& from)
const noexcept;
309 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_);
438 }
catch (
const YAML::BadFile&
e) {
439 JAMI_WARNING(
"[config] Unable to open configuration file");
452 if (
not base_.voipPreferences.getPlayTones())
455 std::lock_guard lock(audioLayerMutex_);
456 if (
not audiodriver_) {
457 JAMI_ERROR(
"[audio] 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(
"[conf:{}] Processing {} remaining participant(s)",
conf.getConfId(),
conf.getConferenceInfos().size());
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)) {
526 JAMI_ERROR(
"[conf:{}] Account no longer available",
conf.getConfId());
535 JAMI_DEBUG(
"[conf:{}] Only one participant left, removing conference",
conf.getConfId());
539 JAMI_DEBUG(
"[conf:{}] No remaining participants, removing conference",
conf.getConfId());
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);
605 if (base_.preferences.isAccountPending(
accountid)) {
613 auto config =
a->buildConfig();
614 config->unserialize(node);
615 a->setConfig(std::move(config));
627 const auto dataPath =
cachePath /
"values";
629 dhtnet::fileutils::removeAll(dataPath);
631 dhtnet::fileutils::removeAll(
idPath,
true);
637 const std::map<std::string, std::string>&
messages,
638 const std::string& from)
const noexcept
641 for (
const auto& callId :
subcalls) {
643 auto call = base_.getCallFromCallID(callId);
645 throw std::runtime_error(
"No associated call");
646 call->sendTextMessage(
messages, from);
647 }
catch (
const std::exception&
e) {
648 JAMI_ERROR(
"[conf:{}] Failed to send message to participant {}: {}",
conf.getConfId(), callId,
e.what());
656 pimpl_->bindCallToConference(call,
conf);
668 base_.detachParticipant(callId);
670 JAMI_DEBUG(
"[call:{}] Bind to conference {} (callState={})", callId,
confId, state);
675 base_.getRingBufferPool().unBindAll(
media.first);
678 conf.addSubCall(callId);
680 if (state ==
"HOLD") {
682 }
else if (state ==
"INCOMING") {
683 base_.acceptCall(call);
684 }
else if (state ==
"CURRENT") {
685 }
else if (state ==
"INACTIVE") {
686 base_.acceptCall(call);
688 JAMI_WARNING(
"[call:{}] Call state {} unrecognized for conference", callId, state);
724 pimpl_ = std::make_unique<ManagerPimpl>(*
this);
727Manager::~Manager() {}
732 pimpl_->autoAnswer_ = enable;
745 JAMI_ERROR(
"Unable to initialize git transport: {}", error ? error->message :
"(unknown)");
759#define PJSIP_TRY(ret) \
761 if ((ret) != PJ_SUCCESS) \
762 throw std::runtime_error(#ret " failed"); \
781 JAMI_LOG(
"Using OpenDHT version: {:s}", dht::version());
790 pimpl_->sipLink_ = std::make_unique<SIPVoIPLink>();
796 pimpl_->ice_tf_ = std::make_shared<dhtnet::IceTransportFactory>(Logger::dhtLogger());
799 JAMI_LOG(
"Configuration file path: {}", pimpl_->path_);
802 pimpl_->jami_plugin_manager = std::make_unique<JamiPluginManager>();
808 pimpl_->finished_ =
false;
817 JAMI_DEBUG(
"LIBJAMI_FLAG_NO_AUTOLOAD is set, accounts will neither be loaded nor backed up");
820 no_errors = pimpl_->parseConfiguration();
821 }
catch (
const YAML::Exception&
e) {
822 JAMI_ERROR(
"[config] Failed to parse configuration: {}",
e.what());
837 pimpl_->parseConfiguration();
838 }
catch (
const YAML::Exception&
e) {
846 std::lock_guard lock(pimpl_->audioLayerMutex_);
847 pimpl_->initAudioDriver();
848 if (pimpl_->audiodriver_) {
849 auto format = pimpl_->audiodriver_->getFormat();
850 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
857 pimpl_->ioContextRunner_ = std::thread([context = pimpl_->ioContext_]() {
859 auto work = asio::make_work_guard(*context);
861 }
catch (
const std::exception&
ex) {
862 JAMI_ERROR(
"[io] Unexpected io_context thread exception: {}",
ex.what());
867 JAMI_DEBUG(
"LIBJAMI_FLAG_NO_AUTOLOAD is set, accounts and conversations will not be loaded");
875Manager::finish() noexcept
877 bool expected =
false;
878 if (not pimpl_->finished_.compare_exchange_strong(expected,
true))
883 upnpContext()->shutdown();
886 callFactory.forbid();
889 JAMI_DBG(
"End %zu remaining call(s)", callFactory.callCount());
890 for (
const auto& call : callFactory.getAllCalls())
894 for (
const auto& account : getAllAccounts<JamiAccount>()) {
895 if (account->getRegistrationState() == RegistrationState::INITIALIZING)
896 removeAccount(account->getAccountID(),
true);
902 unregisterAccounts();
903 accountFactory.clear();
906 std::lock_guard lock(pimpl_->audioLayerMutex_);
907 pimpl_->audiodriver_.reset();
910 JAMI_DEBUG(
"Stopping schedulers and worker threads");
913 dht::ThreadPool::io().join();
914 dht::ThreadPool::computation().join();
919 pimpl_->ice_tf_.reset();
924 if (pimpl_->sipLink_) {
925 pimpl_->sipLink_->shutdown();
926 pimpl_->sipLink_.reset();
930 pimpl_->gitTransports_.clear();
931 git_libgit2_shutdown();
933 if (!pimpl_->ioContext_->stopped()) {
934 pimpl_->ioContext_->stop();
936 if (pimpl_->ioContextRunner_.joinable())
937 pimpl_->ioContextRunner_.join();
940 gnutls_global_deinit();
949Manager::monitor(
bool continuous)
951 Logger::setMonitorLog(
true);
952 JAMI_DEBUG(
"############## START MONITORING ##############");
953 JAMI_DEBUG(
"Using PJSIP version: {} for {}", pj_get_version(), PJ_OS_NAME);
954 JAMI_DEBUG(
"Using GnuTLS version: {}", gnutls_check_version(
nullptr));
955 JAMI_DEBUG(
"Using OpenDHT version: {}", dht::version());
958#if defined(__ANDROID__)
960 auto opened_files = dhtnet::fileutils::readDirectory(
"/proc/" + std::to_string(getpid()) +
"/fd").size();
965 for (
const auto& call : callFactory.getAllCalls())
967 for (
const auto& account : getAllAccounts())
968 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account))
970 JAMI_DEBUG(
"############## END MONITORING ##############");
971 Logger::setMonitorLog(continuous);
974std::vector<std::map<std::string, std::string>>
975Manager::getConnectionList(
const std::string& accountId,
const std::string& conversationId)
977 std::vector<std::map<std::string, std::string>> connectionsList;
979 if (accountId.empty()) {
980 for (
const auto& account : getAllAccounts<JamiAccount>()) {
981 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
982 const auto& cnl = account->getConnectionList(conversationId);
983 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
987 auto account = getAccount(accountId);
989 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
990 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
991 const auto& cnl = acc->getConnectionList(conversationId);
992 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
998 return connectionsList;
1001std::vector<std::map<std::string, std::string>>
1002Manager::getChannelList(
const std::string& accountId,
const std::string& connectionId)
1006 std::vector<std::map<std::string, std::string>> channelsList;
1008 if (accountId.empty()) {
1009 for (
const auto& account : getAllAccounts<JamiAccount>()) {
1010 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
1012 const auto& cnl = account->getChannelList(connectionId);
1013 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1021 auto account = getAccount(accountId);
1023 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
1024 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
1025 const auto& cnl = acc->getChannelList(connectionId);
1026 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1032 return channelsList;
1036Manager::isCurrentCall(
const Call& call)
const
1038 return pimpl_->currentCall_ == call.
getCallId();
1042Manager::hasCurrentCall()
const
1044 for (
const auto& call : callFactory.getAllCalls()) {
1051std::shared_ptr<Call>
1052Manager::getCurrentCall()
const
1054 return getCallFromCallID(pimpl_->currentCall_);
1058Manager::getCurrentCallId()
const
1060 return pimpl_->currentCall_;
1064Manager::unregisterAccounts()
1066 for (
const auto& account : getAllAccounts()) {
1067 if (account->isEnabled()) {
1068 account->doUnregister(
true);
1079Manager::outgoingCall(
const std::string& account_id,
1080 const std::string& to,
1081 const std::vector<libjami::MediaMap>& mediaList)
1083 JAMI_DBG() <<
"Attempt outgoing call to '" << to <<
"'" <<
" with account '" << account_id <<
"'";
1085 std::shared_ptr<Call> call;
1088 call = newOutgoingCall(
trim(to), account_id, mediaList);
1089 }
catch (
const std::exception& e) {
1099 pimpl_->switchCall(call->getCallId());
1101 return call->getCallId();
1106Manager::acceptCall(
const std::string& accountId,
1107 const std::string& callId,
1108 const std::vector<libjami::MediaMap>& mediaList)
1110 if (
auto account = getAccount(accountId)) {
1111 if (
auto call = account->getCall(callId)) {
1112 return acceptCall(*call, mediaList);
1119Manager::acceptCall(
Call& call,
const std::vector<libjami::MediaMap>& mediaList)
1130 pimpl_->removeWaitingCall(call.
getCallId());
1134 }
catch (
const std::runtime_error& e) {
1148 if (audioPreference.getIsAlwaysRecording()) {
1150 emitSignal<libjami::CallSignal::RecordPlaybackFilepath>(call.
getCallId(), call.
getPath());
1151 emitSignal<libjami::CallSignal::RecordingStateChanged>(call.
getCallId(), recResult);
1158Manager::hangupCall(
const std::string& accountId,
const std::string& callId)
1160 auto account = getAccount(accountId);
1165 pimpl_->removeWaitingCall(callId);
1168 auto call = account->getCall(callId);
1170 JAMI_WARN(
"Unable to hang up nonexistent call %s", callId.c_str());
1177 if (call->isConferenceParticipant()) {
1178 removeParticipant(*call);
1181 if (isCurrentCall(*call))
1182 pimpl_->unsetCurrentCall();
1188 JAMI_ERROR(
"[call:{}] Failed to hangup: {}", call->getCallId(), e.what());
1196Manager::hangupConference(
const std::string& accountId,
const std::string& confId)
1198 if (
auto account = getAccount(accountId)) {
1199 if (
auto conference = account->getConference(confId)) {
1200 return pimpl_->hangupConference(*conference);
1202 JAMI_ERROR(
"[conf:{}] Conference not found", confId);
1210Manager::holdCall(
const std::string&,
const std::string& callId)
1216 std::string current_callId(getCurrentCallId());
1218 if (
auto call = getCallFromCallID(callId)) {
1220 result = call->
hold([=](
bool ok) {
1222 JAMI_ERROR(
"CallID {} holdCall failed", callId);
1227 pimpl_->removeWaitingCall(callId);
1231 if (current_callId == callId)
1232 pimpl_->unsetCurrentCall();
1235 JAMI_ERROR(
"[call:{}] Failed to hold: {}", callId, e.what());
1239 JAMI_LOG(
"CallID {} doesn't exist in call holdCall", callId);
1248Manager::resumeCall(
const std::string&,
const std::string& callId)
1254 std::shared_ptr<Call> call = getCallFromCallID(callId);
1259 result = call->resume([=](
bool ok) {
1261 JAMI_ERROR(
"CallID {} resumeCall failed", callId);
1265 if (
auto conf = call->getConference())
1268 pimpl_->switchCall(call->getCallId());
1273 JAMI_ERROR(
"[call] Failed to resume: {}", e.what());
1282Manager::transferCall(
const std::string& accountId,
const std::string& callId,
const std::string& to)
1284 auto account = getAccount(accountId);
1287 if (
auto call = account->getCall(callId)) {
1289 removeParticipant(*call);
1295 pimpl_->removeWaitingCall(callId);
1301Manager::transferFailed()
1303 emitSignal<libjami::CallSignal::TransferFailed>();
1307Manager::transferSucceeded()
1309 emitSignal<libjami::CallSignal::TransferSucceeded>();
1314Manager::refuseCall(
const std::string& accountId,
const std::string&
id)
1316 if (
auto account = getAccount(accountId)) {
1317 if (
auto call = account->getCall(
id)) {
1320 pimpl_->removeWaitingCall(
id);
1329Manager::holdConference(
const std::string& accountId,
const std::string& confId)
1331 JAMI_LOG(
"[conf:{}] Hold conference", confId);
1333 if (
const auto account = getAccount(accountId)) {
1334 if (
auto conf = account->getConference(confId)) {
1336 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId, conf->
getConfId(), conf->
getStateStr());
1344Manager::resumeConference(
const std::string& accountId,
const std::string& confId)
1346 JAMI_DEBUG(
"[conf:{}] Resume conference", confId);
1348 if (
const auto account = getAccount(accountId)) {
1349 if (
auto conf = account->getConference(confId)) {
1352 if (conf->
getState() == Conference::State::HOLD) {
1354 resumeCall(accountId, item);
1356 pimpl_->switchCall(confId);
1357 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1358 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId, conf->
getConfId(), conf->
getStateStr());
1360 }
else if (conf->
getState() == Conference::State::ACTIVE_DETACHED) {
1361 pimpl_->addMainParticipant(*conf);
1369Manager::addSubCall(
const std::string& accountId,
1370 const std::string& callId,
1371 const std::string& account2Id,
1372 const std::string& conferenceId)
1374 auto account = getAccount(accountId);
1375 auto account2 = getAccount(account2Id);
1376 if (account && account2) {
1377 auto call = account->getCall(callId);
1378 auto conf = account2->getConference(conferenceId);
1382 if (callConf != conf)
1383 return addSubCall(*call, *conf);
1394 pimpl_->bindCallToConference(call, conference);
1397 if (conference.
getState() == Conference::State::ACTIVE_DETACHED) {
1404 pimpl_->unsetCurrentCall();
1405 pimpl_->addMainParticipant(conference);
1406 pimpl_->switchCall(conference.
getConfId());
1427 if (subcalls.empty()) {
1429 account->removeConference(conference.
getConfId());
1431 for (
const auto& callId : subcalls) {
1432 if (
auto call = base_.getCallFromCallID(callId))
1440Manager::addMainParticipant(
const std::string& accountId,
const std::string& conferenceId)
1442 JAMI_LOG(
"[conf:{}] Adding main participant", conferenceId);
1444 if (
auto account = getAccount(accountId)) {
1445 if (
auto conf = account->getConference(conferenceId)) {
1446 pimpl_->addMainParticipant(*conf);
1449 JAMI_WARNING(
"[conf:{}] Failed to add main participant (conference not found)", conferenceId);
1454std::shared_ptr<Call>
1455Manager::getCallFromCallID(
const std::string& callID)
const
1457 return callFactory.getCall(callID);
1461Manager::joinParticipant(
const std::string& accountId,
1462 const std::string& callId1,
1463 const std::string& account2Id,
1464 const std::string& callId2,
1467 JAMI_DEBUG(
"Joining participants {} and {}, attached={}", callId1, callId2, attached);
1468 auto account = getAccount(accountId);
1469 auto account2 = getAccount(account2Id);
1470 if (not account or not account2) {
1474 JAMI_LOG(
"Creating conference for participants {} and {}, host attached: {}", callId1, callId2, attached);
1476 if (callId1 == callId2) {
1477 JAMI_ERROR(
"Unable to join participant {} to itself", callId1);
1482 auto call1 = account->getCall(callId1);
1484 JAMI_ERROR(
"Unable to find call {}", callId1);
1489 auto call2 = account2->getCall(callId2);
1491 JAMI_ERROR(
"Unable to find call {}", callId2);
1495 auto mediaAttr = call1->getMediaAttributeList();
1496 if (mediaAttr.empty()) {
1497 JAMI_WARNING(
"[call:{}] No media attribute found, using media attribute from call [{}]", callId1, callId2);
1498 mediaAttr = call2->getMediaAttributeList();
1505 bool audioFound =
false;
1506 mediaAttr.erase(std::remove_if(mediaAttr.begin(),
1509 if (attr.type_ == MediaType::MEDIA_AUDIO) {
1510 if (audioFound && attr.muted_)
1519 JAMI_DEBUG(
"[call:{}] Media attributes for conference:", callId1);
1520 for (
const auto& media : mediaAttr) {
1524 auto conf = std::make_shared<Conference>(account);
1525 conf->
attachHost(MediaAttribute::mediaAttributesToMediaMaps(mediaAttr));
1526 account->attach(conf);
1527 emitSignal<libjami::CallSignal::ConferenceCreated>(account->getAccountID(),
"", conf->
getConfId());
1530 pimpl_->bindCallToConference(*call1, *conf);
1531 pimpl_->bindCallToConference(*call2, *conf);
1536 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1540 emitSignal<libjami::CallSignal::ConferenceChanged>(account->getAccountID(), conf->
getConfId(), conf->
getStateStr());
1546Manager::createConfFromParticipantList(
const std::string& accountId,
const std::vector<std::string>& participantList)
1548 auto account = getAccount(accountId);
1550 JAMI_WARNING(
"[account:{}] Account not found", accountId);
1555 if (participantList.size() <= 1) {
1556 JAMI_ERROR(
"[conf] Participant number must be greater than or equal to 2");
1560 auto conf = std::make_shared<Conference>(account);
1565 unsigned successCounter = 0;
1566 for (
const auto& numberaccount : participantList) {
1567 std::string tostr(numberaccount.substr(0, numberaccount.find(
',')));
1568 std::string account(numberaccount.substr(numberaccount.find(
',') + 1, numberaccount.size()));
1570 pimpl_->unsetCurrentCall();
1573 auto callId = outgoingCall(account, tostr, {});
1583 if (successCounter >= 2) {
1584 account->attach(conf);
1585 emitSignal<libjami::CallSignal::ConferenceCreated>(accountId,
"", conf->
getConfId());
1590Manager::detachHost(
const std::shared_ptr<Conference>& conf)
1595 JAMI_LOG(
"[conf:{}] Detaching host", conf->getConfId());
1597 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(), conf->getConfId(), conf->getStateStr());
1598 pimpl_->unsetCurrentCall();
1603Manager::detachParticipant(
const std::string& callId)
1605 JAMI_DEBUG(
"Detaching participant {}", callId);
1607 auto call = getCallFromCallID(callId);
1609 JAMI_ERROR(
"Unable to find call {}", callId);
1617 removeParticipant(*call);
1622Manager::removeParticipant(
Call& call)
1628 JAMI_ERROR(
"[call:{}] No conference associated, unable to remove participant", call.
getCallId());
1636 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(), conf->getConfId(), conf->getStateStr());
1638 pimpl_->processRemainingParticipants(*conf);
1642Manager::joinConference(
const std::string& accountId,
1643 const std::string& confId1,
1644 const std::string& account2Id,
1645 const std::string& confId2)
1647 auto account = getAccount(accountId);
1648 auto account2 = getAccount(account2Id);
1650 JAMI_ERROR(
"Unable to find account: {}", accountId);
1654 JAMI_ERROR(
"Unable to find account: {}", account2Id);
1658 auto conf = account->getConference(confId1);
1660 JAMI_ERROR(
"[conf:{}] Invalid conference ID", confId1);
1664 auto conf2 = account2->getConference(confId2);
1666 JAMI_ERROR(
"[conf:{}] Invalid conference ID", confId2);
1670 CallIdSet subcalls(conf->getSubCalls());
1672 std::vector<std::shared_ptr<Call>> calls;
1673 calls.reserve(subcalls.size());
1677 for (
const auto& callId : subcalls) {
1679 if (
auto call = account->getCall(callId)) {
1680 conf->removeSubCall(callId);
1682 calls.emplace_back(std::move(call));
1684 JAMI_ERROR(
"Unable to find call {}", callId);
1688 account->removeConference(confId1);
1690 for (
const auto& c : calls)
1691 addSubCall(*c, *conf2);
1702 JAMI_LOG(
"Add audio to call {}", callId);
1706 for (
const auto& media : medias) {
1707 JAMI_DEBUG(
"[call:{}] Attach audio stream {}", callId, media.first);
1708 getRingBufferPool().bindRingBuffers(media.first, RingBufferPool::DEFAULT_ID);
1711 call.
audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1713 std::lock_guard lock(pimpl_->audioLayerMutex_);
1714 if (!pimpl_->audiodriver_) {
1718 pimpl_->audiodriver_->flushUrgent();
1719 getRingBufferPool().flushAllBuffers();
1727 for (
const auto& media : medias) {
1728 JAMI_DEBUG(
"[call:{}] Remove local audio {}", callId, media.first);
1729 getRingBufferPool().unBindAll(media.first);
1733std::shared_ptr<asio::io_context>
1734Manager::ioContext()
const
1736 return pimpl_->ioContext_;
1739std::shared_ptr<dhtnet::upnp::UPnPContext>
1740Manager::upnpContext()
const
1742 return pimpl_->upnpContext_;
1746Manager::saveConfig(
const std::shared_ptr<Account>& acc)
1748 if (
auto account = std::dynamic_pointer_cast<JamiAccount>(acc))
1749 account->saveConfig();
1755Manager::saveConfig()
1757 JAMI_LOG(
"Saving configuration to '{}'", pimpl_->path_);
1759 if (pimpl_->audiodriver_) {
1760 audioPreference.setVolumemic(pimpl_->audiodriver_->getCaptureGain());
1761 audioPreference.setVolumespkr(pimpl_->audiodriver_->getPlaybackGain());
1762 audioPreference.setCaptureMuted(pimpl_->audiodriver_->isCaptureMuted());
1763 audioPreference.setPlaybackMuted(pimpl_->audiodriver_->isPlaybackMuted());
1770 out << YAML::BeginMap << YAML::Key <<
"accounts";
1771 out << YAML::Value << YAML::BeginSeq;
1773 for (
const auto& account : accountFactory.getAllAccounts()) {
1774 if (
auto jamiAccount = std::dynamic_pointer_cast<JamiAccount>(account)) {
1775 auto accountConfig = jamiAccount->getPath() /
"config.yml";
1776 if (not std::filesystem::is_regular_file(accountConfig)) {
1777 saveConfig(jamiAccount);
1780 account->config().serialize(out);
1783 out << YAML::EndSeq;
1786 preferences.verifyAccountOrder(getAccountList());
1787 preferences.serialize(out);
1788 voipPreferences.serialize(out);
1789 audioPreference.serialize(out);
1791 videoPreferences.serialize(out);
1794 pluginPreferences.serialize(out);
1797 std::lock_guard lock(dhtnet::fileutils::getFileLock(pimpl_->path_));
1798 std::ofstream fout(pimpl_->path_);
1799 fout.write(out.c_str(),
static_cast<long>(out.size()));
1800 }
catch (
const YAML::Exception& e) {
1801 JAMI_ERROR(
"[config] YAML error: {}", e.what());
1802 }
catch (
const std::runtime_error& e) {
1809Manager::playDtmf(
char code)
1813 if (not voipPreferences.getPlayDtmf()) {
1818 int pulselen = voipPreferences.getPulseLength();
1820 if (pulselen == 0) {
1824 std::lock_guard lock(pimpl_->audioLayerMutex_);
1827 if (not pimpl_->audiodriver_ or not pimpl_->dtmfKey_) {
1831 std::shared_ptr<AudioDeviceGuard> audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1832 if (not pimpl_->audiodriver_->waitForStart(std::chrono::seconds(1))) {
1833 JAMI_ERROR(
"[audio] Failed to start audio layer for DTMF");
1841 unsigned size = (unsigned) ((pulselen * (
long) pimpl_->audiodriver_->getSampleRate()) / 1000ul);
1842 if (!pimpl_->dtmfBuf_ or pimpl_->dtmfBuf_->getFrameSize() != size)
1843 pimpl_->dtmfBuf_ = std::make_shared<AudioFrame>(pimpl_->audiodriver_->getFormat(), size);
1846 pimpl_->dtmfKey_->startTone(code);
1849 if (pimpl_->dtmfKey_->generateDTMF(pimpl_->dtmfBuf_->pointer())) {
1855 pimpl_->audiodriver_->putUrgent(pimpl_->dtmfBuf_);
1858 auto dtmfTimer = std::make_unique<asio::steady_timer>(*pimpl_->ioContext_, std::chrono::milliseconds(pulselen));
1859 dtmfTimer->async_wait([
this, audioGuard, t = dtmfTimer.get()](
const asio::error_code& ec) {
1862 JAMI_DBG(
"End of dtmf");
1863 std::lock_guard lock(pimpl_->audioLayerMutex_);
1864 if (pimpl_->dtmfTimer_.get() == t)
1865 pimpl_->dtmfTimer_.reset();
1867 if (pimpl_->dtmfTimer_)
1868 pimpl_->dtmfTimer_->cancel();
1869 pimpl_->dtmfTimer_ = std::move(dtmfTimer);
1874Manager::incomingCallsWaiting()
1876 std::lock_guard m(pimpl_->waitingCallsMutex_);
1877 return not pimpl_->waitingCalls_.empty();
1881Manager::incomingCall(
const std::string& accountId,
Call& call)
1883 if (not accountId.empty()) {
1884 pimpl_->stripSipPrefix(call);
1887 auto const& account = getAccount(accountId);
1894 pimpl_->processIncomingCall(accountId, call);
1898Manager::incomingMessage(
const std::string& accountId,
1899 const std::string& callId,
1900 const std::string& from,
1901 const std::map<std::string, std::string>& messages)
1903 auto account = getAccount(accountId);
1907 if (
auto call = account->getCall(callId)) {
1912 bool sendToOtherParicipants =
true;
1913 for (
auto& message : messages) {
1914 if (message.first.find(
"x-ring/ring.profile.vcard") != std::string::npos) {
1915 sendToOtherParicipants =
false;
1918 if (sendToOtherParicipants) {
1919 pimpl_->sendTextMessageToConference(*conf, messages, from);
1923 emitSignal<libjami::CallSignal::IncomingMessage>(accountId, conf->
getConfId(), from, messages);
1925 JAMI_ERROR(
"[call:{}] No conference associated to call", callId);
1928 emitSignal<libjami::CallSignal::IncomingMessage>(accountId, callId, from, messages);
1934Manager::sendCallTextMessage(
const std::string& accountId,
1935 const std::string& callID,
1936 const std::map<std::string, std::string>& messages,
1937 const std::string& from,
1940 auto account = getAccount(accountId);
1945 if (
auto conf = account->getConference(callID)) {
1946 pimpl_->sendTextMessageToConference(*conf, messages, from);
1947 }
else if (
auto call = account->getCall(callID)) {
1950 pimpl_->sendTextMessageToConference(*conf, messages, from);
1952 JAMI_ERROR(
"[call:{}] No conference associated to call", callID);
1958 JAMI_ERR(
"Failed to send message to call %s: %s", call->
getCallId().c_str(), e.what());
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 if (isCurrentCall(call)) {
2046 pimpl_->unsetCurrentCall();
2050 JAMI_LOG(
"[call:{}] Participating in conference, removing participant", call.
getCallId());
2052 removeParticipant(call);
2055 pimpl_->removeWaitingCall(call.
getCallId());
2056 if (not call.
isSubcall() && not incomingCallsWaiting())
2067 if (not voipPreferences.getPlayTones())
2070 pimpl_->toneCtrl_.stop();
2071 pimpl_->toneDeviceGuard_.reset();
2080 pimpl_->playATone(Tone::ToneId::DIALTONE);
2087Manager::playToneWithMessage()
2089 pimpl_->playATone(Tone::ToneId::CONGESTION);
2096Manager::congestion()
2098 pimpl_->playATone(Tone::ToneId::CONGESTION);
2107 pimpl_->playATone(Tone::ToneId::RINGTONE);
2114Manager::playRingtone(
const std::string& accountID)
2116 const auto account = getAccount(accountID);
2118 JAMI_WARNING(
"[account:{}] Invalid account for ringtone", accountID);
2122 if (!account->getRingtoneEnabled()) {
2128 std::lock_guard lock(pimpl_->audioLayerMutex_);
2130 if (not pimpl_->audiodriver_) {
2131 JAMI_ERROR(
"[audio] No audio layer for ringtone");
2135 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2136 pimpl_->toneDeviceGuard_ = startAudioStream(AudioDeviceType::RINGTONE);
2137 auto format = pimpl_->audiodriver_->getFormat();
2138 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2141 if (not pimpl_->toneCtrl_.setAudioFile(account->getRingtonePath().string()))
2145std::shared_ptr<AudioLoop>
2146Manager::getTelephoneTone()
2148 return pimpl_->toneCtrl_.getTelephoneTone();
2151std::shared_ptr<AudioLoop>
2152Manager::getTelephoneFile()
2154 return pimpl_->toneCtrl_.getTelephoneFile();
2161Manager::setAudioPlugin(
const std::string& audioPlugin)
2164 std::lock_guard lock(pimpl_->audioLayerMutex_);
2165 audioPreference.setAlsaPlugin(audioPlugin);
2166 pimpl_->audiodriver_.reset();
2167 pimpl_->initAudioDriver();
2179 std::lock_guard lock(pimpl_->audioLayerMutex_);
2181 if (not pimpl_->audiodriver_) {
2182 JAMI_ERROR(
"[audio] Uninitialized audio driver");
2185 if (pimpl_->getCurrentDeviceIndex(type) == index) {
2186 JAMI_DEBUG(
"[audio] Audio device already selected, doing nothing");
2190 pimpl_->audiodriver_->updatePreference(audioPreference, index, type);
2193 pimpl_->audiodriver_.reset();
2194 pimpl_->initAudioDriver();
2201std::vector<std::string>
2202Manager::getAudioOutputDeviceList()
2204 std::lock_guard lock(pimpl_->audioLayerMutex_);
2206 if (not pimpl_->audiodriver_) {
2207 JAMI_ERROR(
"[audio] Uninitialized audio layer");
2211 return pimpl_->audiodriver_->getPlaybackDeviceList();
2217std::vector<std::string>
2218Manager::getAudioInputDeviceList()
2220 std::lock_guard lock(pimpl_->audioLayerMutex_);
2222 if (not pimpl_->audiodriver_) {
2223 JAMI_ERROR(
"[audio] Uninitialized audio layer");
2227 return pimpl_->audiodriver_->getCaptureDeviceList();
2233std::vector<std::string>
2234Manager::getCurrentAudioDevicesIndex()
2236 std::lock_guard lock(pimpl_->audioLayerMutex_);
2237 if (not pimpl_->audiodriver_) {
2238 JAMI_ERROR(
"[audio] Uninitialized audio layer");
2242 return {std::to_string(pimpl_->audiodriver_->getIndexPlayback()),
2243 std::to_string(pimpl_->audiodriver_->getIndexCapture()),
2244 std::to_string(pimpl_->audiodriver_->getIndexRingtone())};
2252 if (streamId >= manager_.pimpl_->audioStreamUsers_.size())
2253 throw std::invalid_argument(
"Invalid audio device type");
2254 if (manager_.pimpl_->audioStreamUsers_[streamId]++ == 0) {
2256 layer->startStream(type);
2265 std::lock_guard lock(manager_.pimpl_->audioDeviceUsersMutex_);
2276 if (captureDevice_.empty()) {
2278 if (--manager_.pimpl_->audioStreamUsers_[streamId] == 0) {
2280 layer->stopStream(type_);
2283 std::lock_guard lock(manager_.pimpl_->audioDeviceUsersMutex_);
2284 auto it = manager_.pimpl_->audioDeviceUsers_.find(captureDevice_);
2285 if (
it != manager_.pimpl_->audioDeviceUsers_.end()) {
2286 if (--
it->second == 0) {
2288 layer->stopCaptureStream(captureDevice_);
2289 manager_.pimpl_->audioDeviceUsers_.erase(
it);
2311 bool result =
false;
2313 std::shared_ptr<Recordable>
rec;
2315 JAMI_DEBUG(
"[conf:{}] Toggling recording",
id);
2317 }
else if (
auto call =
account->getCall(
id)) {
2318 JAMI_DEBUG(
"[call:{}] Toggling recording",
id);
2321 JAMI_ERROR(
"Unable to find recordable instance {}",
id);
2324 result =
rec->toggleRecording();
2337 std::lock_guard lock(pimpl_->audioLayerMutex_);
2339 if (
not pimpl_->audiodriver_) {
2340 JAMI_ERROR(
"[audio] No audio layer for recorded file playback");
2344 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2346 auto format = pimpl_->audiodriver_->getFormat();
2347 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2350 return pimpl_->toneCtrl_.setAudioFile(
filepath);
2356 pimpl_->toneCtrl_.seek(value);
2362 JAMI_DEBUG(
"[audio] Stop recorded file playback");
2364 pimpl_->toneCtrl_.stopAudioFile();
2365 pimpl_->toneDeviceGuard_.reset();
2385 JAMI_DEBUG(
"[config] Set ringing timeout to {} seconds", timeout);
2400 std::lock_guard lock(pimpl_->audioLayerMutex_);
2402 if (
not pimpl_->audiodriver_)
2406 JAMI_DEBUG(
"[audio] Audio manager '{}' already in use", api);
2412 std::lock_guard lock(pimpl_->audioLayerMutex_);
2414 pimpl_->audiodriver_.reset();
2415 pimpl_->initAudioDriver();
2433 std::lock_guard lock(pimpl_->audioLayerMutex_);
2435 if (
not pimpl_->audiodriver_) {
2436 JAMI_ERROR(
"[audio] Uninitialized audio layer");
2446 std::lock_guard lock(pimpl_->audioLayerMutex_);
2448 if (
not pimpl_->audiodriver_) {
2449 JAMI_ERROR(
"[audio] Uninitialized audio layer");
2520 for (
const auto& type :
TYPES)
2574 if (
not base_.hasCurrentCall()) {
2576#if !(defined(TARGET_OS_IOS) && TARGET_OS_IOS)
2578 base_.playRingtone(accountId);
2581 if (
account->isDenySecondCallEnabled()) {
2589 if (
account->isRendezVous()) {
2591 base_.acceptCall(*incomCall);
2593 for (const auto& callId : account->getCallList()) {
2594 if (auto call = account->getCall(callId)) {
2595 if (call->getState() != Call::CallState::ACTIVE)
2597 if (call != incomCall) {
2598 if (auto conf = call->getConference()) {
2599 base_.addSubCall(*incomCall, *conf);
2601 base_.joinParticipant(account->getAccountID(),
2602 incomCall->getCallId(),
2603 account->getAccountID(),
2613 auto conf = std::make_shared<Conference>(
account);
2622 conf->getStateStr());
2624 }
else if (autoAnswer_ ||
account->isAutoAnswerEnabled()) {
2625 dht::ThreadPool::io().run([
this,
incomCall =
incomCall.shared_from_this()] { base_.acceptCall(*incomCall); });
2646 auto& mgr = Manager::instance();
2647 mgr.acceptCall(*incomCall);
2648 mgr.hangupCall(accountId, currentCallID);
2670 pimpl_->ringbufferpool_->setInternalAudioFormat(format);
2680 JAMI_LOG(
"Set accounts order: {}", order);
2686std::vector<std::string>
2690 std::vector<std::string> v;
2693 v.emplace_back(
account->getAccountID());
2699std::map<std::string, std::string>
2705 return account->getAccountDetails();
2707 JAMI_ERROR(
"[account:{}] Unable to get account details on nonexistent account",
accountID);
2713std::map<std::string, std::string>
2719 return account->getVolatileAccountDetails();
2721 JAMI_ERROR(
"[account:{}] Unable to get volatile account details on nonexistent account",
accountID);
2758 std::lock_guard
l(randMutex_);
2759 return dht::crypto::getDerivedRandomEngine(rand_);
2826 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(
remAccount)) {
2850std::vector<std::string_view>
2871 }
catch (
const YAML::Exception&
e) {
2872 JAMI_ERROR(
"[config] Preferences unserialize YAML exception: {}",
e.what());
2874 }
catch (
const std::exception&
e) {
2875 JAMI_ERROR(
"[config] Preferences unserialize exception: {}",
e.what());
2878 JAMI_ERROR(
"[config] Preferences unserialize unknown exception");
2892 std::condition_variable cv;
2895 std::unique_lock
l(lock);
2896 for (
const auto&
dir :
dirs) {
2902 JAMI_INFO(
"[account:%s] Removing pending account from disk",
dir.c_str());
2904 pimpl_->cleanupAccountStorage(
dir);
2909 dht::ThreadPool::computation().run(
2911 if (std::filesystem::is_regular_file(
configFile)) {
2915 auto config =
a->buildConfig();
2917 a->setConfig(std::move(config));
2919 }
catch (
const std::exception&
e) {
2920 JAMI_ERROR(
"[account:{}] Unable to import account: {}",
dir,
e.what());
2923 std::lock_guard
l(lock);
2939std::vector<std::string>
2942 std::vector<std::string>
results;
2966 acc->setEnabled(enable);
2969 if (acc->isEnabled()) {
2972 acc->doUnregister();
2977 const std::string& to,
2978 const std::map<std::string, std::string>& payloads,
2987 auto cm = std::make_shared<JamiMessage>(
accountID, to,
false, payloads, fromPlugin);
2992 return acc->sendTextMessage(to,
"", payloads, 0,
onlyConnected);
2993 }
catch (
const std::exception&
e) {
3003 JAMI_ERROR(
"Deprecated method. Please use status from message");
3010 JAMI_ERROR(
"Deprecated method. Please use status from message");
3018 if (!acc || acc->isActive() == active)
3020 acc->setActive(active);
3021 if (acc->isEnabled()) {
3025 acc->doUnregister(shutdownConnections);
3047 account->enableAutoLoadConversations(
false);
3049 auto config =
account->buildConfig();
3051 account->setConfig(std::move(config));
3053 }
catch (
const std::runtime_error&
e) {
3054 JAMI_WARNING(
"[account:{}] Failed to load account: {}", accountId,
e.what());
3060 JAMI_WARNING(
"[account:{}] Unable to load account", accountId);
3064 if (
auto jamiAcc = std::dynamic_pointer_cast<JamiAccount>(
account)) {
3069 if (
auto* convModule =
jamiAcc->convModule()) {
3070 convModule->reloadRequests();
3072 convModule->loadConversations();
3073 }
else if (!
convId.empty()) {
3080std::shared_ptr<AudioLayer>
3083 return pimpl_->audiodriver_;
3086std::shared_ptr<Call>
3088 const std::string& accountId,
3089 const std::vector<libjami::MediaMap>&
mediaList)
3093 JAMI_WARNING(
"[account:{}] No account matches ID", accountId);
3098 JAMI_WARNING(
"[account:{}] Account is unusable", accountId);
3106std::shared_ptr<video::SinkClient>
3107Manager::createSinkClient(
const std::string&
id,
bool mixer)
3109 std::lock_guard
lk(pimpl_->sinksMutex_);
3110 auto&
sinkRef = pimpl_->sinkMap_[id];
3113 auto sink = std::make_shared<video::SinkClient>(
id,
mixer);
3119Manager::createSinkClients(
const std::string& callId,
3120 const ConfInfo& infos,
3121 const std::vector<std::shared_ptr<video::VideoFrameActiveWriter>>&
videoStreams,
3122 std::map<std::string, std::shared_ptr<video::SinkClient>>&
sinksMap,
3123 const std::string& accountId)
3128 std::vector<std::pair<std::shared_ptr<video::SinkClient>, std::pair<int, int>>>
newSinks;
3131 std::unique_lock
lk(pimpl_->sinksMutex_);
3134 if (sinkId.empty()) {
3151 auto newSink = std::make_shared<video::SinkClient>(sinkId,
false);
3178 sink->setFrameSize(size.first, size.second);
3184std::shared_ptr<video::SinkClient>
3185Manager::getSinkClient(
const std::string&
id)
3187 std::lock_guard
lk(pimpl_->sinksMutex_);
3188 const auto&
iter = pimpl_->sinkMap_.find(
id);
3189 if (
iter != std::end(pimpl_->sinkMap_))
3190 if (
auto sink =
iter->second.lock())
3199 return *pimpl_->ringbufferpool_;
3208const std::shared_ptr<dhtnet::IceTransportFactory>&
3211 return pimpl_->ice_tf_;
3217 return pimpl_->videoManager_.get();
3220std::vector<libjami::Message>
3231 return *pimpl_->sipLink_;
3236Manager::getJamiPluginManager()
const
3238 return *pimpl_->jami_plugin_manager;
3242std::shared_ptr<dhtnet::ChannelSocket>
3243Manager::gitSocket(std::string_view accountId, std::string_view deviceId, std::string_view conversationId)
3246 if (
auto* convModule = acc->convModule(
true))
3247 return convModule->gitSocket(deviceId, conversationId);
3251std::map<std::string, std::string>
3255 return acc->getNearbyPeers();
3264 JAMI_ERROR(
"[account:{}] Failed to change default moderator: account not found",
accountID);
3269 acc->addDefaultModerator(
peerURI);
3271 acc->removeDefaultModerator(
peerURI);
3275std::vector<std::string>
3280 JAMI_ERROR(
"[account:{}] Failed to get default moderators: account not found",
accountID);
3284 auto set = acc->getDefaultModerators();
3285 return std::vector<std::string>(set.begin(), set.end());
3303 return acc->isLocalModeratorsEnabled();
3321 return acc->isAllModerators();
3327 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3328 pimpl_->gitTransports_[
tr] = std::move(
sub);
3334 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3335 pimpl_->gitTransports_.erase(
tr);
3338dhtnet::tls::CertificateStore&
3344 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)
AudioDeviceGuard(Manager &manager, AudioDeviceType type)
const std::string & getAudioApi() const
bool getIsAlwaysRecording() const
bool getVadEnabled() const
AudioLayer * createAudioLayer()
void setIsAlwaysRecording(bool rec)
void setNoiseReduce(const std::string &enabled)
void setEchoCancel(const std::string &canceller)
void setAudioApi(const std::string &api)
bool isAGCEnabled() const
void setAGCState(bool enabled)
const std::string & getEchoCanceller() const
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.
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.
std::string getStateStr() const
virtual void monitor() const =0
virtual bool hold(OnReadyCb &&cb)=0
Hold call.
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.
virtual void answer(const std::vector< libjami::MediaMap > &mediaList)=0
Answer a call with a list of media attributes.
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
std::vector< libjami::MediaMap > getLastMediaList() const
Return the last media list before the host was detached.
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.
void attachHost(const std::vector< libjami::MediaMap > &mediaList)
Attach host.
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.
std::map< std::string, std::string > getAccountDetails(const std::string &accountID) const
Retrieve details about a given account.
void setVoiceActivityDetectionState(bool state)
Set the voice activity detection engine state in the current audio layer.
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::string getEchoCancellationState() const
Get the echo cancellation engine state in the current audio layer.
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 markAccountReady(const std::string &accountId)
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()
void markAccountPending(const std::string &accountId)
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)
bool getVoiceActivityDetectionState() const
Get the voice activity detection engine state from the current audio layer.
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::chrono::seconds getRingingTimeout() const
Get ringing timeout (number of seconds after which a call will enter BUSY state if not 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
void setRingingTimeout(std::chrono::seconds timeout)
Set ringing timeout (number of seconds after which a call will enter BUSY state if not answered).
std::size_t accountCount() const
int loadAccountMap(const YAML::Node &node)
Load the account map from configuration.
void setEchoCancellationState(const std::string &state)
Get the echo cancellation engine state from the current audio layer.
void setRingingTimeout(std::chrono::seconds timeout)
void setAccountOrder(const std::string &ord)
void setHistoryLimit(int lim)
bool removePendingAccountId(const std::string &accountId)
void removeAccount(const std::string &acc)
void unserialize(const YAML::Node &in) override
bool addPendingAccountId(const std::string &accountId)
int getHistoryLimit() const
std::chrono::seconds getRingingTimeout() const
const std::string & getAccountOrder() const
void addAccount(const std::string &acc)
bool isAccountPending(const std::string &accountId) const
virtual std::string getPath() const
Return the file path for this recording.
static const char *const DEFAULT_ID
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()
std::string getOrCreateLocalDeviceId()
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.
static void check_rename(const std::filesystem::path &old_dir, const std::filesystem::path &new_dir)
static unsigned getDhtnetLogLevel()
static unsigned getDhtLogLevel()
Set OpenDHT's log level based on the JAMI_LOG_DHT environment variable.
std::string_view string_remove_suffix(std::string_view str, char separator)
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 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.
void cleanupAccountStorage(const std::string &accountId)
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_
std::map< std::string, unsigned > audioDeviceUsers_
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::shared_ptr< asio::steady_timer > dtmfTimer_
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)
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::mutex audioDeviceUsersMutex_
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.