33#include <dhtnet/ip_utils.h>
34#include <opendht/thread_pool.h>
36#include <system_error>
52 for (
auto& call :
calls) {
55 dht::ThreadPool::io().run([call = std::move(call),
errcode] { call->hangup(
errcode); });
74 , timeoutTimer_(*
Manager::instance().ioContext())
87 if (
ec == asio::error::operation_aborted)
91 JAMI_LOG(
"Call {} is still ringing after timeout, setting state to BUSY",
147 return shared->getAccountID();
167Call::validStateTransition(CallState
newState)
225 std::unique_lock<std::recursive_mutex> lock(
callMutex_);
226 JAMI_DBG(
"[call:%s] state change %u/%u, cnx %u/%u, code %d",
236 JAMI_ERR(
"[call:%s] invalid call state transition from %u to %u",
251 for (
auto it = stateChangedListeners_.begin();
it != stateChangedListeners_.end();) {
255 it = stateChangedListeners_.erase(
it);
347std::map<std::string, std::string>
369 auto it =
messages.find(
"application/confInfo+json");
375 it =
messages.find(
"application/confOrder+json");
436 if (
auto p =
parent.lock()) {
437 if (auto s = sub.lock()) {
438 p->subcallStateChanged(*s, new_state, new_cstate, code);
452Call::subcallStateChanged(Call& subcall, Call::CallState new_state, Call::ConnectionState new_cstate,
int code)
459 std::lock_guard lk {callMutex_};
460 auto sit = subcalls_.find(
getPtr(subcall));
461 if (sit == subcalls_.end())
466 if (new_state == CallState::ACTIVE and new_cstate == ConnectionState::CONNECTED) {
467 JAMI_DBG(
"[call:%s] subcall %s answered by peer", getCallId().c_str(), subcall.getCallId().c_str());
469 hangupCallsIf(safePopSubcalls(), 0, [&](
const Call* call) {
return call != &subcall; });
471 Manager::instance().peerAnsweredCall(*
this);
476 if ((new_state == CallState::ACTIVE or new_state == CallState::PEER_BUSY)
477 and new_cstate == ConnectionState::DISCONNECTED) {
478 JAMI_WARN(
"[call:%s] subcall %s hangup by peer", getCallId().c_str(), subcall.getCallId().c_str());
479 reason_ = new_state == CallState::ACTIVE ?
"declined" :
"busy";
481 Manager::instance().peerHungupCall(*
this);
487 if (new_state >= CallState::BUSY) {
488 if (new_state == CallState::BUSY || new_state == CallState::PEER_BUSY)
489 JAMI_WARN(
"[call:%s] subcall %s busy", getCallId().c_str(), subcall.getCallId().c_str());
491 JAMI_WARN(
"[call:%s] subcall %s failed", getCallId().c_str(), subcall.getCallId().c_str());
492 std::lock_guard lk {callMutex_};
493 subcalls_.erase(
getPtr(subcall));
496 if (subcalls_.empty()) {
497 if (new_state == CallState::BUSY) {
498 setState(CallState::BUSY, ConnectionState::DISCONNECTED, PJSIP_SC_BUSY_HERE);
499 }
else if (new_state == CallState::PEER_BUSY) {
500 setState(CallState::PEER_BUSY, ConnectionState::DISCONNECTED, PJSIP_SC_BUSY_EVERYWHERE);
503 setState(CallState::MERROR, ConnectionState::DISCONNECTED, PJSIP_SC_INTERNAL_SERVER_ERROR);
505 Manager::instance().callFailure(*
this);
508 JAMI_DEBUG(
"[call:{}] Subcalls remaining : {}", getCallId(), subcalls_.size());
515 if (new_state == CallState::ACTIVE && callState_ == CallState::INACTIVE) {
518 if (
static_cast<unsigned>(connectionState_) <
static_cast<unsigned>(new_cstate)
519 and
static_cast<unsigned>(new_cstate) <=
static_cast<unsigned>(ConnectionState::RINGING)) {
529 JAMI_DBG(
"[call:%s] merge subcall %s", getCallId().c_str(), subcall.
getCallId().c_str());
533 if (peerNumber_.empty())
538 std::weak_ptr<Call> subCallWeak = subcall.shared_from_this();
539 runOnMainThread([subCallWeak] {
540 if (
auto subcall = subCallWeak.lock())
549Call::checkPendingIM()
551 std::lock_guard lk {callMutex_};
553 auto state = getStateStr();
557 for (
const auto& msg : pendingInMessages_)
558 Manager::instance().incomingMessage(getAccountId(), getCallId(), getPeerNumber(), msg.first);
559 pendingInMessages_.clear();
561 std::weak_ptr<Call> callWkPtr = shared_from_this();
562 runOnMainThread([callWkPtr, pending = std::move(pendingOutMessages_)] {
563 if (
auto call = callWkPtr.lock())
564 for (
const auto& msg : pending)
565 call->sendTextMessage(msg.first, msg.second);
578 auto state = getStateStr();
579 if (state == StateEvent::RINGING) {
580 Manager::instance().peerRingingCall(*
this);
581 }
else if (state == StateEvent::BUSY) {
582 Manager::instance().callBusy(*
this);
588Call::safePopSubcalls()
590 std::lock_guard lk {callMutex_};
592 auto old_value = std::move(subcalls_);
598Call::setConferenceInfo(
const std::string& msg)
602 if (json::parse(msg, json)) {
603 if (json.isObject()) {
605 if (json.isMember(
"p")) {
606 for (
const auto& participantInfo : json[
"p"]) {
608 if (!participantInfo.isMember(
"uri"))
611 newInfo.emplace_back(pInfo);
614 if (json.isMember(
"v")) {
615 newInfo.
v = json[
"v"].asInt();
616 peerConfProtocol_ = newInfo.
v;
618 if (json.isMember(
"w"))
619 newInfo.
w = json[
"w"].asInt();
620 if (json.isMember(
"h"))
621 newInfo.
h = json[
"h"].asInt();
624 for (
const auto& participantInfo : json) {
626 if (!participantInfo.isMember(
"uri"))
629 newInfo.emplace_back(pInfo);
635 std::lock_guard lk(confInfoMutex_);
636 if (not isConferenceParticipant()) {
638 confInfo_ = std::move(newInfo);
642 createSinks(confInfo_);
646 }
else if (
auto conf = conf_.lock()) {
647 conf->mergeConfInfo(newInfo, getPeerNumber());
653Call::sendConfOrder(
const Json::Value& root)
655 std::map<std::string, std::string> messages;
656 messages[
"application/confOrder+json"] = json::toString(root);
658 auto w = getAccount();
659 auto account = w.lock();
661 sendTextMessage(messages, account->getFromUri());
665Call::sendConfInfo(
const std::string& json)
667 std::map<std::string, std::string> messages;
668 messages[
"application/confInfo+json"] = json;
670 auto w = getAccount();
671 auto account = w.lock();
673 sendTextMessage(messages, account->getFromUri());
Interface to protocol account (ex: SIPAccount) It can be enable on loading or activate after.
void removeCall(Call &call)
Remove given call instance from call list.
asio::steady_timer timeoutTimer_
void onTextMessage(std::map< std::string, std::string > &&messages)
MsgList pendingInMessages_
const std::string & toUsername() const
Get "To" from the invite.
std::string peerNumber_
Number of the peer.
const std::string & getCallId() const
Return a reference on the call id.
virtual bool isCaptureDeviceMuted(const MediaType &mediaType) const =0
std::set< std::shared_ptr< Call >, std::owner_less< std::shared_ptr< Call > > > SubcallSet
CallState getState() const
Get the call state of the call (protected by mutex)
void addSubCall(Call &call)
Attach subcall to this instance.
std::weak_ptr< Account > account_
Associate account ID.
std::string getStateStr() const
virtual void monitor() const =0
ConnectionState
Tell where we're at with the call.
std::chrono::milliseconds getCallDuration() const
ConnectionState getConnectionState() const
Get the connection state of the call (protected by mutex)
CallType type_
Type of the call.
std::shared_ptr< Call > parent_
MultiDevice: list of attached subcall.
bool isSubcall() const
Return true if this call instance is a subcall (internal call for multi-device handling)
virtual std::map< std::string, std::string > getDetails() const
bool isIncoming() const
Tell if the call is incoming.
Call(const std::shared_ptr< Account > &account, const std::string &id, Call::CallType type)
Constructor of a call.
virtual void peerHungup()
Peer has hung up a call.
virtual bool hasVideo() const =0
virtual void removeCall(int code=0)
std::string peerDisplayName_
Peer Display Name.
CallType getCallType() const
std::weak_ptr< Conference > conf_
Unique conference ID, used exclusively in case of a conference.
const std::string & getPeerNumber() const
Get the peer number (destination on outgoing) not protected by mutex (when created)
std::string getAccountId() const
CallType
This determines if the call originated from the local user (OUTGOING) or from some remote peer (INCOM...
virtual bool toggleRecording()
This method must be implemented for this interface as calls and conferences have different behavior.
MsgList pendingOutMessages_
void setConferenceInfo(const std::string &msg)
A Call can be in a conference.
bool setState(CallState call_state, signed code=0)
Set the state of the call (protected by mutex)
const std::string id_
MultiDevice: parent call, nullptr otherwise. Access protected by callMutex_.
CallState callState_
Inactive/Active/Hold/Busy/Error.
ConnectionState connectionState_
Disconnected/Progressing/Trying/Ringing/Connected.
time_t timestamp_start_
MultiDevice: message received by subcall to merged yet.
std::weak_ptr< Account > getAccount() const
time_point duration_start_
void addStateListener(StateListenerCb &&listener)
std::recursive_mutex callMutex_
Protect every attribute that can be changed by two threads.
Manager (controller) of daemon.
void callFailure(Call &call)
Handle played sound when a failure occurs.
static LIBJAMI_TEST_EXPORT Manager & instance()
void incomingMessage(const std::string &accountId, const std::string &callId, const std::string &from, const std::map< std::string, std::string > &messages)
Notify the client with an incoming message.
std::chrono::seconds getRingingTimeout() const
Get ringing timeout (number of seconds after which a call will enter BUSY state if not answered).
virtual bool startRecording(const std::string &path)
Start recording.
virtual void stopRecording()
Stop recording.
virtual bool toggleRecording()
This method must be implemented for this interface as calls and conferences have different behavior.
bool isRecording() const
Return recording state (true/false)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_LOG(formatstr,...)
void setState(const std::string &accountID, const State migrationState)
static void hangupCallsIf(Call::SubcallSet &&calls, int errcode, T pred)
Hangup many calls with same error code, filtered by a predicate.
void emitSignal(Args... args)
static constexpr const char * bool_to_str(bool b) noexcept
std::shared_ptr< Call > getPtr(Call &call)
Obtain a shared smart pointer of instance.
static void hangupCalls(Call::SubcallSet &&callptr_list, int errcode)
Hangup many calls with same error code.
static void runOnMainThread(Callback &&cb)
static constexpr char CONF_ID[]
static constexpr char CALL_TYPE[]
static constexpr char TO_USERNAME[]
static constexpr char TIMESTAMP_START[]
static constexpr char VIDEO_MUTED[]
static constexpr char AUDIO_ONLY[]
static constexpr char CALL_STATE[]
static constexpr char DISPLAY_NAME[]
static constexpr char PEER_NUMBER[]
static constexpr char ACCOUNTID[]
static constexpr char AUDIO_MUTED[]
static constexpr char BUSY[]
static constexpr char OVER[]
static constexpr char RINGING[]
static constexpr char HOLD[]
static constexpr char INACTIVE[]
static constexpr char INCOMING[]
static constexpr char CURRENT[]
static constexpr char FAILURE[]
static constexpr char CONNECTING[]
static constexpr char HUNGUP[]
static constexpr char PEER_BUSY[]
void fromJson(const Json::Value &v)