diff --git a/src/call.cpp b/src/call.cpp
index 03d433ccf50c6ef6938345dbe20c5ac5942f2698..c950270d179e583c855de62b14be1b968c025cee 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -108,7 +108,6 @@ Call::removeCall()
 {
     auto this_ = shared_from_this();
     Manager::instance().callFactory.removeCall(*this);
-    iceTransport_.reset();
     setState(CallState::OVER);
     recAudio_->closeFile();
 }
@@ -340,28 +339,6 @@ Call::getNullDetails()
     };
 }
 
-bool
-Call::initIceTransport(bool master, unsigned channel_num)
-{
-    auto& iceTransportFactory = Manager::instance().getIceTransportFactory();
-    iceTransport_ = iceTransportFactory.createTransport(getCallId().c_str(),
-                                                        channel_num, master,
-                                                        account_.getIceOptions());
-    return static_cast<bool>(iceTransport_);
-}
-
-bool
-Call::isIceRunning() const
-{
-    return iceTransport_ and iceTransport_->isRunning();
-}
-
-std::unique_ptr<IceSocket>
-Call::newIceSocket(unsigned compId)
-{
-    return std::unique_ptr<IceSocket> {new IceSocket(iceTransport_, compId)};
-}
-
 void
 Call::onTextMessage(std::map<std::string, std::string>&& messages)
 {
@@ -497,7 +474,6 @@ Call::merge(Call& subcall)
         std::lock_guard<std::recursive_mutex> lk1 {callMutex_, std::adopt_lock};
         std::lock_guard<std::recursive_mutex> lk2 {subcall.callMutex_, std::adopt_lock};
         pendingInMessages_ = std::move(subcall.pendingInMessages_);
-        iceTransport_ = std::move(subcall.iceTransport_);
         if (peerNumber_.empty())
             peerNumber_ = std::move(subcall.peerNumber_);
         peerDisplayName_ = std::move(subcall.peerDisplayName_);
diff --git a/src/call.h b/src/call.h
index aeb4838b3d6d2556eb4bc31088595e58de710dd3..0d4ac4a0b2201b7cbddfd3593ebbb626b3112427 100644
--- a/src/call.h
+++ b/src/call.h
@@ -31,7 +31,6 @@
 
 #include "recordable.h"
 #include "ip_utils.h"
-#include "ice_transport.h"
 
 #include <mutex>
 #include <map>
@@ -289,16 +288,6 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
 
         void onTextMessage(std::map<std::string, std::string>&& messages);
 
-        virtual bool initIceTransport(bool master, unsigned channel_num=4);
-
-        bool isIceRunning() const;
-
-        std::unique_ptr<IceSocket> newIceSocket(unsigned compId);
-
-        std::shared_ptr<IceTransport> getIceTransport() const {
-            return iceTransport_;
-        }
-
         virtual bool useVideoCodec(const AccountVideoCodecInfo* /*codec*/) const {
             return false;
         }
@@ -319,8 +308,6 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
 
         // TODO all these members are not protected against multi-thread access
 
-        std::shared_ptr<IceTransport> iceTransport_ {};
-
         bool isAudioMuted_{false};
         bool isVideoMuted_{false};
 
diff --git a/src/manager.h b/src/manager.h
index 79326fa2fe752832765ece2f11c23f4387bd3097..28f6197563495536ab6f9db608cef363fe08b958 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -51,6 +51,7 @@ class RingBufferPool;
 class VideoManager;
 class Conference;
 class AudioLoop;
+class IceTransportFactory;
 
 /** Manager (controller) of Ring daemon */
 class Manager {
diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp
index 862c074a043a7070643f4f6b5f2ab1f66b5d4483..3139363dba97416fa4b99508d680bb0ccb5bf925 100644
--- a/src/media/audio/audiolayer.cpp
+++ b/src/media/audio/audiolayer.cpp
@@ -29,6 +29,7 @@
 #include "client/ring_signal.h"
 
 #include <ctime>
+#include <algorithm>
 
 namespace ring {
 
diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index 25965dbc73fd1039d54b188f8b4a6878a12a4d88..22aa6f8d37d9b380bac2248f9a6e8f83ff551f66 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -564,13 +564,13 @@ RingAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std:
 {
     RING_WARN("RingAccount::createOutgoingCall to: %s target: %s",
               to_id.c_str(), target.toString(true).c_str());
-    call->initIceTransport(true);
+    call->initIceMediaTransport(true);
     call->setIPToIP(true);
     call->setPeerNumber(getToUri(to_id+"@"+target.toString(true).c_str()));
     call->initRecFilename(to_id);
 
     const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface());
-    call->setCallMediaLocal(call->getIceTransport()->getDefaultLocalAddress());
+    call->setCallMediaLocal(call->getIceMediaTransport()->getDefaultLocalAddress());
 
     IpAddr addrSdp;
     if (getUPnPActive()) {
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index 1bafebdd9279c39d56c39edffa41aa17c865837b..8d261e2f7fc0f970761f43978e498d7b85f5bdfb 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -200,7 +200,7 @@ SIPAccount::newOutgoingCall(const std::string& toUrl)
     }
 
     auto toUri = getToUri(to);
-    call->initIceTransport(true);
+    call->initIceMediaTransport(true);
     call->setIPToIP(isIP2IP());
     call->setPeerNumber(toUri);
     call->initRecFilename(to);
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 881354590c6d0a722cb16250590f104c8e01e79d..877e7962cada4c273423c79ac816219d9c64758e 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -40,6 +40,7 @@
 #include "dring/call_const.h"
 #include "dring/media_const.h"
 #include "client/ring_signal.h"
+#include "ice_transport.h"
 
 #ifdef RING_VIDEO
 #include "client/videomanager.h"
@@ -232,7 +233,7 @@ SIPCall::SIPSessionReinvite()
                               getState() == CallState::HOLD))
         return !PJ_SUCCESS;
 
-    if (initIceTransport(true))
+    if (initIceMediaTransport(true))
         setupLocalSDPFromIce();
 
     pjsip_tx_data* tdata;
@@ -684,6 +685,7 @@ SIPCall::removeCall()
 {
     RING_WARN("[call:%s] removeCall()", getCallId().c_str());
     Call::removeCall();
+    mediaTransport_.reset();
     inv.reset();
     setTransport({});
 }
@@ -724,25 +726,25 @@ SIPCall::onPeerRinging()
 void
 SIPCall::setupLocalSDPFromIce()
 {
-    if (not iceTransport_) {
+    if (not mediaTransport_) {
         RING_WARN("[call:%s] null icetransport, no attributes added to SDP", getCallId().c_str());
         return;
     }
 
     // we need an initialized ICE to progress further
-    if (iceTransport_->waitForInitialization(DEFAULT_ICE_INIT_TIMEOUT) <= 0) {
+    if (mediaTransport_->waitForInitialization(DEFAULT_ICE_INIT_TIMEOUT) <= 0) {
         RING_ERR("[call:%s] Medias' ICE init failed", getCallId().c_str());
         return;
     }
 
-    sdp_->addIceAttributes(iceTransport_->getLocalAttributes());
+    sdp_->addIceAttributes(mediaTransport_->getLocalAttributes());
 
     // Add video and audio channels
-    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_AUDIO_RTP_COMPID));
-    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_AUDIO_RTCP_COMPID));
+    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, mediaTransport_->getLocalCandidates(ICE_AUDIO_RTP_COMPID));
+    sdp_->addIceCandidates(SDP_AUDIO_MEDIA_ID, mediaTransport_->getLocalCandidates(ICE_AUDIO_RTCP_COMPID));
 #ifdef RING_VIDEO
-    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_VIDEO_RTP_COMPID));
-    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, iceTransport_->getLocalCandidates(ICE_VIDEO_RTCP_COMPID));
+    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, mediaTransport_->getLocalCandidates(ICE_VIDEO_RTP_COMPID));
+    sdp_->addIceCandidates(SDP_VIDEO_MEDIA_ID, mediaTransport_->getLocalCandidates(ICE_VIDEO_RTCP_COMPID));
 #endif
 }
 
@@ -755,7 +757,7 @@ SIPCall::getAllRemoteCandidates()
                                    std::vector<IceCandidate>& out) {
         IceCandidate cand;
         for (auto& line : sdp_->getIceCandidates(sdpMediaId)) {
-            if (iceTransport_->getCandidateFromSDP(line, cand)) {
+            if (mediaTransport_->getCandidateFromSDP(line, cand)) {
                 RING_DBG("[call:%s] add remote ICE candidate: %s", getCallId().c_str(), line.c_str());
                 out.emplace_back(cand);
             }
@@ -773,9 +775,9 @@ SIPCall::getAllRemoteCandidates()
 bool
 SIPCall::startIce()
 {
-    if (not iceTransport_ or iceTransport_->isFailed() or not iceTransport_->isInitialized())
+    if (not mediaTransport_ or mediaTransport_->isFailed() or not mediaTransport_->isInitialized())
         return false;
-    if (iceTransport_->isStarted()) {
+    if (mediaTransport_->isStarted()) {
         RING_DBG("[call:%s] ICE already started", getCallId().c_str());
         return true;
     }
@@ -784,7 +786,7 @@ SIPCall::startIce()
         RING_ERR("[call:%s] ICE empty attributes", getCallId().c_str());
         return false;
     }
-    return iceTransport_->start(rem_ice_attrs, getAllRemoteCandidates());
+    return mediaTransport_->start(rem_ice_attrs, getAllRemoteCandidates());
 }
 
 bool
@@ -965,23 +967,23 @@ SIPCall::onMediaUpdate()
 void
 SIPCall::waitForIceAndStartMedia()
 {
-    auto ice = iceTransport_;
+    auto ice = mediaTransport_;
     auto iceTimeout = std::chrono::steady_clock::now() + std::chrono::seconds(10);
     auto wthis = std::weak_ptr<SIPCall>(std::static_pointer_cast<SIPCall>(shared_from_this()));
     Manager::instance().addTask([wthis,ice,iceTimeout] {
         if (auto sthis = wthis.lock()) {
             auto& this_ = *sthis;
-            if (ice != this_.iceTransport_) {
+            if (ice != this_.mediaTransport_) {
                 RING_WARN("[call:%s] ICE transport replaced", this_.getCallId().c_str());
                 return false;
             }
             /* First step: wait for an ICE transport for SIP channel */
-            if (this_.iceTransport_->isFailed() or std::chrono::steady_clock::now() >= iceTimeout) {
+            if (this_.mediaTransport_->isFailed() or std::chrono::steady_clock::now() >= iceTimeout) {
                 RING_DBG("[call:%s] ICE init failed (or timeout)", this_.getCallId().c_str());
                 this_.onFailure(ETIMEDOUT);
                 return false;
             }
-            if (not this_.iceTransport_->isRunning())
+            if (not this_.mediaTransport_->isRunning())
                 return true;
             this_.startAllMedia();
         }
@@ -1100,16 +1102,19 @@ SIPCall::InvSessionDeleter::operator ()(pjsip_inv_session* inv) const noexcept
 }
 
 bool
-SIPCall::initIceTransport(bool master, unsigned channel_num)
+SIPCall::initIceMediaTransport(bool master, unsigned channel_num)
 {
-    auto result = Call::initIceTransport(master, channel_num);
-    if (result) {
+    auto& iceTransportFactory = Manager::instance().getIceTransportFactory();
+    mediaTransport_ = iceTransportFactory.createTransport(getCallId().c_str(),
+                                                          channel_num, master,
+                                                          getAccount().getIceOptions());
+    if (mediaTransport_) {
         if (const auto& publicIP = getSIPAccount().getPublishedIpAddress()) {
-            for (unsigned compId = 1; compId <= iceTransport_->getComponentCount(); ++compId)
-                iceTransport_->registerPublicIP(compId, publicIP);
+            for (unsigned compId = 1; compId <= mediaTransport_->getComponentCount(); ++compId)
+                mediaTransport_->registerPublicIP(compId, publicIP);
         }
     }
-    return result;
+    return static_cast<bool>(mediaTransport_);
 }
 
 void
@@ -1130,10 +1135,11 @@ SIPCall::merge(Call& call)
     pj_strcpy(&contactHeader_, &subcall.contactHeader_);
     localAudioPort_ = subcall.localAudioPort_;
     localVideoPort_ = subcall.localVideoPort_;
+    mediaTransport_ = std::move(subcall.mediaTransport_);
 
     Call::merge(subcall);
 
-    if (iceTransport_->isStarted())
+    if (mediaTransport_->isStarted())
         waitForIceAndStartMedia();
 }
 
@@ -1145,12 +1151,24 @@ SIPCall::setRemoteSdp(const pjmedia_sdp_session* sdp)
 
     auto ice_attrs = Sdp::getIceAttributes(sdp);
     if (not ice_attrs.ufrag.empty() and not ice_attrs.pwd.empty()) {
-        if (not getIceTransport()) {
+        if (not mediaTransport_) {
             RING_DBG("Initializing ICE transport");
-            initIceTransport(false);
+            initIceMediaTransport(false);
         }
         setupLocalSDPFromIce();
     }
 }
 
+bool
+SIPCall::isIceRunning() const
+{
+    return mediaTransport_ and mediaTransport_->isRunning();
+}
+
+std::unique_ptr<IceSocket>
+SIPCall::newIceSocket(unsigned compId)
+{
+    return std::unique_ptr<IceSocket> {new IceSocket(mediaTransport_, compId)};
+}
+
 } // namespace ring
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index ab0714bb87ed585d0a37070657a646d720353349..09078911f0030799b2d72f9b869a2ab7b485124d 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -43,6 +43,7 @@
 struct pjsip_evsub;
 struct pjsip_inv_session;
 struct pjmedia_sdp_session;
+struct pj_ice_sess_cand;
 
 namespace ring {
 
@@ -50,6 +51,10 @@ class Sdp;
 class SIPAccountBase;
 class SipTransport;
 class AudioRtpSession;
+class IceTransport;
+class IceSocket;
+
+using IceCandidate = pj_ice_sess_cand;
 
 namespace upnp {
 class Controller;
@@ -99,7 +104,6 @@ public: // overridden
     void restartMediaReceiver() override;
     bool useVideoCodec(const AccountVideoCodecInfo* codec) const override;
     std::map<std::string, std::string> getDetails() const override;
-    bool initIceTransport(bool master, unsigned channel_num=4) override;
 
 public: // SIP related
     /**
@@ -207,6 +211,16 @@ public: // NOT SIP RELATED (good candidates to be moved elsewhere)
         peerRegistredName_ = name;
     }
 
+    bool initIceMediaTransport(bool master, unsigned channel_num=4);
+
+    bool isIceRunning() const;
+
+    std::unique_ptr<IceSocket> newIceSocket(unsigned compId);
+
+    std::shared_ptr<IceTransport> getIceMediaTransport() const {
+        return mediaTransport_;
+    }
+
 private:
     NON_COPYABLE(SIPCall);
 
@@ -265,6 +279,9 @@ private:
 
     /** Local video port, as seen by me. */
     unsigned int localVideoPort_ {0};
+
+    ///< Transport used for media streams
+    std::shared_ptr<IceTransport> mediaTransport_ {};
 };
 
 // Helpers