Commit 82a0e238 authored by Adrien Béraud's avatar Adrien Béraud Committed by Tristan Matthews

sip: transport refactoring

SIPTransport now represents a SIP transport.
The old SIPTransport class becomes SIPTransportBroker.
Makes every sip call have its own transport, to allow IP2IP calls
using TLS.

Refs #53057
Change-Id: I6ae8e1a4c681c6f4f5887772f5b852bd440df13f
parent ff4ee38a
......@@ -204,5 +204,4 @@ void AccountFactory::initIP2IPAccount()
// cache this often used account using a weak_ptr
ip2ip_account_ = createAccount(SIPAccount::ACCOUNT_TYPE,
SIPAccount::IP2IP_PROFILE);
SIPVoIPLink::loadIP2IPSettings();
}
......@@ -192,6 +192,14 @@ SIPAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
toUri = getToUri(to);
family = ipv6 ? pj_AF_INET6() : pj_AF_INET();
std::shared_ptr<SipTransport> t =
#if HAVE_TLS
isTlsEnabled() ? link_->sipTransport->getTlsTransport(tlsListener_, toUri) :
#endif
transport_;
setTransport(t);
call->setTransport(t);
DEBUG("New %s IP to IP call to %s", ipv6?"IPv6":"IPv4", to.c_str());
}
else {
......@@ -204,6 +212,7 @@ SIPAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
else
toUri = getToUri(to);
call->setTransport(transport_);
// FIXME : for now, use the same address family as the SIP tranport
family = pjsip_transport_type_get_af(getTransportType());
......@@ -328,7 +337,7 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
return false;
}
const pjsip_tpselector tp_sel = getTransportSelector();
const pjsip_tpselector tp_sel = SipTransportBroker::getTransportSelector(call->getTransport()->get());
if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
ERROR("Unable to associate transport for invite session dialog");
return false;
......@@ -687,9 +696,21 @@ void SIPAccount::doRegister()
transportType_ = IPv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS;
initTlsConfiguration();
if (!tlsListener_) {
tlsListener_ = link_->sipTransport->getTlsListener(
SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
getTlsSetting());
if (!tlsListener_) {
setRegistrationState(RegistrationState::ERROR_GENERIC);
ERROR("Error creating TLS listener.");
return;
}
}
} else
#endif
{
tlsListener_.reset();
transportType_ = IPv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP;
}
......@@ -703,14 +724,36 @@ void SIPAccount::doRegister()
// In our definition of the ip2ip profile (aka Direct IP Calls),
// no registration should be performed
if (isIP2IP())
if (isIP2IP()) {
// If we use Tls for IP2IP, transports will be created on connection.
if (!tlsEnable_)
setTransport(link_->sipTransport->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
return;
}
try {
WARN("Creating transport");
transport_.reset();
#if HAVE_TLS
if (isTlsEnabled()) {
setTransport(link_->sipTransport->getTlsTransport(tlsListener_, getServerUri()));
} else
#endif
{
setTransport(link_->sipTransport->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
}
if (!transport_)
throw VoipLinkException("Can't create transport");
sendRegister();
} catch (const VoipLinkException &e) {
ERROR("%s", e.what());
setRegistrationState(RegistrationState::ERROR_GENERIC);
return;
}
#ifdef SFL_PRESENCE
......@@ -723,6 +766,7 @@ void SIPAccount::doRegister()
void SIPAccount::doUnregister(std::function<void(bool)> released_cb)
{
tlsListener_.reset();
if (isIP2IP()) {
if (released_cb)
released_cb(false);
......@@ -730,18 +774,20 @@ void SIPAccount::doUnregister(std::function<void(bool)> released_cb)
}
try {
sendUnregister(released_cb);
sendUnregister();
} catch (const VoipLinkException &e) {
ERROR("doUnregister %s", e.what());
setTransport();
if (released_cb)
released_cb(false);
}
// remove the transport from the account
if (transport_)
setTransport();
if (released_cb)
released_cb(true);
}
void SIPAccount::startKeepAliveTimer()
{
if (isTlsEnabled())
return;
......@@ -794,13 +840,6 @@ SIPAccount::sendRegister()
return;
}
try {
link_->sipTransport->createSipTransport(*this);
} catch (const std::runtime_error &e) {
ERROR("%s", e.what());
throw VoipLinkException("Could not create or acquire SIP transport");
}
setRegister(true);
setRegistrationState(RegistrationState::TRYING);
......@@ -826,10 +865,10 @@ SIPAccount::sendRegister()
pjsip_host_port *via = getViaAddr();
DEBUG("Setting VIA sent-by to %.*s:%d", via->host.slen, via->host.ptr, via->port);
if (pjsip_regc_set_via_sent_by(regc, via, transport_) != PJ_SUCCESS)
if (pjsip_regc_set_via_sent_by(regc, via, transport_->get()) != PJ_SUCCESS)
throw VoipLinkException("Unable to set the \"sent-by\" field");
} else if (isStunEnabled()) {
if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_) != PJ_SUCCESS)
if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_->get()) != PJ_SUCCESS)
throw VoipLinkException("Unable to set the \"sent-by\" field");
}
}
......@@ -861,7 +900,7 @@ SIPAccount::sendRegister()
if (pjsip_regc_register(regc, PJ_TRUE, &tdata) != PJ_SUCCESS)
throw VoipLinkException("Unable to initialize transaction data for account registration");
const pjsip_tpselector tp_sel = SipTransport::getTransportSelector(transport_);
const pjsip_tpselector tp_sel = getTransportSelector();
if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
throw VoipLinkException("Unable to set transport");
......@@ -872,7 +911,6 @@ SIPAccount::sendRegister()
}
setRegistrationInfo(regc);
link_->sipTransport->cleanupTransports();
}
void
......@@ -972,13 +1010,11 @@ SIPAccount::onRegister(pjsip_regc_cbparam *param)
}
void
SIPAccount::sendUnregister(std::function<void(bool)> released_cb)
SIPAccount::sendUnregister()
{
// This may occurs if account failed to register and is in state INVALID
if (!isRegistered()) {
setRegistrationState(RegistrationState::UNREGISTERED);
if (released_cb)
released_cb(true);
return;
}
......@@ -1000,13 +1036,6 @@ SIPAccount::sendUnregister(std::function<void(bool)> released_cb)
}
setRegister(false);
// remove the transport from the account
auto transport = getTransport();
setTransport();
link_->sipTransport->cleanupTransports();
if (released_cb)
link_->sipTransport->waitForReleased(transport, released_cb);
}
#if HAVE_TLS
......@@ -1243,7 +1272,7 @@ std::string SIPAccount::getServerUri() const
pj_str_t
SIPAccount::getContactHeader()
{
if (transport_ == nullptr)
if (!transport_)
ERROR("Transport not created yet");
if (contact_.slen and contactOverwritten_)
......@@ -1259,14 +1288,22 @@ SIPAccount::getContactHeader()
std::string address;
pj_uint16_t port;
link_->sipTransport->findLocalAddressFromTransport(transport_, transportType, hostname_, address, port);
link_->sipTransport->findLocalAddressFromTransport(
transport_ ? transport_->get() : nullptr,
transportType,
hostname_,
address, port);
if (not publishedSameasLocal_) {
address = publishedIpAddress_;
port = publishedPort_;
DEBUG("Using published address %s and port %d", address.c_str(), port);
} else if (stunEnabled_) {
link_->sipTransport->findLocalAddressFromSTUN(transport_, &stunServerName_, stunPort_, address, port);
link_->sipTransport->findLocalAddressFromSTUN(
transport_ ? transport_->get() : nullptr,
&stunServerName_,
stunPort_,
address, port);
setPublishedAddress(address);
publishedPort_ = port;
usePublishedAddressPortInVIA();
......@@ -1317,7 +1354,11 @@ SIPAccount::getHostPortFromSTUN(pj_pool_t *pool)
{
std::string addr;
pj_uint16_t port;
link_->sipTransport->findLocalAddressFromSTUN(transport_, &stunServerName_, stunPort_, addr, port);
link_->sipTransport->findLocalAddressFromSTUN(
transport_ ? transport_->get() : nullptr,
&stunServerName_,
stunPort_,
addr, port);
pjsip_host_port result;
pj_strdup2(pool, &result.host, addr.c_str());
result.host.slen = addr.length();
......@@ -1380,19 +1421,13 @@ computeMd5HashFromCredential(const std::string& username,
}
void
SIPAccount::setTransport(pjsip_transport* transport, pjsip_tpfactory* lis)
SIPAccount::setTransport(const std::shared_ptr<SipTransport>& t)
{
// release old transport
if (transport_ && transport_ != transport) {
if (regc_)
pjsip_regc_release_transport(regc_);
pjsip_transport_dec_ref(transport_);
}
if (tlsListener_ && tlsListener_ != lis)
tlsListener_->destroy(tlsListener_);
// set new transport
transport_ = transport;
tlsListener_ = lis;
if (transport_ == t)
return;
if (transport_ && regc_)
pjsip_regc_release_transport(regc_);
SIPAccountBase::setTransport(t);
}
void SIPAccount::setCredentials(const std::vector<std::map<std::string, std::string> >& creds)
......@@ -1794,7 +1829,10 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam *param, pj_pool_t *pool)
pj_assert(contactRewriteMethod_ == 1 or contactRewriteMethod_ == 2);
std::shared_ptr<SipTransport> tmp_tp {nullptr};
if (contactRewriteMethod_ == 1) {
/* Save transport in case we're gonna reuse it */
tmp_tp = transport_;
/* Unregister current contact */
sendUnregister();
destroyRegistrationInfo();
......@@ -1841,6 +1879,7 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam *param, pj_pool_t *pool)
/* Unregister old contact */
try {
tmp_tp = transport_;
sendUnregister();
} catch (const VoipLinkException &e) {
ERROR("%s", e.what());
......
......@@ -174,7 +174,7 @@ class SIPAccount : public SIPAccountBase {
* Build and send SIP unregistration request
* @param destroy_transport If true, attempt to destroy the transport.
*/
void sendUnregister(std::function<void(bool)> cb = std::function<void(bool)>());
void sendUnregister();
const pjsip_cred_info* getCredInfo() const {
return cred_.data();
......@@ -405,7 +405,7 @@ class SIPAccount : public SIPAccountBase {
return keepAliveEnabled_;
}
void setTransport(pjsip_transport* transport = nullptr, pjsip_tpfactory* lis = nullptr);
virtual void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
/* Returns true if the username and/or hostname match this account */
MatchRank matches(const std::string &username, const std::string &hostname, pjsip_endpoint *endpt, pj_pool_t *pool) const;
......
......@@ -235,19 +235,37 @@ SIPAccountBase::getAccountDetails() const
return a;
}
void
SIPAccountBase::onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info *info)
{
DEBUG("Transport state changed to %s for account %s !", SipTransport::stateToStr(state), accountID_.c_str());
if (!SipTransport::isAlive(transport_, state)) {
if (info) {
char err_msg[128];
err_msg[0] = '\0';
pj_str_t descr = pj_strerror(info->status, err_msg, sizeof(err_msg));
ERROR("Transport disconnected: %.*s", descr.slen, descr.ptr);
}
setRegistrationState(RegistrationState::ERROR_GENERIC);
setTransport();
}
}
void
SIPAccountBase::setTransport(pjsip_transport* transport, pjsip_tpfactory* lis)
SIPAccountBase::setTransport(const std::shared_ptr<SipTransport>& t)
{
// release old transport
if (transport_ && transport_ != transport) {
pjsip_transport_dec_ref(transport_);
using namespace std::placeholders;
if (t == transport_)
return;
if (transport_) {
DEBUG("Removing transport from account");
transport_->removeStateListener(reinterpret_cast<uintptr_t>(this));
}
if (tlsListener_ && tlsListener_ != lis)
tlsListener_->destroy(tlsListener_);
// set new transport
transport_ = transport;
tlsListener_ = lis;
transport_ = t;
if (transport_)
transport_->addStateListener(reinterpret_cast<uintptr_t>(this), std::bind(&SIPAccountBase::onTransportStateChanged, this, _1, _2));
}
// returns even number in range [lower, upper]
......
......@@ -126,8 +126,9 @@ public:
*/
SIPAccountBase(const std::string& accountID);
virtual ~SIPAccountBase() = default;
virtual ~SIPAccountBase() {
setTransport();
}
/**
* Create incoming SIPCall.
......@@ -265,12 +266,12 @@ public:
#endif
static void releasePort(uint16_t port);
inline pjsip_transport* getTransport() {
virtual void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
inline const std::shared_ptr<SipTransport>& getTransport() {
return transport_;
}
virtual void setTransport(pjsip_transport* transport = nullptr, pjsip_tpfactory* lis = nullptr);
inline pjsip_transport_type_e getTransportType() const {
return transportType_;
}
......@@ -279,10 +280,11 @@ public:
* Shortcut for SipTransport::getTransportSelector(account.getTransport()).
*/
inline pjsip_tpselector getTransportSelector() {
return SipTransport::getTransportSelector(transport_);
if (!transport_)
return SipTransportBroker::getTransportSelector(nullptr);
return SipTransportBroker::getTransportSelector(transport_->get());
}
protected:
virtual void serialize(YAML::Emitter &out);
virtual void unserialize(const YAML::Node &node);
......@@ -292,14 +294,19 @@ protected:
virtual std::map<std::string, std::string> getAccountDetails() const;
/**
* Voice over IP Link contains a listener thread and calls
* Callback called by the transport layer when the registration
* transport state changes.
*/
std::shared_ptr<SIPVoIPLink> link_;
virtual void onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info *info);
/**
* Pointer to the transport used by this acccount
* Voice over IP Link contains a listener thread and calls
*/
pjsip_transport* transport_ {nullptr};
std::shared_ptr<SIPVoIPLink> link_;
std::shared_ptr<SipTransport> transport_ {};
std::shared_ptr<TlsListener> tlsListener_ {};
/**
* Transport type used for this sip account. Currently supported types:
......@@ -337,11 +344,6 @@ protected:
*/
pj_uint16_t publishedPort_ {DEFAULT_SIP_PORT};
/**
* If a TLS tranport, pointer to the tls listener.
*/
pjsip_tpfactory* tlsListener_ {nullptr};
/**
* The global TLS listener port which can be configured through the IP2IP_PROFILE
*/
......
......@@ -57,6 +57,7 @@ struct pjsip_inv_session;
class Sdp;
class SIPAccountBase;
class SipTransport;
/**
* @file sipcall.h
......@@ -122,6 +123,14 @@ class SIPCall : public Call
void setContactHeader(pj_str_t *contact);
void setTransport(const std::shared_ptr<SipTransport>& t) {
transport_ = t;
}
inline const std::shared_ptr<SipTransport>& getTransport() {
return transport_;
}
void sendSIPInfo(const char *const body, const char *const subtype);
void answer();
......@@ -199,6 +208,13 @@ class SIPCall : public Call
sfl_video::VideoRtpSession videortp_;
#endif
/**
* Hold the transport used for SIP communication.
* Will be different from the account registration transport for
* non-IP2IP calls.
*/
std::shared_ptr<SipTransport> transport_ {};
/**
* The pool to allocate memory, released once call hang up
*/
......
......@@ -32,16 +32,15 @@
#include "siptransport.h"
#include "sipaccount.h"
#include "sipvoiplink.h"
#include "sip_utils.h"
#include "manager.h"
#include "client/configurationmanager.h"
#include "map_utils.h"
#include "ip_utils.h"
#include "array_size.h"
#include <pjsip.h>
#include <pjsip_ua.h>
#include <pjsip/sip_types.h>
#if HAVE_TLS
#include <pjsip/sip_transport_tls.h>
......@@ -53,327 +52,311 @@
#include <stdexcept>
#include <sstream>
#include <algorithm>
#define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); return (VAL); }
SipTransport::SipTransport(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool) :
transportMap_(), transportMapMutex_(), transportDestroyedCv_(), cp_(cp), pool_(pool), endpt_(endpt)
// FIXME: remove this when pjsip_tp_state_callback gives us enough info
static SipTransportBroker* instance = nullptr;
constexpr const char* TRANSPORT_STATE_STR[] = {
"CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE"
};
constexpr const size_t TRANSPORT_STATE_SZ = ARRAYSIZE(TRANSPORT_STATE_STR);
std::string
SipTransportDescr::toString() const
{
std::stringstream ss;
ss << "{" << pjsip_transport_get_type_desc(type) << " on " << interface << ":" << listenerPort << "}";
return ss.str();
}
SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l) :
transport(t), tlsListener(l)
{
pjsip_transport_add_ref(transport);
}
SipTransport::~SipTransport()
{
auto status = pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), SipTransport::tp_state_callback);
if (transport) {
pjsip_transport_shutdown(transport);
pjsip_transport_dec_ref(transport); // ??
DEBUG("Destroying transport (refcount: %u)", pj_atomic_get(transport->ref_cnt));
transport = nullptr;
}
}
bool
SipTransport::isAlive(const std::shared_ptr<SipTransport>& t, pjsip_transport_state state)
{
return state != PJSIP_TP_STATE_DISCONNECTED
#if PJ_VERSION_NUM > (2 << 24 | 1 << 16)
&& state != PJSIP_TP_STATE_SHUTDOWN
&& state != PJSIP_TP_STATE_DESTROY
#else
&& t && t->get()
&& !t->get()->is_shutdown
&& !t->get()->is_destroying
#endif
;
}
const char*
SipTransport::stateToStr(pjsip_transport_state state)
{
return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ-1)];
}
SipTransportBroker::SipTransportBroker(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool) :
cp_(cp), pool_(pool), endpt_(endpt)
{
instance = this;
auto status = pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), SipTransportBroker::tp_state_callback);
if (status != PJ_SUCCESS) {
ERROR("Can't set transport callback");
sip_utils::sip_strerror(status);
}
}
SipTransport::~SipTransport()
SipTransportBroker::~SipTransportBroker()
{
instance = nullptr;
pjsip_tpmgr_set_state_cb(pjsip_endpt_get_tpmgr(endpt_), nullptr);
}
static std::string
transportMapKey(const std::string &interface, int port, pjsip_transport_type_e type)
{
std::ostringstream os;
auto family = pjsip_transport_type_get_af(type);
char af_ver_num = (family == pj_AF_INET6()) ? '6' : '4';
if (type == PJSIP_TRANSPORT_START_OTHER) // STUN
type = (family == pj_AF_INET6()) ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP;
os << interface << ':' << port << ':' << pjsip_transport_get_type_name(type) << af_ver_num;
return os.str();
}
/** Static tranport state change callback */
void
SipTransport::tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
SipTransportBroker::tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
{
SipTransport& this_ = *getSIPVoIPLink()->sipTransport;
this_.transportStateChanged(tp, state, info);
if (!instance) {
ERROR("Can't bubble event: SipTransportBroker instance is null !");
return;
}
instance->transportStateChanged(tp, state, info);
}
void
SipTransport::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
SipTransportBroker::transportStateChanged(pjsip_transport* tp, pjsip_transport_state state, const pjsip_transport_state_info* info)
{
std::lock_guard<std::mutex> lock(transportMapMutex_);
auto transport_key = map_utils::findByValue(transportMap_, tp);
if (transport_key == transportMap_.cend())
return;
WARN("Transport %s -> %s", tp->info, SipTransport::stateToStr(state));
{
std::shared_ptr<SipTransport> transport {};
{
// The mutex is unlocked so the callback can lock it.
std::lock_guard<std::mutex> lock(transportMapMutex_);
auto t = transports_.find(tp);
if (t != transports_.end()) {
transport = t->second.lock();
}
}
// Propagate the event to the appropriate transport.
if (transport)
transport->stateCallback(state, info);
}
#if PJ_VERSION_NUM > (2 << 24 | 1 << 16)
if (state == PJSIP_TP_STATE_SHUTDOWN || state == PJSIP_TP_STATE_DESTROY) {
if (state == PJSIP_TP_STATE_DESTROY) {
#else
if (tp->is_shutdown || tp->is_destroying) {
if (tp->is_destroying) {
#endif
char err_msg[128];
err_msg[0] = '\0';
std::lock_guard<std::mutex> lock(transportMapMutex_);
pj_str_t description;
if (info) {
description = pjsip_strerror(info->status, err_msg, sizeof(err_msg));
// Transport map cleanup
auto t = transports_.find(tp);
if (t != transports_.end() && t->second.expired())
transports_.erase(t);
// If UDP
if (std::strlen(tp->type_name) >= 3 && std::strncmp(tp->type_name, "UDP", 3ul) == 0) {
auto transport_key = std::find_if(udpTransports_.cbegin(), udpTransports_.cend(), [tp](const std::pair<SipTransportDescr, pjsip_transport*>& i) {
return i.second == tp;
});
if (transport_key != udpTransports_.end()) {
transports_.erase(transport_key->second);
udpTransports_.erase(transport_key);
transportDestroyedCv_.notify_all();
}
}
}
}
WARN("Transport was destroyed: {%s} %.*s",
tp->info,
info && description.slen > 0 ? description.slen : 0, err_msg);
transportMap_.erase(transport_key++);
transportDestroyedCv_.notify_all();
std::shared_ptr<SipTransport>
SipTransportBroker::findTransport(pjsip_transport* t)
{
if (!t)
return nullptr;
{
std::lock_guard<std::mutex> lock(transportMapMutex_);
auto i = transports_.find(t);
if (i == transports_.end()) {
auto ret = std::make_shared<SipTransport>(t);
transports_[t] = ret;
return ret;
}
else if (auto spt = i->second.lock())
return spt;
else
return nullptr;
}
}
void
SipTransport::waitForReleased(pjsip_transport* tp, std::function<void(bool)> released_cb)
SipTransportBroker::waitForReleased(const SipTransportDescr& tp, std::function<void(bool)> released_cb)
{
if (!released_cb)
return;
if (!tp) {
released_cb(true);
return;
}
std::vector<pjsip_transport*> to_destroy_all;
std::vector<std::pair<SipTransportDescr, pjsip_transport*>> to_destroy_all;
bool destroyed = false;
{
std::unique_lock<std::mutex> lock(transportMapMutex_);
auto check_destroyed = [&](){
std::vector<pjsip_transport*> to_destroy = _cleanupTransports();
bool _destr = false;
for (auto t : to_destroy) {
if (t == tp) {
_destr = true;
break;
}
}
to_destroy_all.insert(to_destroy_all.end(), to_destroy.begin(), to_destroy.end());
return _destr;
return udpTransports_.find(tp) == udpTransports_.end();
};
destroyed = transportDestroyedCv_.wait_for(lock, std::chrono::milliseconds(50), check_destroyed);
destroyed = transportDestroyedCv_.wait_for(lock, std::chrono::seconds(10), check_destroyed);
if (!destroyed)
destroyed = check_destroyed();
}
for (auto t : to_destroy_all) {
pj_lock_release(t->lock);
pjsip_transport_destroy(t);
}
released_cb(destroyed);
if (released_cb)
released_cb(destroyed);
}
void
SipTransport::createSipTransport(SIPAccountBase &account)
std::shared_ptr<SipTransport>
SipTransportBroker::getUdpTransport(const SipTransportDescr& descr)