diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index ece2c7324a7b84bcdac5fb2a259bc39e15377e98..4571f79062900e7b76b93b66da0fd95249995326 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -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() + "@"
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index d036d89ea101d910ac8079768b170c2f5a6c99e2..8419220002148a0e5b1cd72f3b7ab96ac4ce4174 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -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;
     }
 
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 6cb5c817bb29627c54859e9b7b5e343f06523c7f..59647726ce342a5023868ff18b7fa378dcceb91d 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -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)