diff --git a/src/account.cpp b/src/account.cpp
index c05dea5bb9d0a7270bf19d6b86f369f290f2b228..4c6b224909c8d143de60e4b9399f1d7a0350eb79 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -127,7 +127,6 @@ Account::Account(const std::string& accountID)
 #else
     , multiStreamEnabled_(false)
 #endif
-    , iceCompIdRfc5245Compliant_(false)
 {
     // Initialize the codec order, used when creating a new account
     loadDefaultCodecs();
diff --git a/src/account.h b/src/account.h
index 6aa17129d519234b929342807e62246147e8bd44..f4d915a954b83953e6bfe01020accefad2aa41ae 100644
--- a/src/account.h
+++ b/src/account.h
@@ -353,6 +353,10 @@ public:
         allModeratorsEnabled_ = isAllModeratorEnabled;
     }
 
+    // Enable/disable ICE for media
+    bool isIceForMediaEnabled() const { return iceForMediaEnabled_; }
+    void enableIceForMedia(bool enable) { iceForMediaEnabled_ = enable; }
+
     // Enable/disable multi-stream feature.
     // Multi-stream feature changes the callflow of the re-invite process. All
     // clients must support this feature before it can be enabled by default.
@@ -579,6 +583,7 @@ protected:
     bool allModeratorsEnabled_;
 
     bool multiStreamEnabled_ {false};
+    bool iceForMediaEnabled_ {true};
     bool iceCompIdRfc5245Compliant_ {false};
 
     /**
diff --git a/src/call.h b/src/call.h
index 10e3121b0f9aace24ec85275639016a4fd1233c3..8ac8f49ede6fc90951a854c765cd740f684b4fc4 100644
--- a/src/call.h
+++ b/src/call.h
@@ -268,6 +268,11 @@ public:
 
     virtual void sendKeyframe() = 0;
 
+    /**
+     * Check wether ICE is enabled for media
+     */
+    virtual bool isIceEnabled() const = 0;
+
     /**
      * Peer has hung up a call
      */
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index dd03af28db5a604eaddcc433ad68e2d300e4ebe2..d02084c88c9aff11f468ba380b30079dd7b3e6ae 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -811,7 +811,9 @@ JamiAccount::onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call,
 bool
 JamiAccount::SIPStartCall(SIPCall& call, IpAddr target)
 {
-    call.addLocalIceAttributes();
+    if (call.isIceEnabled())
+        call.addLocalIceAttributes();
+
     std::string toUri(getToUri(call.getPeerNumber() + "@"
                                + target.toString(true))); // expecting a fully well formed sip uri
 
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index bb7e93f79db021abb791dbfcea423f7a96afd438..d87c5189d14b533d85a736a03807fb0657bc7c9f 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -231,7 +231,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl,
     }
 
     auto toUri = getToUri(to);
-    call->initIceMediaTransport(true);
+    if (call->isIceEnabled()) {
+        call->initIceMediaTransport(true);
+    }
     call->setPeerNumber(toUri);
     call->setPeerUri(toUri);
 
@@ -321,7 +323,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
     }
 
     auto toUri = getToUri(to);
-    call->initIceMediaTransport(true);
+    if (call->isIceEnabled()) {
+        call->initIceMediaTransport(true);
+    }
     call->setPeerNumber(toUri);
     call->setPeerUri(toUri);
 
@@ -441,7 +445,8 @@ bool
 SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
 {
     // Add Ice headers to local SDP if ice transport exist
-    call->addLocalIceAttributes();
+    if (call->isIceEnabled())
+        call->addLocalIceAttributes();
 
     const std::string& toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
     pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index f24b01b6bdd2f9a1738e73c27e628363d5231c58..bd73ec1290b6c9bfe51aa51252785de2986d9bfc 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -90,6 +90,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
                  const std::map<std::string, std::string>& details)
     : Call(account, callId, type, details)
     , sdp_(new Sdp(callId))
+    , enableIce_(account->isIceForMediaEnabled())
 {
     if (account->getUPnPActive())
         upnp_.reset(new upnp::Controller());
@@ -123,6 +124,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
     : Call(account, callId, type)
     , peerSupportMultiStream_(false)
     , sdp_(new Sdp(callId))
+    , enableIce_(account->isIceForMediaEnabled())
 {
     if (account->getUPnPActive())
         upnp_.reset(new upnp::Controller());
@@ -506,8 +508,10 @@ SIPCall::SIPSessionReinvite(const std::vector<MediaAttribute>& mediaAttrList)
     if (not sdp_->createOffer(mediaAttrList))
         return !PJ_SUCCESS;
 
-    if (initIceMediaTransport(true))
-        addLocalIceAttributes();
+    if (isIceEnabled()) {
+        if (initIceMediaTransport(true))
+            addLocalIceAttributes();
+    }
 
     pjsip_tx_data* tdata;
     auto local_sdp = sdp_->getLocalSdpSession();
@@ -891,7 +895,8 @@ SIPCall::answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaAttrLi
         return;
     }
 
-    setupLocalIce();
+    if (isIceEnabled())
+        setupLocalIce();
 
     if (not sdp_->startNegotiation()) {
         JAMI_ERR("[call:%s] Could not start media negotiation for a re-invite request",
@@ -1459,6 +1464,12 @@ SIPCall::sendKeyframe()
 #endif
 }
 
+bool
+SIPCall::isIceEnabled() const
+{
+    return enableIce_;
+}
+
 void
 SIPCall::setPeerUaVersion(std::string_view ua)
 {
@@ -1558,7 +1569,10 @@ SIPCall::addLocalIceAttributes()
     }
 
     JAMI_DBG("[call:%s] fill SDP with ICE transport %p", getCallId().c_str(), media_tr);
-    sdp_->addIceAttributes(media_tr->getLocalAttributes());
+
+    if (isIceEnabled()) {
+        sdp_->addIceAttributes(media_tr->getLocalAttributes());
+    }
 
     if (account->isIceCompIdRfc5245Compliant()) {
         unsigned streamIdx = 0;
@@ -2164,24 +2178,33 @@ SIPCall::onMediaNegotiationComplete()
             JAMI_WARN("[call:%s] media changed", this_->getCallId().c_str());
             // The call is already ended, so we don't need to restart medias
             if (not this_->inviteSession_
-                or this_->inviteSession_->state == PJSIP_INV_STATE_DISCONNECTED or not this_->sdp_)
+                or this_->inviteSession_->state == PJSIP_INV_STATE_DISCONNECTED
+                or not this_->sdp_) {
                 return;
-            // If ICE is not used, start media now
-            auto rem_ice_attrs = this_->sdp_->getIceAttributes();
-            if (rem_ice_attrs.ufrag.empty() or rem_ice_attrs.pwd.empty()) {
-                JAMI_DBG("[call:%s] No ICE, starting media using default ",
-                         this_->getCallId().c_str());
+            }
 
+            bool hasIce = this_->isIceEnabled();
+            if (hasIce) {
+                // If ICE is not used, start medias now
+                auto rem_ice_attrs = this_->sdp_->getIceAttributes();
+                hasIce = not rem_ice_attrs.ufrag.empty() and not rem_ice_attrs.pwd.empty();
+            }
+            if (hasIce) {
+                if (not this_->isSubcall()) {
+                    // Start ICE checks. Media will be started once ICE checks complete.
+                    this_->startIceMedia();
+                }
+            } else {
+                // No ICE, start media now.
+                JAMI_WARN("[call:%s] ICE media disabled, using default media ports",
+                          this_->getCallId().c_str());
                 // Update the negotiated media.
                 this_->updateNegotiatedMedia();
 
                 // Start the media.
                 this_->stopAllMedia();
                 this_->startAllMedia();
-                return;
             }
-            if (not this_->isSubcall())
-                this_->startIceMedia();
         }
     });
 }
@@ -2326,8 +2349,10 @@ SIPCall::onReceiveOffer(const pjmedia_sdp_session* offer, const pjsip_rx_data* r
     // Use current media list.
     sdp_->processIncomingOffer(getMediaAttributeList());
 
-    if (offer)
-        setupLocalIce();
+    if (isIceEnabled()) {
+        if (offer)
+            setupLocalIce();
+    }
     sdp_->startNegotiation();
 
     pjsip_tx_data* tdata = nullptr;
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index f1f6f24433906a612d29cf39d7398786a460cf8d..960f7c7efaa17365f240cd2aea0142e58037988e 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -147,6 +147,7 @@ public:
     std::shared_ptr<AccountCodecInfo> getAudioCodec() const override;
     std::shared_ptr<AccountCodecInfo> getVideoCodec() const override;
     void sendKeyframe() override;
+    bool isIceEnabled() const override;
     std::map<std::string, std::string> getDetails() const override;
     void enterConference(const std::string& confId) override;
     void exitConference() override;
@@ -426,6 +427,8 @@ private:
     /** Local video port, as seen by me. */
     unsigned int localVideoPort_ {0};
 
+    bool enableIce_ {true};
+
     ///< Transport used for media streams
     std::shared_ptr<IceTransport> mediaTransport_;
 
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 60ccd5a068837b816acf22d314e2585aa4e45080..ab8aaea6d2f9014a7aa5e2a4ac53cbf292d1ecf3 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -453,8 +453,10 @@ transaction_request_cb(pjsip_rx_data* rdata)
         call->getSDP().setReceivedOffer(r_sdp);
         call->getSDP().processIncomingOffer(mediaList);
     }
-    if (r_sdp)
+
+    if (r_sdp and call->isIceEnabled()) {
         call->setupLocalIce();
+    }
 
     pjsip_dialog* dialog = nullptr;
     if (pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, nullptr, &dialog)