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_->reset();
916 pimpl_->ioContext_->stop();
918 if (pimpl_->ioContextRunner_.joinable())
919 pimpl_->ioContextRunner_.join();
922 gnutls_global_deinit();
931Manager::monitor(
bool continuous)
933 Logger::setMonitorLog(
true);
934 JAMI_DBG(
"############## START MONITORING ##############");
935 JAMI_DBG(
"Using PJSIP version: %s for %s", pj_get_version(), PJ_OS_NAME);
936 JAMI_DBG(
"Using GnuTLS version: %s", gnutls_check_version(
nullptr));
937 JAMI_DBG(
"Using OpenDHT version: %s", dht::version());
940#if defined(__ANDROID__)
943 = dhtnet::fileutils::readDirectory(
"/proc/" + std::to_string(getpid()) +
"/fd").size();
944 JAMI_DBG(
"Opened files: %lu", opened_files);
948 for (
const auto& call : callFactory.getAllCalls())
950 for (
const auto& account : getAllAccounts())
951 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account))
953 JAMI_DBG(
"############## END MONITORING ##############");
954 Logger::setMonitorLog(continuous);
957std::vector<std::map<std::string, std::string>>
958Manager::getConnectionList(
const std::string& accountId,
const std::string& conversationId)
960 std::vector<std::map<std::string, std::string>> connectionsList;
962 if (accountId.empty()) {
963 for (
const auto& account : getAllAccounts<JamiAccount>()) {
964 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
965 const auto& cnl = account->getConnectionList(conversationId);
966 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
970 auto account = getAccount(accountId);
972 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
973 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
974 const auto& cnl = acc->getConnectionList(conversationId);
975 connectionsList.insert(connectionsList.end(), cnl.begin(), cnl.end());
981 return connectionsList;
984std::vector<std::map<std::string, std::string>>
985Manager::getChannelList(
const std::string& accountId,
const std::string& connectionId)
989 std::vector<std::map<std::string, std::string>> channelsList;
991 if (accountId.empty()) {
992 for (
const auto& account : getAllAccounts<JamiAccount>()) {
993 if (account->getRegistrationState() != RegistrationState::INITIALIZING) {
995 const auto& cnl = account->getChannelList(connectionId);
996 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1004 auto account = getAccount(accountId);
1006 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account)) {
1007 if (acc->getRegistrationState() != RegistrationState::INITIALIZING) {
1008 const auto& cnl = acc->getChannelList(connectionId);
1009 channelsList.insert(channelsList.end(), cnl.begin(), cnl.end());
1015 return channelsList;
1019Manager::isCurrentCall(
const Call& call)
const
1021 return pimpl_->currentCall_ == call.
getCallId();
1025Manager::hasCurrentCall()
const
1027 for (
const auto& call : callFactory.getAllCalls()) {
1034std::shared_ptr<Call>
1035Manager::getCurrentCall()
const
1037 return getCallFromCallID(pimpl_->currentCall_);
1041Manager::getCurrentCallId()
const
1043 return pimpl_->currentCall_;
1047Manager::unregisterAccounts()
1049 for (
const auto& account : getAllAccounts()) {
1050 if (account->isEnabled()) {
1051 account->doUnregister(
true);
1062Manager::outgoingCall(
const std::string& account_id,
1063 const std::string& to,
1064 const std::vector<libjami::MediaMap>& mediaList)
1066 JAMI_DBG() <<
"Attempt outgoing call to '" << to <<
"'"
1067 <<
" with account '" << account_id <<
"'";
1069 std::shared_ptr<Call> call;
1072 call = newOutgoingCall(
trim(to), account_id, mediaList);
1073 }
catch (
const std::exception& e) {
1083 pimpl_->switchCall(call->getCallId());
1085 return call->getCallId();
1090Manager::answerCall(
const std::string& accountId,
1091 const std::string& callId,
1092 const std::vector<libjami::MediaMap>& mediaList)
1094 if (
auto account = getAccount(accountId)) {
1095 if (
auto call = account->getCall(callId)) {
1096 return answerCall(*call, mediaList);
1103Manager::answerCall(
Call& call,
const std::vector<libjami::MediaMap>& mediaList)
1114 pimpl_->removeWaitingCall(call.
getCallId());
1118 }
catch (
const std::runtime_error& e) {
1132 if (audioPreference.getIsAlwaysRecording()) {
1134 emitSignal<libjami::CallSignal::RecordPlaybackFilepath>(call.
getCallId(), call.
getPath());
1135 emitSignal<libjami::CallSignal::RecordingStateChanged>(call.
getCallId(), recResult);
1142Manager::hangupCall(
const std::string& accountId,
const std::string& callId)
1144 auto account = getAccount(accountId);
1149 pimpl_->removeWaitingCall(callId);
1152 auto call = account->getCall(callId);
1154 JAMI_WARN(
"Unable to hang up nonexistent call %s", callId.c_str());
1161 if (call->isConferenceParticipant()) {
1162 removeParticipant(*call);
1165 if (isCurrentCall(*call))
1166 pimpl_->unsetCurrentCall();
1180Manager::hangupConference(
const std::string& accountId,
const std::string& confId)
1182 if (
auto account = getAccount(accountId)) {
1183 if (
auto conference = account->getConference(confId)) {
1184 return pimpl_->hangupConference(*conference);
1194Manager::onHoldCall(
const std::string&,
const std::string& callId)
1200 std::string current_callId(getCurrentCallId());
1202 if (
auto call = getCallFromCallID(callId)) {
1204 result = call->
onhold([=](
bool ok) {
1206 JAMI_ERR(
"Hold failed for call %s", callId.c_str());
1211 pimpl_->removeWaitingCall(callId);
1215 if (current_callId == callId)
1216 pimpl_->unsetCurrentCall();
1223 JAMI_DBG(
"CallID %s doesn't exist in call onHold", callId.c_str());
1232Manager::offHoldCall(
const std::string&,
const std::string& callId)
1238 std::shared_ptr<Call> call = getCallFromCallID(callId);
1243 result = call->offhold([=](
bool ok) {
1245 JAMI_ERR(
"offHold failed for call %s", callId.c_str());
1249 if (
auto conf = call->getConference())
1252 pimpl_->switchCall(call->getCallId());
1266Manager::transferCall(
const std::string& accountId,
const std::string& callId,
const std::string& to)
1268 auto account = getAccount(accountId);
1271 if (
auto call = account->getCall(callId)) {
1273 removeParticipant(*call);
1279 pimpl_->removeWaitingCall(callId);
1285Manager::transferFailed()
1287 emitSignal<libjami::CallSignal::TransferFailed>();
1291Manager::transferSucceeded()
1293 emitSignal<libjami::CallSignal::TransferSucceeded>();
1298Manager::refuseCall(
const std::string& accountId,
const std::string&
id)
1300 if (
auto account = getAccount(accountId)) {
1301 if (
auto call = account->getCall(
id)) {
1304 pimpl_->removeWaitingCall(
id);
1313Manager::holdConference(
const std::string& accountId,
const std::string& confId)
1315 JAMI_INFO(
"Hold conference %s", confId.c_str());
1317 if (
const auto account = getAccount(accountId)) {
1318 if (
auto conf = account->getConference(confId)) {
1320 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId,
1330Manager::unHoldConference(
const std::string& accountId,
const std::string& confId)
1332 JAMI_DBG(
"[conf:%s] Unholding conference", confId.c_str());
1334 if (
const auto account = getAccount(accountId)) {
1335 if (
auto conf = account->getConference(confId)) {
1338 if (conf->
getState() == Conference::State::HOLD) {
1340 offHoldCall(accountId, item);
1342 pimpl_->switchCall(confId);
1343 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1344 emitSignal<libjami::CallSignal::ConferenceChanged>(accountId,
1348 }
else if (conf->
getState() == Conference::State::ACTIVE_DETACHED) {
1349 pimpl_->addMainParticipant(*conf);
1357Manager::addSubCall(
const std::string& accountId,
1358 const std::string& callId,
1359 const std::string& account2Id,
1360 const std::string& conferenceId)
1362 auto account = getAccount(accountId);
1363 auto account2 = getAccount(account2Id);
1364 if (account && account2) {
1365 auto call = account->getCall(callId);
1366 auto conf = account2->getConference(conferenceId);
1370 if (callConf != conf)
1371 return addSubCall(*call, *conf);
1382 pimpl_->bindCallToConference(call, conference);
1385 if (conference.
getState() == Conference::State::ACTIVE_DETACHED) {
1392 pimpl_->unsetCurrentCall();
1393 pimpl_->addMainParticipant(conference);
1394 pimpl_->switchCall(conference.
getConfId());
1404 emitSignal<libjami::CallSignal::ConferenceChanged>(conf.
getAccountId(),
1416 if (subcalls.empty()) {
1418 account->removeConference(conference.
getConfId());
1420 for (
const auto& callId : subcalls) {
1421 if (
auto call = base_.getCallFromCallID(callId))
1429Manager::addMainParticipant(
const std::string& accountId,
const std::string& conferenceId)
1431 JAMI_INFO(
"Add main participant to conference %s", conferenceId.c_str());
1433 if (
auto account = getAccount(accountId)) {
1434 if (
auto conf = account->getConference(conferenceId)) {
1435 pimpl_->addMainParticipant(*conf);
1436 JAMI_DBG(
"Successfully added main participant to conference %s", conferenceId.c_str());
1439 JAMI_WARN(
"Failed to add main participant to conference %s", conferenceId.c_str());
1444std::shared_ptr<Call>
1445Manager::getCallFromCallID(
const std::string& callID)
const
1447 return callFactory.getCall(callID);
1451Manager::joinParticipant(
const std::string& accountId,
1452 const std::string& callId1,
1453 const std::string& account2Id,
1454 const std::string& callId2,
1457 JAMI_INFO(
"JoinParticipant(%s, %s, %i)", callId1.c_str(), callId2.c_str(), attached);
1458 auto account = getAccount(accountId);
1459 auto account2 = getAccount(account2Id);
1460 if (not account or not account2) {
1464 JAMI_INFO(
"Creating conference for participants %s and %s. Attach host [%s]",
1467 attached ?
"YES" :
"NO");
1469 if (callId1 == callId2) {
1470 JAMI_ERR(
"Unable to join participant %s to itself", callId1.c_str());
1475 auto call1 = account->getCall(callId1);
1477 JAMI_ERR(
"Unable to find call %s", callId1.c_str());
1482 auto call2 = account2->getCall(callId2);
1484 JAMI_ERR(
"Unable to find call %s", callId2.c_str());
1488 auto mediaAttr = call1->getMediaAttributeList();
1489 if (mediaAttr.empty())
1490 mediaAttr = call2->getMediaAttributeList();
1491 auto conf = std::make_shared<Conference>(account);
1493 account->attach(conf);
1494 emitSignal<libjami::CallSignal::ConferenceCreated>(account->getAccountID(),
"", conf->
getConfId());
1497 pimpl_->bindCallToConference(*call1, *conf);
1498 pimpl_->bindCallToConference(*call2, *conf);
1503 conf->
setState(Conference::State::ACTIVE_ATTACHED);
1507 emitSignal<libjami::CallSignal::ConferenceChanged>(account->getAccountID(),
1515Manager::createConfFromParticipantList(
const std::string& accountId,
1516 const std::vector<std::string>& participantList)
1518 auto account = getAccount(accountId);
1525 if (participantList.size() <= 1) {
1526 JAMI_ERR(
"Participant number must be greater than or equal to 2");
1530 auto conf = std::make_shared<Conference>(account);
1533 unsigned successCounter = 0;
1534 for (
const auto& numberaccount : participantList) {
1535 std::string tostr(numberaccount.substr(0, numberaccount.find(
',')));
1536 std::string account(numberaccount.substr(numberaccount.find(
',') + 1, numberaccount.size()));
1538 pimpl_->unsetCurrentCall();
1541 auto callId = outgoingCall(account, tostr, {});
1551 if (successCounter >= 2) {
1552 account->attach(conf);
1553 emitSignal<libjami::CallSignal::ConferenceCreated>(accountId,
"", conf->
getConfId());
1558Manager::detachHost(
const std::shared_ptr<Conference>& conf)
1563 JAMI_LOG(
"Detach local participant from conference {}", conf->getConfId());
1565 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(),
1567 conf->getStateStr());
1568 pimpl_->unsetCurrentCall();
1573Manager::detachParticipant(
const std::string& callId)
1575 JAMI_DBG(
"Detach participant %s", callId.c_str());
1577 auto call = getCallFromCallID(callId);
1579 JAMI_ERR(
"Unable to find call %s", callId.c_str());
1587 removeParticipant(*call);
1592Manager::removeParticipant(
Call& call)
1598 JAMI_ERR(
"No conference, unable to remove participant");
1606 emitSignal<libjami::CallSignal::ConferenceChanged>(conf->getAccountId(),
1608 conf->getStateStr());
1610 pimpl_->processRemainingParticipants(*conf);
1614Manager::joinConference(
const std::string& accountId,
1615 const std::string& confId1,
1616 const std::string& account2Id,
1617 const std::string& confId2)
1619 auto account = getAccount(accountId);
1620 auto account2 = getAccount(account2Id);
1622 JAMI_ERR(
"Unable to find account: %s", accountId.c_str());
1626 JAMI_ERR(
"Unable to find account: %s", account2Id.c_str());
1630 auto conf = account->getConference(confId1);
1632 JAMI_ERR(
"Invalid conference ID: %s", confId1.c_str());
1636 auto conf2 = account2->getConference(confId2);
1638 JAMI_ERR(
"Invalid conference ID: %s", confId2.c_str());
1642 CallIdSet subcalls(conf->getSubCalls());
1644 std::vector<std::shared_ptr<Call>> calls;
1645 calls.reserve(subcalls.size());
1649 for (
const auto& callId : subcalls) {
1651 if (
auto call = account->getCall(callId)) {
1652 conf->removeSubCall(callId);
1654 calls.emplace_back(std::move(call));
1656 JAMI_ERROR(
"Unable to find call {}", callId);
1660 account->removeConference(confId1);
1662 for (
const auto& c : calls)
1663 addSubCall(*c, *conf2);
1674 JAMI_LOG(
"Add audio to call {}", callId);
1678 for (
const auto& media : medias) {
1679 JAMI_DEBUG(
"[call:{}] Attach audio", media.first);
1680 getRingBufferPool().bindRingBuffers(media.first, RingBufferPool::DEFAULT_ID);
1683 call.
audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1685 std::lock_guard lock(pimpl_->audioLayerMutex_);
1686 if (!pimpl_->audiodriver_) {
1690 pimpl_->audiodriver_->flushUrgent();
1691 getRingBufferPool().flushAllBuffers();
1699 for (
const auto& media : medias) {
1700 JAMI_DEBUG(
"[call:{}] Remove local audio {}", callId, media.first);
1701 getRingBufferPool().unBindAll(media.first);
1708 return pimpl_->scheduler_;
1711std::shared_ptr<asio::io_context>
1712Manager::ioContext()
const
1714 return pimpl_->ioContext_;
1717std::shared_ptr<dhtnet::upnp::UPnPContext>
1718Manager::upnpContext()
const
1720 return pimpl_->upnpContext_;
1723std::shared_ptr<Task>
1724Manager::scheduleTask(std::function<
void()>&& task,
1725 std::chrono::steady_clock::time_point when,
1726 const char* filename,
1729 return pimpl_->scheduler_.
schedule(std::move(task), when, filename, linum);
1732std::shared_ptr<Task>
1733Manager::scheduleTaskIn(std::function<
void()>&& task,
1734 std::chrono::steady_clock::duration timeout,
1735 const char* filename,
1738 return pimpl_->scheduler_.scheduleIn(std::move(task), timeout, filename, linum);
1742Manager::saveConfig(
const std::shared_ptr<Account>& acc)
1744 if (
auto ringAcc = std::dynamic_pointer_cast<JamiAccount>(acc))
1745 ringAcc->saveConfig();
1751Manager::saveConfig()
1753 JAMI_LOG(
"Saving configuration to '{}'", pimpl_->path_);
1755 if (pimpl_->audiodriver_) {
1756 audioPreference.setVolumemic(pimpl_->audiodriver_->getCaptureGain());
1757 audioPreference.setVolumespkr(pimpl_->audiodriver_->getPlaybackGain());
1758 audioPreference.setCaptureMuted(pimpl_->audiodriver_->isCaptureMuted());
1759 audioPreference.setPlaybackMuted(pimpl_->audiodriver_->isPlaybackMuted());
1766 out << YAML::BeginMap << YAML::Key <<
"accounts";
1767 out << YAML::Value << YAML::BeginSeq;
1769 for (
const auto& account : accountFactory.getAllAccounts()) {
1770 if (
auto jamiAccount = std::dynamic_pointer_cast<JamiAccount>(account)) {
1771 auto accountConfig = jamiAccount->getPath() /
"config.yml";
1772 if (not std::filesystem::is_regular_file(accountConfig)) {
1773 saveConfig(jamiAccount);
1776 account->config().serialize(out);
1779 out << YAML::EndSeq;
1782 preferences.verifyAccountOrder(getAccountList());
1783 preferences.serialize(out);
1784 voipPreferences.serialize(out);
1785 audioPreference.serialize(out);
1787 videoPreferences.serialize(out);
1790 pluginPreferences.serialize(out);
1793 std::lock_guard lock(dhtnet::fileutils::getFileLock(pimpl_->path_));
1794 std::ofstream fout(pimpl_->path_);
1795 fout.write(out.c_str(), out.size());
1796 }
catch (
const YAML::Exception& e) {
1798 }
catch (
const std::runtime_error& e) {
1805Manager::playDtmf(
char code)
1809 if (not voipPreferences.getPlayDtmf()) {
1810 JAMI_DBG(
"Do not have to play a tone…");
1815 int pulselen = voipPreferences.getPulseLength();
1817 if (pulselen == 0) {
1818 JAMI_DBG(
"Pulse length is not set…");
1822 std::lock_guard lock(pimpl_->audioLayerMutex_);
1825 if (not pimpl_->audiodriver_ or not pimpl_->dtmfKey_) {
1830 std::shared_ptr<AudioDeviceGuard> audioGuard = startAudioStream(AudioDeviceType::PLAYBACK);
1831 if (not pimpl_->audiodriver_->waitForStart(std::chrono::seconds(1))) {
1832 JAMI_ERR(
"Failed to start audio layer…");
1840 unsigned size = (unsigned) ((pulselen * (
long) pimpl_->audiodriver_->getSampleRate()) / 1000ul);
1841 if (!pimpl_->dtmfBuf_ or pimpl_->dtmfBuf_->getFrameSize() != size)
1842 pimpl_->dtmfBuf_ = std::make_shared<AudioFrame>(pimpl_->audiodriver_->getFormat(), size);
1845 pimpl_->dtmfKey_->startTone(code);
1848 if (pimpl_->dtmfKey_->generateDTMF(pimpl_->dtmfBuf_->pointer())) {
1854 pimpl_->audiodriver_->putUrgent(pimpl_->dtmfBuf_);
1857 scheduler().scheduleIn([audioGuard] {
JAMI_WARN(
"End of dtmf"); },
1858 std::chrono::milliseconds(pulselen));
1865Manager::incomingCallsWaiting()
1867 std::lock_guard m(pimpl_->waitingCallsMutex_);
1868 return not pimpl_->waitingCalls_.empty();
1872Manager::incomingCall(
const std::string& accountId,
Call& call)
1874 if (not accountId.empty()) {
1875 pimpl_->stripSipPrefix(call);
1878 auto const& account = getAccount(accountId);
1880 JAMI_ERROR(
"Incoming call {} on unknown account {}",
1887 pimpl_->processIncomingCall(accountId, call);
1891Manager::incomingMessage(
const std::string& accountId,
1892 const std::string& callId,
1893 const std::string& from,
1894 const std::map<std::string, std::string>& messages)
1896 auto account = getAccount(accountId);
1900 if (
auto call = account->getCall(callId)) {
1903 JAMI_DBG(
"Is a conference, send incoming message to everyone");
1906 bool sendToOtherParicipants =
true;
1907 for (
auto& message : messages) {
1908 if (message.first.find(
"x-ring/ring.profile.vcard") != std::string::npos) {
1909 sendToOtherParicipants =
false;
1912 if (sendToOtherParicipants) {
1913 pimpl_->sendTextMessageToConference(*conf, messages, from);
1917 emitSignal<libjami::CallSignal::IncomingMessage>(accountId,
1922 JAMI_ERR(
"No conference associated to ID %s", callId.c_str());
1925 emitSignal<libjami::CallSignal::IncomingMessage>(accountId, callId, from, messages);
1931Manager::sendCallTextMessage(
const std::string& accountId,
1932 const std::string& callID,
1933 const std::map<std::string, std::string>& messages,
1934 const std::string& from,
1937 auto account = getAccount(accountId);
1942 if (
auto conf = account->getConference(callID)) {
1943 JAMI_DBG(
"Is a conference, send instant message to everyone");
1944 pimpl_->sendTextMessageToConference(*conf, messages, from);
1945 }
else if (
auto call = account->getCall(callID)) {
1948 JAMI_DBG(
"Call is participant in a conference, send instant message to everyone");
1949 pimpl_->sendTextMessageToConference(*conf, messages, from);
1951 JAMI_ERR(
"No conference associated to call ID %s", callID.c_str());
1957 JAMI_ERR(
"Failed to send message to call %s: %s",
1963 JAMI_ERR(
"Failed to send message to %s: nonexistent call ID", callID.c_str());
1969Manager::peerAnsweredCall(
Call& call)
1972 JAMI_DBG(
"[call:%s] Peer answered", callId.c_str());
1975 if (isCurrentCall(call))
1980 if (pimpl_->audiodriver_) {
1981 std::lock_guard lock(pimpl_->audioLayerMutex_);
1982 getRingBufferPool().flushAllBuffers();
1983 pimpl_->audiodriver_->flushUrgent();
1986 if (audioPreference.getIsAlwaysRecording()) {
1988 emitSignal<libjami::CallSignal::RecordPlaybackFilepath>(callId, call.
getPath());
1989 emitSignal<libjami::CallSignal::RecordingStateChanged>(callId, result);
1995Manager::peerRingingCall(
Call& call)
1998 if (!hasCurrentCall())
2007 JAMI_LOG(
"[call:{}] Peer hung up", callId);
2010 removeParticipant(call);
2011 }
else if (isCurrentCall(call)) {
2013 pimpl_->unsetCurrentCall();
2018 pimpl_->removeWaitingCall(callId);
2019 if (not incomingCallsWaiting())
2031 if (isCurrentCall(call)) {
2032 pimpl_->unsetCurrentCall();
2035 pimpl_->removeWaitingCall(call.
getCallId());
2036 if (not incomingCallsWaiting())
2046 call.
isSubcall() ?
"Sub-call" :
"Parent call");
2048 if (isCurrentCall(call)) {
2049 pimpl_->unsetCurrentCall();
2055 removeParticipant(call);
2058 pimpl_->removeWaitingCall(call.
getCallId());
2059 if (not call.
isSubcall() && not incomingCallsWaiting())
2070 if (not voipPreferences.getPlayTones())
2073 pimpl_->toneCtrl_.stop();
2074 pimpl_->toneDeviceGuard_.reset();
2083 pimpl_->playATone(Tone::ToneId::DIALTONE);
2090Manager::playToneWithMessage()
2092 pimpl_->playATone(Tone::ToneId::CONGESTION);
2099Manager::congestion()
2101 pimpl_->playATone(Tone::ToneId::CONGESTION);
2110 pimpl_->playATone(Tone::ToneId::RINGTONE);
2117Manager::playRingtone(
const std::string& accountID)
2119 const auto account = getAccount(accountID);
2121 JAMI_WARN(
"Invalid account in ringtone");
2125 if (!account->getRingtoneEnabled()) {
2131 std::lock_guard lock(pimpl_->audioLayerMutex_);
2133 if (not pimpl_->audiodriver_) {
2134 JAMI_ERR(
"No audio layer in ringtone");
2138 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2139 pimpl_->toneDeviceGuard_ = startAudioStream(AudioDeviceType::RINGTONE);
2140 auto format = pimpl_->audiodriver_->getFormat();
2141 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2144 if (not pimpl_->toneCtrl_.setAudioFile(account->getRingtonePath().string()))
2148std::shared_ptr<AudioLoop>
2149Manager::getTelephoneTone()
2151 return pimpl_->toneCtrl_.getTelephoneTone();
2154std::shared_ptr<AudioLoop>
2155Manager::getTelephoneFile()
2157 return pimpl_->toneCtrl_.getTelephoneFile();
2164Manager::setAudioPlugin(
const std::string& audioPlugin)
2167 std::lock_guard lock(pimpl_->audioLayerMutex_);
2168 audioPreference.setAlsaPlugin(audioPlugin);
2169 pimpl_->audiodriver_.reset();
2170 pimpl_->initAudioDriver();
2182 std::lock_guard lock(pimpl_->audioLayerMutex_);
2184 if (not pimpl_->audiodriver_) {
2185 JAMI_ERR(
"Uninitialized audio driver");
2188 if (pimpl_->getCurrentDeviceIndex(type) == index) {
2189 JAMI_WARN(
"Audio device already selected, doing nothing.");
2193 pimpl_->audiodriver_->updatePreference(audioPreference, index, type);
2196 pimpl_->audiodriver_.reset();
2197 pimpl_->initAudioDriver();
2204std::vector<std::string>
2205Manager::getAudioOutputDeviceList()
2207 std::lock_guard lock(pimpl_->audioLayerMutex_);
2209 if (not pimpl_->audiodriver_) {
2210 JAMI_ERR(
"Uninitialized audio layer");
2214 return pimpl_->audiodriver_->getPlaybackDeviceList();
2220std::vector<std::string>
2221Manager::getAudioInputDeviceList()
2223 std::lock_guard lock(pimpl_->audioLayerMutex_);
2225 if (not pimpl_->audiodriver_) {
2226 JAMI_ERR(
"Uninitialized audio layer");
2230 return pimpl_->audiodriver_->getCaptureDeviceList();
2236std::vector<std::string>
2237Manager::getCurrentAudioDevicesIndex()
2239 std::lock_guard lock(pimpl_->audioLayerMutex_);
2240 if (not pimpl_->audiodriver_) {
2241 JAMI_ERR(
"Uninitialized audio layer");
2245 return {std::to_string(pimpl_->audiodriver_->getIndexPlayback()),
2246 std::to_string(pimpl_->audiodriver_->getIndexCapture()),
2247 std::to_string(pimpl_->audiodriver_->getIndexRingtone())};
2251Manager::startAudio()
2253 if (!pimpl_->audiodriver_)
2254 pimpl_->audiodriver_.reset(pimpl_->base_.audioPreference.createAudioLayer());
2255 constexpr std::array<AudioDeviceType, 3> TYPES {AudioDeviceType::CAPTURE,
2256 AudioDeviceType::PLAYBACK,
2257 AudioDeviceType::RINGTONE};
2258 for (
const auto& type : TYPES)
2259 if (pimpl_->audioStreamUsers_[(
unsigned) type])
2260 pimpl_->audiodriver_->startStream(type);
2268 if (streamId >= manager_.pimpl_->audioStreamUsers_.size())
2269 throw std::invalid_argument(
"Invalid audio device type");
2270 if (manager_.pimpl_->audioStreamUsers_[streamId]++ == 0) {
2272 layer->startStream(type);
2279 if (--manager_.pimpl_->audioStreamUsers_[streamId] == 0) {
2281 layer->stopStream(type_);
2301 bool result =
false;
2303 std::shared_ptr<Recordable>
rec;
2307 }
else if (
auto call =
account->getCall(
id)) {
2311 JAMI_ERR(
"Unable to find recordable instance %s",
id.
c_str());
2314 result =
rec->toggleRecording();
2327 std::lock_guard lock(pimpl_->audioLayerMutex_);
2329 if (
not pimpl_->audiodriver_) {
2330 JAMI_ERR(
"No audio layer in start recorded file playback");
2334 auto oldGuard = std::move(pimpl_->toneDeviceGuard_);
2336 auto format = pimpl_->audiodriver_->getFormat();
2337 pimpl_->toneCtrl_.setSampleRate(format.sample_rate, format.sampleFormat);
2340 return pimpl_->toneCtrl_.setAudioFile(
filepath);
2346 pimpl_->toneCtrl_.seek(
value);
2352 JAMI_DBG(
"Stop recorded file playback");
2354 pimpl_->toneCtrl_.stopAudioFile();
2355 pimpl_->toneDeviceGuard_.reset();
2390 std::lock_guard lock(pimpl_->audioLayerMutex_);
2392 if (
not pimpl_->audiodriver_)
2396 JAMI_DBG(
"Audio manager chosen already in use. No changes made.");
2402 std::lock_guard lock(pimpl_->audioLayerMutex_);
2404 pimpl_->audiodriver_.reset();
2405 pimpl_->initAudioDriver();
2423 std::lock_guard lock(pimpl_->audioLayerMutex_);
2425 if (
not pimpl_->audiodriver_) {
2426 JAMI_ERR(
"Uninitialized audio layer");
2436 std::lock_guard lock(pimpl_->audioLayerMutex_);
2438 if (
not pimpl_->audiodriver_) {
2439 JAMI_ERR(
"Uninitialized audio layer");
2486 for (
const auto& type :
TYPES)
2537 JAMI_DEBUG(
"Incoming call {} on account {} with {} media",
2547 if (
not base_.hasCurrentCall()) {
2549#if !(defined(TARGET_OS_IOS) && TARGET_OS_IOS)
2551 base_.playRingtone(accountId);
2554 if (
account->isDenySecondCallEnabled()) {
2562 if (
account->isRendezVous()) {
2564 base_.answerCall(*incomCall);
2566 for (const auto& callId : account->getCallList()) {
2567 if (auto call = account->getCall(callId)) {
2568 if (call->getState() != Call::CallState::ACTIVE)
2570 if (call != incomCall) {
2571 if (auto conf = call->getConference()) {
2572 base_.addSubCall(*incomCall, *conf);
2574 base_.joinParticipant(account->getAccountID(),
2575 incomCall->getCallId(),
2576 account->getAccountID(),
2586 auto conf = std::make_shared<Conference>(
account);
2596 conf->getStateStr());
2598 }
else if (autoAnswer_ ||
account->isAutoAnswerEnabled()) {
2599 dht::ThreadPool::io().run(
2622 auto& mgr = Manager::instance();
2623 mgr.answerCall(*incomCall);
2624 mgr.hangupCall(accountId, currentCallID);
2650 pimpl_->ringbufferpool_->setInternalAudioFormat(format);
2660 JAMI_LOG(
"Set accounts order: {}", order);
2666std::vector<std::string>
2670 std::vector<std::string> v;
2673 v.emplace_back(
account->getAccountID());
2679std::map<std::string, std::string>
2685 return account->getAccountDetails();
2687 JAMI_ERR(
"Unable to get account details on a nonexistent accountID %s",
accountID.c_str());
2693std::map<std::string, std::string>
2699 return account->getVolatileAccountDetails();
2701 JAMI_ERR(
"Unable to get volatile account details on a nonexistent accountID %s",
2709 const std::map<std::string, std::string>&
details)
2740 return dht::crypto::getDerivedRandomEngine(rand_);
2771 JAMI_ERROR(
"Unknown {:s} param when calling addAccount(): {:s}",
2794 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(
remAccount)) {
2817std::vector<std::string_view>
2838 }
catch (
const YAML::Exception&
e) {
2839 JAMI_ERR(
"Preferences node unserialize YAML exception: %s",
e.what());
2841 }
catch (
const std::exception&
e) {
2842 JAMI_ERR(
"Preferences node unserialize standard exception: %s",
e.what());
2845 JAMI_ERR(
"Preferences node unserialize unknown exception");
2861 std::condition_variable cv;
2864 std::unique_lock
l(lock);
2865 for (
const auto&
dir :
dirs) {
2870 dht::ThreadPool::computation().run(
2872 if (std::filesystem::is_regular_file(
configFile)) {
2876 auto config =
a->buildConfig();
2878 a->setConfig(std::move(config));
2880 }
catch (
const std::exception&
e) {
2881 JAMI_ERR(
"Unable to import account %s: %s",
dir.c_str(),
e.what());
2884 std::lock_guard
l(lock);
2900std::vector<std::string>
2903 std::vector<std::string>
results;
2927 acc->setEnabled(enable);
2930 if (acc->isEnabled()) {
2933 acc->doUnregister();
2938 const std::string& to,
2939 const std::map<std::string, std::string>& payloads,
2948 auto cm = std::make_shared<JamiMessage>(
accountID, to,
false, payloads, fromPlugin);
2953 return acc->sendTextMessage(to,
"", payloads, 0,
onlyConnected);
2954 }
catch (
const std::exception&
e) {
2955 JAMI_ERR(
"Exception during text message sending: %s",
e.what());
2964 JAMI_ERROR(
"Deprecated method. Please use status from message");
2971 JAMI_ERROR(
"Deprecated method. Please use status from message");
2979 if (!acc || acc->isActive() == active)
2981 acc->setActive(active);
2982 if (acc->isEnabled()) {
2986 acc->doUnregister(shutdownConnections);
2990 accountID, acc->getVolatileAccountDetails());
2996 const std::string&
convId)
3011 account->enableAutoLoadConversations(
false);
3013 auto config =
account->buildConfig();
3015 account->setConfig(std::move(config));
3017 }
catch (
const std::runtime_error&
e) {
3018 JAMI_WARN(
"Failed to load account: %s",
e.what());
3024 JAMI_WARN(
"Unable to load account %s", accountId.c_str());
3028 if (
auto jamiAcc = std::dynamic_pointer_cast<JamiAccount>(
account)) {
3033 if (
auto convModule =
jamiAcc->convModule()) {
3034 convModule->reloadRequests();
3036 convModule->loadConversations();
3037 }
else if (!
convId.empty()) {
3044std::shared_ptr<AudioLayer>
3047 return pimpl_->audiodriver_;
3050std::shared_ptr<Call>
3052 const std::string& accountId,
3053 const std::vector<libjami::MediaMap>&
mediaList)
3057 JAMI_WARN(
"No account matches ID %s", accountId.c_str());
3062 JAMI_WARN(
"Account %s is unusable", accountId.c_str());
3070std::shared_ptr<video::SinkClient>
3071Manager::createSinkClient(
const std::string&
id,
bool mixer)
3073 const auto&
iter = pimpl_->sinkMap_.find(
id);
3074 if (
iter != std::end(pimpl_->sinkMap_)) {
3075 if (
auto sink =
iter->second.lock())
3077 pimpl_->sinkMap_.erase(
iter);
3080 auto sink = std::make_shared<video::SinkClient>(
id,
mixer);
3081 pimpl_->sinkMap_.emplace(
id,
sink);
3086Manager::createSinkClients(
3087 const std::string& callId,
3088 const ConfInfo& infos,
3089 const std::vector<std::shared_ptr<video::VideoFrameActiveWriter>>&
videoStreams,
3090 std::map<std::string, std::shared_ptr<video::SinkClient>>&
sinksMap,
3091 const std::string& accountId)
3093 std::lock_guard
lk(pimpl_->sinksMutex_);
3099 if (sinkId.empty()) {
3145std::shared_ptr<video::SinkClient>
3146Manager::getSinkClient(
const std::string&
id)
3148 const auto&
iter = pimpl_->sinkMap_.find(
id);
3149 if (
iter != std::end(pimpl_->sinkMap_))
3150 if (
auto sink =
iter->second.lock())
3159 return *pimpl_->ringbufferpool_;
3168const std::shared_ptr<dhtnet::IceTransportFactory>&
3171 return pimpl_->ice_tf_;
3177 return pimpl_->videoManager_.get();
3180std::vector<libjami::Message>
3191 return *pimpl_->sipLink_;
3196Manager::getJamiPluginManager()
const
3198 return *pimpl_->jami_plugin_manager;
3202std::shared_ptr<dhtnet::ChannelSocket>
3204 std::string_view deviceId,
3205 std::string_view conversationId)
3208 if (
auto convModule = acc->convModule(
true))
3209 return convModule->gitSocket(deviceId, conversationId);
3213std::map<std::string, std::string>
3217 return acc->getNearbyPeers();
3226 JAMI_ERR(
"Failed to change default moderator, account %s not found",
accountID.c_str());
3231 acc->addDefaultModerator(
peerURI);
3233 acc->removeDefaultModerator(
peerURI);
3237std::vector<std::string>
3242 JAMI_ERR(
"Failed to get default moderators, account %s not found",
accountID.c_str());
3246 auto set = acc->getDefaultModerators();
3247 return std::vector<std::string>(set.begin(), set.end());
3263 JAMI_ERR(
"Failed to get local moderators, account %s not found",
accountID.c_str());
3266 return acc->isLocalModeratorsEnabled();
3281 JAMI_ERR(
"Failed to get all moderators, account %s not found",
accountID.c_str());
3284 return acc->isAllModerators();
3290 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3291 pimpl_->gitTransports_[
tr] = std::move(
sub);
3297 std::lock_guard
lk(pimpl_->gitTransportsMtx_);
3298 pimpl_->gitTransports_.erase(
tr);
3301dhtnet::tls::CertificateStore&
3307 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.