Commit b98db961 authored by Mohamed Chibani's avatar Mohamed Chibani Committed by Sébastien Blin
Browse files

ice: share ice instance between subcalls

Currently, when making an outgoing call to a Jami account with
multiple registered devices, a new ice media instance is created
for each subcall. This is not necessary, because all ice instances
will be similar until the peer answers (and provide its candidates)
But when the answer is received from the peer, all ice instances are
destroyed except the instance of the successful subcall, which will
be used to negotiate the media path.
Instead, only one instance will be created for the parent call, and
shared amongst the attached subcalls.

Gitlab: #619

Change-Id: I001c27f69d21b3ea640b189aea401e43c3f6bdef
parent 910a5c5d
......@@ -424,7 +424,10 @@ JamiAccount::newOutgoingCall(std::string_view toUrl,
if (not call)
return {};
newOutgoingCallHelper(call, toUrl);
getIceOptions([=](auto&& opts) {
call->initIceMediaTransport(true, std::forward<IceTransportOptions>(opts));
newOutgoingCallHelper(call, toUrl);
});
return call;
}
......@@ -442,7 +445,10 @@ JamiAccount::newOutgoingCall(std::string_view toUrl, const std::vector<DRing::Me
if (not call)
return {};
newOutgoingCallHelper(call, toUrl);
getIceOptions([=](auto&& opts) {
call->initIceMediaTransport(true, std::forward<IceTransportOptions>(opts));
newOutgoingCallHelper(call, toUrl);
});
return call;
}
......@@ -491,7 +497,6 @@ std::shared_ptr<SIPCall>
JamiAccount::createSubCall(const std::shared_ptr<SIPCall>& mainCall)
{
auto mediaList = MediaAttribute::mediaAttributesToMediaMaps(mainCall->getMediaAttributeList());
if (not mediaList.empty()) {
return Manager::instance().callFactory.newSipCall(shared(),
Call::CallType::OUTGOING,
......@@ -567,6 +572,7 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
auto dummyCall = createSubCall(call);
call->addSubCall(*dummyCall);
dummyCall->setIceMedia(call->getIceMedia());
auto sendRequest =
[this, wCall, toUri, dummyCall = std::move(dummyCall)](const DeviceId& deviceId,
bool eraseDummy) {
......@@ -596,6 +602,7 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
}
});
call->addSubCall(*dev_call);
dev_call->setIceMedia(call->getIceMedia());
{
std::lock_guard<std::mutex> lk(pendingCallsMutex_);
pendingCalls_[deviceId].emplace_back(dev_call);
......@@ -634,6 +641,8 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
dev_call->setTransport(transport);
call->addSubCall(*dev_call);
dev_call->setIceMedia(call->getIceMedia());
// Set the call in PROGRESSING State because the ICE session
// is already ready. Note that this line should be after
// addSubcall() to change the state of the main call
......@@ -654,9 +663,9 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
}
});
auto remoted_address = ice->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
auto remote_address = ice->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
try {
onConnectedOutgoingCall(dev_call, toUri, remoted_address);
onConnectedOutgoingCall(dev_call, toUri, remote_address);
} catch (const VoipLinkException&) {
// In this case, the main scenario is that SIPStartCall failed because
// the ICE is dead and the TLS session didn't send any packet on that dead
......@@ -708,70 +717,55 @@ JamiAccount::onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call,
return;
JAMI_DBG("[call:%s] outgoing call connected to %s", call->getCallId().c_str(), to_id.c_str());
getIceOptions([=](auto&& opts) {
opts.onInitDone = [w = weak(), target = std::move(target), call](bool ok) {
if (!ok) {
JAMI_ERR("ICE medias are not initialized");
return;
}
auto shared = w.lock();
if (!shared or !call)
return;
const auto localAddress = ip_utils::getInterfaceAddr(shared->getLocalInterface(),
target.getFamily());
const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), target.getFamily());
IpAddr addrSdp = shared->getPublishedSameasLocal()
? localAddress
: shared->getPublishedIpAddress(target.getFamily());
IpAddr addrSdp = getPublishedSameasLocal() ? localAddress
: getPublishedIpAddress(target.getFamily());
// fallback on local address
if (not addrSdp)
addrSdp = localAddress;
// fallback on local address
if (not addrSdp)
addrSdp = localAddress;
// Initialize the session using ULAW as default codec in case of early media
// The session should be ready to receive media once the first INVITE is sent, before
// the session initialization is completed
if (!getSystemCodecContainer()->searchCodecByName("PCMA", jami::MEDIA_AUDIO))
JAMI_WARN("Could not instantiate codec for early media");
// Initialize the session using ULAW as default codec in case of early media
// The session should be ready to receive media once the first INVITE is sent, before
// the session initialization is completed
if (!getSystemCodecContainer()->searchCodecByName("PCMA", jami::MEDIA_AUDIO))
JAMI_WARN("Could not instantiate codec for early media");
// Building the local SDP offer
auto& sdp = call->getSDP();
// Building the local SDP offer
auto& sdp = call->getSDP();
sdp.setPublishedIP(addrSdp);
sdp.setPublishedIP(addrSdp);
auto mediaAttrList = call->getMediaAttributeList();
auto mediaAttrList = call->getMediaAttributeList();
if (mediaAttrList.empty()) {
JAMI_ERR("Call [%s] has no media. Abort!", call->getCallId().c_str());
return;
}
if (mediaAttrList.empty()) {
JAMI_ERR("Call [%s] has no media. Abort!", call->getCallId().c_str());
return;
}
if (not sdp.createOffer(mediaAttrList)) {
JAMI_ERR("Could not send outgoing INVITE request for new call");
return;
}
if (not sdp.createOffer(mediaAttrList)) {
JAMI_ERR("Could not send outgoing INVITE request for new call");
return;
}
// Note: pj_ice_strans_create can call onComplete in the same thread
// This means that iceMutex_ in IceTransport can be locked when onInitDone is called
// So, we need to run the call creation in the main thread
// Also, we do not directly call SIPStartCall before receiving onInitDone, because
// there is an inside waitForInitialization that can block the thread.
// Note: avoid runMainThread as SIPStartCall use transportMutex
dht::ThreadPool::io().run(
[w = std::move(w), target = std::move(target), call = std::move(call)] {
auto shared = w.lock();
if (!shared)
return;
call->setIPToIP(true);
call->setPeerNumber(to_id);
// Note: pj_ice_strans_create can call onComplete in the same thread
// This means that iceMutex_ in IceTransport can be locked when onInitDone is called
// So, we need to run the call creation in the main thread
// Also, we do not directly call SIPStartCall before receiving onInitDone, because
// there is an inside waitForInitialization that can block the thread.
// Note: avoid runMainThread as SIPStartCall use transportMutex
dht::ThreadPool::io().run([w = weak(), call = std::move(call), target] {
auto account = w.lock();
if (not account)
return;
if (not shared->SIPStartCall(*call, target)) {
JAMI_ERR("Could not send outgoing INVITE request for new call");
}
});
};
call->setIPToIP(true);
call->setPeerNumber(to_id);
call->initIceMediaTransport(true, std::move(opts));
if (not account->SIPStartCall(*call, target)) {
JAMI_ERR("Could not send outgoing INVITE request for new call");
}
});
}
......@@ -780,7 +774,7 @@ JamiAccount::SIPStartCall(SIPCall& call, const IpAddr& target)
{
JAMI_DBG("Start SIP call [%s]", call.getCallId().c_str());
if (call.isIceEnabled())
if (call.getIceMedia())
call.addLocalIceAttributes();
std::string toUri(getToUri(call.getPeerNumber() + "@"
......
......@@ -2998,12 +2998,6 @@ SIPCall::merge(Call& call)
pj_strcpy(&contactHeader_, &subcall.contactHeader_);
localAudioPort_ = subcall.localAudioPort_;
localVideoPort_ = subcall.localVideoPort_;
{
std::lock_guard<std::mutex> lk(transportMtx_);
resetTransport(std::move(mediaTransport_));
mediaTransport_ = std::move(subcall.mediaTransport_);
}
peerUserAgent_ = subcall.peerUserAgent_;
peerSupportMultiStream_ = subcall.peerSupportMultiStream_;
......@@ -3034,6 +3028,17 @@ SIPCall::remoteHasValidIceAttributes()
return true;
}
void SIPCall::setIceMedia(std::shared_ptr<IceTransport> ice)
{
JAMI_DBG("[call:%s] Setting ICE session [%p]", getCallId().c_str(), ice.get());
std::lock_guard<std::mutex> lk(transportMtx_);
if (not isSubcall()) {
JAMI_ERR("[call:%s] The call is expected to be a sub-call", getCallId().c_str());
}
mediaTransport_ = std::move(ice);
}
void
SIPCall::setupIceResponse()
{
......@@ -3056,12 +3061,13 @@ SIPCall::setupIceResponse()
// Try to use the discovered public address. If not available,
// fallback on local address.
opt.accountPublicAddr = account->getPublishedIpAddress();
if (opt.accountPublicAddr) {
if (opt.accountLocalAddr) {
opt.accountLocalAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(),
opt.accountPublicAddr.getFamily());
} else {
opt.accountLocalAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(),
pj_AF_INET());
// Just set the local address for both, most likely the account is not
// registered.
opt.accountLocalAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(), AF_INET);
opt.accountPublicAddr = opt.accountLocalAddr;
}
......
......@@ -236,6 +236,17 @@ public:
bool remoteHasValidIceAttributes();
void addLocalIceAttributes();
std::shared_ptr<IceTransport> getIceMedia() const
{
std::lock_guard<std::mutex> lk(transportMtx_);
return mediaTransport_;
};
/**
* Set ICE instance. Must be called only for sub-calls
*/
void setIceMedia(std::shared_ptr<IceTransport> ice);
/**
* Setup ICE locally to answer to an ICE offer. The ICE session has
* the controlled role (slave)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment