diff --git a/src/account.h b/src/account.h
index 623d1e8407983dab782dfed58d64d96d91da5e43..c0b6c28f8310dce17c86e6c16615dcbbb418ebe9 100644
--- a/src/account.h
+++ b/src/account.h
@@ -162,7 +162,7 @@ public:
      * @return The created call
      */
     virtual std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
-                                                  const std::vector<MediaAttribute>& mediaList)
+                                                  const std::vector<DRing::MediaMap>& mediaList)
         = 0;
 
     /**
diff --git a/src/call.h b/src/call.h
index 8ac8f49ede6fc90951a854c765cd740f684b4fc4..a3ed2d47a95f213cfa1628f7b2d601f8cef23c0c 100644
--- a/src/call.h
+++ b/src/call.h
@@ -216,7 +216,7 @@ public:
      * determine the response sent to the peer and the configuration
      * of the local media.
      */
-    virtual void answer(const std::vector<MediaAttribute>& mediaList) = 0;
+    virtual void answer(const std::vector<DRing::MediaMap>& mediaList) = 0;
 
     /**
      * Answer to a media update request. The media attributes set by the
@@ -227,7 +227,7 @@ public:
      * call continue with the current media. It's up to the implementation
      * to determine wether an answer will be sent to the peer.
      */
-    virtual void answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaList) = 0;
+    virtual void answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList) = 0;
     /**
      * Hang up the call
      * @param reason
@@ -343,7 +343,7 @@ public: // media management
      * @param mediaList the new media list
      * @return true on success
      */
-    virtual bool requestMediaChange(const std::vector<MediaAttribute>& mediaList) = 0;
+    virtual bool requestMediaChange(const std::vector<DRing::MediaMap>& mediaList) = 0;
 
     /**
      * Send a message to a call identified by its callid
diff --git a/src/call_factory.cpp b/src/call_factory.cpp
index 6e3ff7ab60cf1394140a004c5087e93ed37489de..a846e42336ef787dc159711fcae4df3a46e6f8fd 100644
--- a/src/call_factory.cpp
+++ b/src/call_factory.cpp
@@ -59,7 +59,7 @@ CallFactory::newSipCall(const std::shared_ptr<SIPAccountBase>& account,
 std::shared_ptr<SIPCall>
 CallFactory::newSipCall(const std::shared_ptr<SIPAccountBase>& account,
                         Call::CallType type,
-                        const std::vector<MediaAttribute>& mediaList)
+                        const std::vector<DRing::MediaMap>& mediaList)
 {
     if (not allowNewCall_) {
         JAMI_WARN("Creation of new calls is not allowed");
diff --git a/src/call_factory.h b/src/call_factory.h
index 8260e8f005c85ac18372ca78dda8f22c91d87cc1..4acfb15c3ed45d43fccf211043d8c20822539121 100644
--- a/src/call_factory.h
+++ b/src/call_factory.h
@@ -64,7 +64,7 @@ public:
      */
     std::shared_ptr<SIPCall> newSipCall(const std::shared_ptr<SIPAccountBase>& account,
                                         Call::CallType type,
-                                        const std::vector<MediaAttribute>& mediaList);
+                                        const std::vector<DRing::MediaMap>& mediaList);
 
     /**
      * Forbid creation of new calls.
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index d02084c88c9aff11f468ba380b30079dd7b3e6ae..345235fe67ba114e724545182e9d9ee42fa0dd9c 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -391,38 +391,7 @@ JamiAccount::flush()
 
 std::shared_ptr<SIPCall>
 JamiAccount::newIncomingCall(const std::string& from,
-                             const std::map<std::string, std::string>& details,
-                             const std::shared_ptr<SipTransport>& sipTr)
-{
-    if (sipTr) {
-        std::unique_lock<std::mutex> lk(sipConnsMtx_);
-        for (auto& [key, value] : sipConns_) {
-            if (key.first == from) {
-                // For each SipConnection of the device
-                for (auto cit = value.rbegin(); cit != value.rend(); ++cit) {
-                    // Search linked Sip Transport
-                    if (cit->transport != sipTr)
-                        continue;
-
-                    auto call = Manager::instance().callFactory.newSipCall(shared(),
-                                                                           Call::CallType::INCOMING);
-                    call->setPeerUri(JAMI_URI_PREFIX + from);
-                    call->setPeerNumber(from);
-                    call->updateDetails(details);
-                    return call;
-                }
-            }
-        }
-        lk.unlock();
-    }
-
-    JAMI_ERR("newIncomingCall: can't find matching call for %s", from.c_str());
-    return nullptr;
-}
-
-std::shared_ptr<SIPCall>
-JamiAccount::newIncomingCall(const std::string& from,
-                             const std::vector<MediaAttribute>& mediaList,
+                             const std::vector<DRing::MediaMap>& mediaList,
                              const std::shared_ptr<SipTransport>& sipTransp)
 {
     JAMI_DBG("New incoming call from %s with %lu media", from.c_str(), mediaList.size());
@@ -468,15 +437,14 @@ JamiAccount::newOutgoingCall(std::string_view toUrl,
 }
 
 std::shared_ptr<Call>
-JamiAccount::newOutgoingCall(std::string_view toUrl,
-                             const std::vector<MediaAttribute>& mediaAttrList)
+JamiAccount::newOutgoingCall(std::string_view toUrl, const std::vector<DRing::MediaMap>& mediaList)
 {
     auto suffix = stripPrefix(toUrl);
     JAMI_DBG() << *this << "Calling peer " << suffix;
 
     auto& manager = Manager::instance();
 
-    auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaAttrList);
+    auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
 
     if (not call)
         return {};
@@ -492,8 +460,6 @@ JamiAccount::newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, std::st
     auto suffix = stripPrefix(toUri);
     JAMI_DBG() << *this << "Calling DHT peer " << suffix;
 
-    call->setSecure(isTlsEnabled());
-
     try {
         const std::string uri {parseJamiUri(suffix)};
         startOutgoingCall(call, uri);
@@ -531,12 +497,12 @@ JamiAccount::newOutgoingCallHelper(const std::shared_ptr<SIPCall>& call, std::st
 std::shared_ptr<SIPCall>
 JamiAccount::createSubCall(const std::shared_ptr<SIPCall>& mainCall)
 {
-    auto mediaAttrList = mainCall->getMediaAttributeList();
+    auto mediaList = MediaAttribute::mediaAttributesToMediaMaps(mainCall->getMediaAttributeList());
 
-    if (not mediaAttrList.empty()) {
+    if (not mediaList.empty()) {
         return Manager::instance().callFactory.newSipCall(shared(),
                                                           Call::CallType::OUTGOING,
-                                                          mediaAttrList);
+                                                          mediaList);
     } else {
         return Manager::instance().callFactory.newSipCall(shared(),
                                                           Call::CallType::OUTGOING,
@@ -607,7 +573,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
     // cached connection is failing with ICE (close event still not detected).
     auto dummyCall = createSubCall(call);
 
-    dummyCall->setSecure(isTlsEnabled());
     call->addSubCall(*dummyCall);
     auto sendRequest =
         [this, wCall, toUri, dummyCall = std::move(dummyCall)](const DeviceId& deviceId,
@@ -628,7 +593,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
 
             auto dev_call = createSubCall(call);
             dev_call->setIPToIP(true);
-            dev_call->setSecure(isTlsEnabled());
             dev_call->setState(Call::ConnectionState::TRYING);
             call->addStateListener(
                 [w = weak(), deviceId](Call::CallState, Call::ConnectionState state, int) {
@@ -674,7 +638,6 @@ JamiAccount::startOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::
 
         auto dev_call = createSubCall(call);
 
-        dev_call->setSecure(isTlsEnabled());
         dev_call->setTransport(transport);
         call->addSubCall(*dev_call);
         // Set the call in PROGRESSING State because the ICE session
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index 8134ad36fdacf7cc4c2105cefd632755539ba899..b1cddc62457771cf98bcb3fc0111cf9fdd800502 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -266,19 +266,7 @@ public:
      * @return A shared pointer on the created call.
      */
     std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
-                                          const std::vector<MediaAttribute>& mediaList) override;
-
-    /**
-     * Create incoming SIPCall.
-     * @param[in] from The origin of the call
-     * @param details Call details
-     * @param sipTr: SIP Transport
-     * @return A shared pointer on the created call.
-     */
-    std::shared_ptr<SIPCall> newIncomingCall(
-        const std::string& from,
-        const std::map<std::string, std::string>& details = {},
-        const std::shared_ptr<SipTransport>& sipTr = nullptr) override;
+                                          const std::vector<DRing::MediaMap>& mediaList) override;
 
     /**
      * Create incoming SIPCall.
@@ -289,7 +277,7 @@ public:
      */
     std::shared_ptr<SIPCall> newIncomingCall(
         const std::string& from,
-        const std::vector<MediaAttribute>& mediaList,
+        const std::vector<DRing::MediaMap>& mediaList,
         const std::shared_ptr<SipTransport>& sipTr = {}) override;
 
     void onTextMessage(const std::string& id,
@@ -533,7 +521,8 @@ public:
     std::vector<uint8_t> conversationVCard(const std::string& conversationId) const;
 
     // Member management
-    void saveMembers(const std::string& convId, const std::vector<std::string>& members); // Save confInfos
+    void saveMembers(const std::string& convId,
+                     const std::vector<std::string>& members); // Save confInfos
     void addConversationMember(const std::string& conversationId,
                                const std::string& contactUri,
                                bool sendRequest = true);
diff --git a/src/manager.cpp b/src/manager.cpp
index 3b6670d80228ab807bfb27b5c711ae596798cf3c..2133335f844c09b42d3b7556ca3271af1a61c4f7 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -1078,9 +1078,7 @@ Manager::requestMediaChange(const std::string& callID, const std::vector<DRing::
         return false;
     }
 
-    auto const& mediaAttrList = MediaAttribute::parseMediaList(mediaList);
-
-    return call->requestMediaChange(mediaAttrList);
+    return call->requestMediaChange(mediaList);
 }
 
 // THREAD=Main : for outgoing Call
@@ -1159,8 +1157,7 @@ Manager::answerCallWithMedia(const std::string& callId,
     stopTone();
 
     try {
-        auto mediaAttrList = MediaAttribute::parseMediaList(mediaList);
-        call->answer(mediaAttrList);
+        call->answer(mediaList);
     } catch (const std::runtime_error& e) {
         JAMI_ERR("%s", e.what());
         result = false;
@@ -1205,8 +1202,7 @@ Manager::answerMediaChangeRequest(const std::string& callId,
     }
 
     try {
-        auto mediaAttrList = MediaAttribute::parseMediaList(mediaList);
-        call->answerMediaChangeRequest(mediaAttrList);
+        call->answerMediaChangeRequest(mediaList);
     } catch (const std::runtime_error& e) {
         JAMI_ERR("%s", e.what());
         result = false;
@@ -3379,7 +3375,7 @@ Manager::newOutgoingCall(std::string_view toUrl,
         return {};
     }
 
-    return account->newOutgoingCall(toUrl, MediaAttribute::parseMediaList(mediaList));
+    return account->newOutgoingCall(toUrl, mediaList);
 }
 
 #ifdef ENABLE_VIDEO
diff --git a/src/media/media_attribute.cpp b/src/media/media_attribute.cpp
index 8f4ce363c7eadf8e4ab7a73f3177007b141d44b5..d27557595c3e012aefeeac4fe69bd7152dd77ecb 100644
--- a/src/media/media_attribute.cpp
+++ b/src/media/media_attribute.cpp
@@ -23,7 +23,7 @@
 
 namespace jami {
 
-MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap)
+MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap, bool secure)
 {
     std::pair<bool, MediaType> pairType = getMediaType(mediaMap);
     if (pairType.first)
@@ -51,16 +51,18 @@ MediaAttribute::MediaAttribute(const DRing::MediaMap& mediaMap)
     pairBool = getBoolValue(mediaMap, DRing::Media::MediaAttributeKey::ON_HOLD);
     if (pairBool.first)
         onHold_ = pairBool.second;
+
+    secure_ = secure;
 }
 
 std::vector<MediaAttribute>
-MediaAttribute::parseMediaList(const std::vector<DRing::MediaMap>& mediaList)
+MediaAttribute::buildMediaAtrributesList(const std::vector<DRing::MediaMap>& mediaList, bool secure)
 {
     std::vector<MediaAttribute> mediaAttrList;
     mediaAttrList.reserve(mediaList.size());
 
     for (auto const& mediaMap : mediaList) {
-        mediaAttrList.emplace_back(MediaAttribute(mediaMap));
+        mediaAttrList.emplace_back(MediaAttribute(mediaMap, secure));
     }
 
     return mediaAttrList;
diff --git a/src/media/media_attribute.h b/src/media/media_attribute.h
index c21cc196cf38a0c8b2ddda5dd0abd0290216a113..bf63141d9189c0eb61f3a76a9ae68b8ec2ef07b4 100644
--- a/src/media/media_attribute.h
+++ b/src/media/media_attribute.h
@@ -47,9 +47,10 @@ public:
         , onHold_(onHold)
     {}
 
-    MediaAttribute(const DRing::MediaMap& mediaMap);
+    MediaAttribute(const DRing::MediaMap& mediaMap, bool secure);
 
-    static std::vector<MediaAttribute> parseMediaList(const std::vector<DRing::MediaMap>& mediaList);
+    static std::vector<MediaAttribute> buildMediaAtrributesList(
+        const std::vector<DRing::MediaMap>& mediaList, bool secure);
 
     static MediaType stringToMediaType(const std::string& mediaType);
 
diff --git a/src/sip/sdp.cpp b/src/sip/sdp.cpp
index e060524148b596bcf9255bd6b6ea839319a1724b..279d669f3b9b1f4d8274484e902d1a6cf74629bc 100644
--- a/src/sip/sdp.cpp
+++ b/src/sip/sdp.cpp
@@ -989,6 +989,7 @@ Sdp::clearIce(pjmedia_sdp_session* session)
         return;
     pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-ufrag");
     pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "ice-pwd");
+    // TODO. Why this? we should not have "candidate" attribute at session level.
     pjmedia_sdp_attr_remove_all(&session->attr_count, session->attr, "candidate");
     for (unsigned i = 0; i < session->media_count; i++) {
         auto media = session->media[i];
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index a76b1746f830701e3a2f7f5954844f98941d0639..5f4a6eaf2418eb778b8bae87f5dea91e7754ed57 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -173,21 +173,10 @@ SIPAccount::~SIPAccount() noexcept
 
 std::shared_ptr<SIPCall>
 SIPAccount::newIncomingCall(const std::string& from UNUSED,
-                            const std::map<std::string, std::string>& details,
+                            const std::vector<DRing::MediaMap>& mediaList,
                             const std::shared_ptr<SipTransport>&)
 {
-    auto& manager = Manager::instance();
-    return manager.callFactory.newSipCall(shared(), Call::CallType::INCOMING, details);
-}
-
-std::shared_ptr<SIPCall>
-SIPAccount::newIncomingCall(const std::string& from UNUSED,
-                            const std::vector<MediaAttribute>& mediaAttrList,
-                            const std::shared_ptr<SipTransport>&)
-{
-    return Manager::instance().callFactory.newSipCall(shared(),
-                                                      Call::CallType::INCOMING,
-                                                      mediaAttrList);
+    return Manager::instance().callFactory.newSipCall(shared(), Call::CallType::INCOMING, mediaList);
 }
 
 template<>
@@ -204,8 +193,6 @@ SIPAccount::newOutgoingCall(std::string_view toUrl,
     auto call = manager.callFactory.newSipCall(shared(),
                                                Call::CallType::OUTGOING,
                                                volatileCallDetails);
-    call->setSecure(isTlsEnabled());
-
     if (isIP2IP()) {
         bool ipv6 = IpAddr::isIpv6(toUrl);
         to = ipv6 ? IpAddr(toUrl).toString(false, true) : toUrl;
@@ -282,7 +269,7 @@ SIPAccount::newOutgoingCall(std::string_view toUrl,
 }
 
 std::shared_ptr<Call>
-SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttribute>& mediaAttrList)
+SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<DRing::MediaMap>& mediaList)
 {
     std::string to;
     int family;
@@ -291,12 +278,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
 
     auto& manager = Manager::instance();
 
-    auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaAttrList);
+    auto call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
     if (not call)
         throw std::runtime_error("Failed to create the call");
-    ;
-
-    call->setSecure(isTlsEnabled());
 
     if (isIP2IP()) {
         bool ipv6 = IpAddr::isIpv6(toUrl);
@@ -326,7 +310,7 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
 
     // Do not init ICE yet if the the media list is empty. This may occur
     // if we are sending an invite with no SDP offer.
-    if (call->isIceEnabled() and not mediaAttrList.empty()) {
+    if (call->isIceEnabled() and not mediaList.empty()) {
         call->initIceMediaTransport(true);
     }
 
@@ -356,7 +340,9 @@ SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<MediaAttri
     else
         sdp.setPublishedIP(getPublishedAddress());
 
-    const bool created = sdp.createOffer(mediaAttrList);
+    // TODO. We should not dot his here. Move it to SIPCall.
+    const bool created = sdp.createOffer(
+        MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled()));
 
     if (created) {
         std::weak_ptr<SIPCall> weak_call = call;
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index 4aac96371d1e9a2e068d83f9769b5266407d4573..2e505912bcc282118001f6590f2414193f1c3d91 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -437,19 +437,7 @@ public:
      * @return a shared pointer on the created call.
      */
     std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
-                                          const std::vector<MediaAttribute>& mediaList) override;
-
-    /**
-     * Create incoming SIPCall.
-     * @param[in] from The origin uri of the call
-     * @param details use to set some specific details
-     * @return std::shared_ptr<T> A shared pointer on the created call.
-     *      The type of this instance is given in template argument.
-     *      This type can be any base class of SIPCall class (included).
-     */
-    std::shared_ptr<SIPCall> newIncomingCall(const std::string& from,
-                                             const std::map<std::string, std::string>& details = {},
-                                             const std::shared_ptr<SipTransport>& = nullptr) override;
+                                          const std::vector<DRing::MediaMap>& mediaList) override;
 
     /**
      * Create incoming SIPCall.
@@ -460,7 +448,7 @@ public:
      */
     std::shared_ptr<SIPCall> newIncomingCall(
         const std::string& from,
-        const std::vector<MediaAttribute>& mediaList,
+        const std::vector<DRing::MediaMap>& mediaList,
         const std::shared_ptr<SipTransport>& sipTr = {}) override;
 
     void onRegister(pjsip_regc_cbparam* param);
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index c96e92997be3dd426bb327a995ecf70baa3595e2..38f11bc5cff93a07296dedf4a2028e4276dd2f30 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -133,20 +133,6 @@ public:
 
     virtual ~SIPAccountBase() noexcept;
 
-    /**
-     * Create incoming SIPCall.
-     * @param[in] id The ID of the call
-     * @param details use to set some specific details
-     * @return std::shared_ptr<T> A shared pointer on the created call.
-     *      The type of this instance is given in template argument.
-     *      This type can be any base class of SIPCall class (included).
-     */
-    virtual std::shared_ptr<SIPCall> newIncomingCall(
-        const std::string& from,
-        const std::map<std::string, std::string>& details = {},
-        const std::shared_ptr<SipTransport>& = nullptr)
-        = 0;
-
     /**
      * Create incoming SIPCall.
      * @param[in] from The origin of the call
@@ -155,7 +141,7 @@ public:
      * @return A shared pointer on the created call.
      */
     virtual std::shared_ptr<SIPCall> newIncomingCall(const std::string& from,
-                                                     const std::vector<MediaAttribute>& mediaList,
+                                                     const std::vector<DRing::MediaMap>& mediaList,
                                                      const std::shared_ptr<SipTransport>& sipTr = {})
         = 0;
 
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 13c75c855987ec7534775e72d6e3762cd8f20bd7..c2ab034280dee1e427909250eef4021c6c5a57b5 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -91,6 +91,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
     : Call(account, callId, type, details)
     , sdp_(new Sdp(callId))
     , enableIce_(account->isIceForMediaEnabled())
+    , srtpEnabled_(account->isSrtpEnabled())
 {
     if (account->getUPnPActive())
         upnp_.reset(new upnp::Controller());
@@ -120,11 +121,12 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
 SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
                  const std::string& callId,
                  Call::CallType type,
-                 const std::vector<MediaAttribute>& mediaAttrList)
+                 const std::vector<DRing::MediaMap>& mediaList)
     : Call(account, callId, type)
     , peerSupportMultiStream_(false)
     , sdp_(new Sdp(callId))
     , enableIce_(account->isIceForMediaEnabled())
+    , srtpEnabled_(account->isSrtpEnabled())
 {
     if (account->getUPnPActive())
         upnp_.reset(new upnp::Controller());
@@ -139,18 +141,20 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
                                     account->getActiveAccountCodecInfoList(MEDIA_VIDEO));
 #endif
 
-    std::vector<MediaAttribute> mediaList;
-    if (mediaAttrList.size() > 0) {
-        mediaList = mediaAttrList;
-    } else if (type_ == Call::CallType::INCOMING) {
-        // Handle incoming call without media offer.
-        JAMI_WARN("[call:%s] No media offered in the incoming invite. An offer will be provided in "
-                  "the answer",
-                  getCallId().c_str());
-        mediaList = getSIPAccount()->createDefaultMediaList(getSIPAccount()->isVideoEnabled(),
-                                                            getState() == CallState::HOLD);
-    } else {
-        JAMI_WARN("[call:%s] Creating an outgoing call with empty offer", getCallId().c_str());
+    auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
+
+    if (mediaAttrList.size() == 0) {
+        if (type_ == Call::CallType::INCOMING) {
+            // Handle incoming call without media offer.
+            JAMI_WARN(
+                "[call:%s] No media offered in the incoming invite. An offer will be provided in "
+                "the answer",
+                getCallId().c_str());
+            mediaAttrList = getSIPAccount()->createDefaultMediaList(getSIPAccount()->isVideoEnabled(),
+                                                                    getState() == CallState::HOLD);
+        } else {
+            JAMI_WARN("[call:%s] Creating an outgoing call with empty offer", getCallId().c_str());
+        }
     }
 
     JAMI_DBG("[call:%s] Create a new [%s] SIP call with %lu media",
@@ -160,7 +164,7 @@ SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
                  : (type == Call::CallType::OUTGOING ? "OUTGOING" : "MISSED"),
              mediaList.size());
 
-    initMediaStreams(mediaList);
+    initMediaStreams(mediaAttrList);
 }
 
 SIPCall::~SIPCall()
@@ -422,37 +426,39 @@ SIPCall::setContactHeader(pj_str_t* contact)
 void
 SIPCall::setTransport(const std::shared_ptr<SipTransport>& t)
 {
-    if (isSecure() and t and not t->isSecure()) {
-        JAMI_ERR("Can't set un-secure transport to secure call.");
+    transport_ = t;
+    if (not transport_) {
+        JAMI_WARN("[call:%s] Transport was set to null", getCallId().c_str());
         return;
     }
 
-    const auto list_id = reinterpret_cast<uintptr_t>(this);
-    if (transport_)
-        transport_->removeStateListener(list_id);
-    transport_ = t;
+    if (isSrtpEnabled() and not transport_->isSecure()) {
+        JAMI_WARN("[call:%s] Crypto (SRTP) is negotiated over an un-encrypted signaling channel",
+                  getCallId().c_str());
+    }
 
-    if (transport_) {
-        setSecure(transport_->isSecure());
-
-        // listen for transport destruction
-        transport_->addStateListener(
-            list_id,
-            [wthis_ = weak()](pjsip_transport_state state, const pjsip_transport_state_info*) {
-                if (auto this_ = wthis_.lock()) {
-                    // end the call if the SIP transport is shut down
-                    auto isAlive = SipTransport::isAlive(state);
-                    if (not isAlive
-                        and this_->getConnectionState() != ConnectionState::DISCONNECTED) {
-                        JAMI_WARN(
-                            "[call:%s] Ending call because underlying SIP transport was closed",
-                            this_->getCallId().c_str());
-                        this_->stopAllMedia();
-                        this_->onFailure(ECONNRESET);
-                    }
-                }
-            });
+    if (not isSrtpEnabled() and transport_->isSecure()) {
+        JAMI_WARN("[call:%s] The signaling channel is encrypted but the media is not encrypted",
+                  getCallId().c_str());
     }
+
+    const auto list_id = reinterpret_cast<uintptr_t>(this);
+    transport_->removeStateListener(list_id);
+
+    // listen for transport destruction
+    transport_->addStateListener(
+        list_id, [wthis_ = weak()](pjsip_transport_state state, const pjsip_transport_state_info*) {
+            if (auto this_ = wthis_.lock()) {
+                // end the call if the SIP transport is shut down
+                auto isAlive = SipTransport::isAlive(state);
+                if (not isAlive and this_->getConnectionState() != ConnectionState::DISCONNECTED) {
+                    JAMI_WARN("[call:%s] Ending call because underlying SIP transport was closed",
+                              this_->getCallId().c_str());
+                    this_->stopAllMedia();
+                    this_->onFailure(ECONNRESET);
+                }
+            }
+        });
 }
 
 void
@@ -760,7 +766,7 @@ SIPCall::answer()
 }
 
 void
-SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
+SIPCall::answer(const std::vector<DRing::MediaMap>& mediaList)
 {
     std::lock_guard<std::recursive_mutex> lk {callMutex_};
     auto account = getSIPAccount();
@@ -769,6 +775,8 @@ SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
         return;
     }
 
+    auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
+
     if (mediaAttrList.empty()) {
         JAMI_DBG("[call:%s] Media list must not be empty!", getCallId().c_str());
         return;
@@ -894,7 +902,7 @@ SIPCall::answer(const std::vector<MediaAttribute>& mediaAttrList)
 }
 
 void
-SIPCall::answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaAttrList)
+SIPCall::answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList)
 {
     std::lock_guard<std::recursive_mutex> lk {callMutex_};
 
@@ -904,6 +912,8 @@ SIPCall::answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaAttrLi
         return;
     }
 
+    auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
+
     if (mediaAttrList.empty()) {
         JAMI_DBG("[call:%s] Media list size is empty. Ignoring the media change request",
                  getCallId().c_str());
@@ -1879,7 +1889,7 @@ SIPCall::updateNegotiatedMedia()
             continue;
         }
 
-        if (isSecure() and local.enabled and not local.crypto) {
+        if (isSrtpEnabled() and local.enabled and not local.crypto) {
             JAMI_WARN("[call:%s] [SDP:slot#%u] Secure mode but no local crypto attributes. "
                       "Ignoring the media",
                       getCallId().c_str(),
@@ -1887,7 +1897,7 @@ SIPCall::updateNegotiatedMedia()
             continue;
         }
 
-        if (isSecure() and remote.enabled and not remote.crypto) {
+        if (isSrtpEnabled() and remote.enabled and not remote.crypto) {
             JAMI_WARN("[call:%s] [SDP:slot#%u] Secure mode but no crypto remote attributes. "
                       "Ignoring the media",
                       getCallId().c_str(),
@@ -1924,11 +1934,9 @@ SIPCall::startAllMedia()
         return;
     }
 
-    if (isSecure() && not transport_->isSecure()) {
-        JAMI_ERR("[call:%s] Can't perform secure call over insecure SIP transport",
-                 getCallId().c_str());
-        onFailure(EPROTONOSUPPORT);
-        return;
+    if (isSrtpEnabled() && not transport_->isSecure()) {
+        JAMI_WARN("[call:%s] Crypto (SRTP) is negotiated over an insecure signaling transport",
+                  getCallId().c_str());
     }
 
     // reset
@@ -2078,7 +2086,7 @@ SIPCall::muteMedia(const std::string& mediaType, bool mute)
     }
 
     // Apply
-    requestMediaChange(mediaList);
+    requestMediaChange(MediaAttribute::mediaAttributesToMediaMaps(mediaList));
 }
 
 void
@@ -2187,8 +2195,10 @@ SIPCall::isReinviteRequired(const std::vector<MediaAttribute>& mediaAttrList)
 }
 
 bool
-SIPCall::requestMediaChange(const std::vector<MediaAttribute>& mediaAttrList)
+SIPCall::requestMediaChange(const std::vector<DRing::MediaMap>& mediaList)
 {
+    auto mediaAttrList = MediaAttribute::buildMediaAtrributesList(mediaList, isSrtpEnabled());
+
     // If the peer does not support multi-stream and the size of the new
     // media list is different from the current media list, the media
     // change request will be ignored.
@@ -2396,7 +2406,7 @@ SIPCall::onReceiveReinvite(const pjmedia_sdp_session* offer, pjsip_rx_data* rdat
     if (acc->isMultiStreamEnabled() and remoteMediaList.size() != rtpStreams_.size()) {
         Manager::instance().mediaChangeRequested(getCallId(), getAccountId(), remoteMediaList);
     } else {
-        auto localMediaList = getMediaAttributeList();
+        auto localMediaList = MediaAttribute::mediaAttributesToMediaMaps(getMediaAttributeList());
         answerMediaChangeRequest(localMediaList);
     }
 
@@ -2798,17 +2808,6 @@ SIPCall::deinitRecorder()
     }
 }
 
-void
-SIPCall::setSecure(bool sec)
-{
-    if (srtpEnabled_)
-        return;
-    if (sec && getConnectionState() != ConnectionState::DISCONNECTED) {
-        throw std::runtime_error("Can't enable security since call is already connected");
-    }
-    srtpEnabled_ = sec;
-}
-
 void
 SIPCall::InvSessionDeleter::operator()(pjsip_inv_session* inv) const noexcept
 {
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index be2b71d7fe1f37b16ed5902761c874564f22f01b..fd16200ff403aab596c366347754060fd77e4346 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -115,7 +115,7 @@ public:
     SIPCall(const std::shared_ptr<SIPAccountBase>& account,
             const std::string& id,
             Call::CallType type,
-            const std::vector<MediaAttribute>& mediaList);
+            const std::vector<DRing::MediaMap>& mediaList);
 
     // Inherited from Call class
     LinkType getLinkType() const override { return LINK_TYPE; }
@@ -126,8 +126,8 @@ private:
 
 public:
     void answer() override;
-    void answer(const std::vector<MediaAttribute>& mediaList) override;
-    void answerMediaChangeRequest(const std::vector<MediaAttribute>& mediaList) override;
+    void answer(const std::vector<DRing::MediaMap>& mediaList) override;
+    void answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList) override;
     void hangup(int reason) override;
     void refuse() override;
     void transfer(const std::string& to) override;
@@ -137,7 +137,7 @@ public:
     void switchInput(const std::string& resource = {}) override;
     void peerHungup() override;
     void carryingDTMFdigits(char code) override;
-    bool requestMediaChange(const std::vector<MediaAttribute>& mediaList) override;
+    bool requestMediaChange(const std::vector<DRing::MediaMap>& mediaList) override;
     void sendTextMessage(const std::map<std::string, std::string>& messages,
                          const std::string& from) override;
     void removeCall() override;
@@ -257,9 +257,7 @@ public:
     std::shared_ptr<video::VideoRtpSession> addDummyVideoRtpSession();
 #endif
 
-    void setSecure(bool sec);
-
-    bool isSecure() const { return srtpEnabled_; }
+    bool isSrtpEnabled() const { return srtpEnabled_; }
 
     void generateMediaPorts();
 
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 55885536cbe82c1e93bf9cace125baa37af635f4..cd84971513015f0e29339850dd9619f093685986 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -410,7 +410,9 @@ transaction_request_cb(pjsip_rx_data* rdata)
         }
     }
 
-    auto call = account->newIncomingCall(std::string(remote_user), localMediaList, transport);
+    auto call = account->newIncomingCall(std::string(remote_user),
+                                         MediaAttribute::mediaAttributesToMediaMaps(localMediaList),
+                                         transport);
 
     if (!call) {
         return PJ_FALSE;
diff --git a/test/unitTest/Makefile.am b/test/unitTest/Makefile.am
index 9ec11786e7688787c0970a1b48f0cf43a68787aa..95940fdc040aa06a72781ae8e75f9566b83e95c8 100644
--- a/test/unitTest/Makefile.am
+++ b/test/unitTest/Makefile.am
@@ -178,5 +178,7 @@ check_PROGRAMS += ut_sip_basic_calls
 ut_sip_basic_calls_SOURCES = sip_account/sip_basic_calls.cpp
 check_PROGRAMS += ut_sip_empty_offer
 ut_sip_empty_offer_SOURCES = sip_account/sip_empty_offer.cpp
+check_PROGRAMS += ut_sip_srtp
+ut_sip_srtp_SOURCES = sip_account/sip_srtp.cpp
 
 TESTS = $(check_PROGRAMS)
diff --git a/test/unitTest/media_negotiation/media_negotiation.cpp b/test/unitTest/media_negotiation/media_negotiation.cpp
index 48c6a6bdd2a09c681826436e7ff44e671742c835..32a40984a09e285cd8119ef2c5870656e60240e5 100644
--- a/test/unitTest/media_negotiation/media_negotiation.cpp
+++ b/test/unitTest/media_negotiation/media_negotiation.cpp
@@ -574,7 +574,8 @@ MediaNegotiationTest::testWithScenario(CallData& aliceData,
 
     auto const& aliceCall = std::dynamic_pointer_cast<SIPCall>(
         (Manager::instance().getAccount<JamiAccount>(aliceData.accountId_))
-            ->newOutgoingCall(bobData.userName_, scenario.offer_));
+            ->newOutgoingCall(bobData.userName_,
+                              MediaAttribute::mediaAttributesToMediaMaps(scenario.offer_)));
     CPPUNIT_ASSERT(aliceCall);
     aliceData.callId_ = aliceCall->getCallId();
 
diff --git a/test/unitTest/sip_account/sip_basic_calls.cpp b/test/unitTest/sip_account/sip_basic_calls.cpp
index 59fc15892f393b2163b736960587df54f9fec76f..7637ec3d527cfa15f440b9372a1de45625b554d6 100644
--- a/test/unitTest/sip_account/sip_basic_calls.cpp
+++ b/test/unitTest/sip_account/sip_basic_calls.cpp
@@ -529,24 +529,20 @@ SipBasicCallTest::audio_only_test()
     // Configure Alice
     audio.enabled_ = true;
     audio.label_ = "audio_0";
-    audio.secure_ = aliceAcc->isSrtpEnabled();
     offer.emplace_back(audio);
 
     video.enabled_ = true;
     video.label_ = "video_0";
-    video.secure_ = aliceAcc->isSrtpEnabled();
     aliceAcc->enableVideo(true);
     offer.emplace_back(video);
 
     // Configure Bob
     audio.enabled_ = true;
     audio.label_ = "audio_0";
-    audio.secure_ = bobAcc->isSrtpEnabled();
     answer.emplace_back(audio);
 
     video.enabled_ = false;
     video.label_ = "video_0";
-    video.secure_ = bobAcc->isSrtpEnabled();
     bobAcc->enableVideo(false);
     answer.emplace_back(video);
 
@@ -569,24 +565,20 @@ SipBasicCallTest::audio_video_test()
     // Configure Alice
     audio.enabled_ = true;
     audio.label_ = "audio_0";
-    audio.secure_ = aliceAcc->isSrtpEnabled();
     offer.emplace_back(audio);
 
     video.enabled_ = true;
     video.label_ = "video_0";
-    video.secure_ = aliceAcc->isSrtpEnabled();
     aliceAcc->enableVideo(true);
     offer.emplace_back(video);
 
     // Configure Bob
     audio.enabled_ = true;
     audio.label_ = "audio_0";
-    audio.secure_ = bobAcc->isSrtpEnabled();
     answer.emplace_back(audio);
 
     video.enabled_ = true;
     video.label_ = "video_0";
-    video.secure_ = bobAcc->isSrtpEnabled();
     bobAcc->enableVideo(true);
     answer.emplace_back(video);
 
diff --git a/test/unitTest/sip_account/sip_srtp.cpp b/test/unitTest/sip_account/sip_srtp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..835685e32877bd46a4b7b99cc3ba9b6838aa061d
--- /dev/null
+++ b/test/unitTest/sip_account/sip_srtp.cpp
@@ -0,0 +1,553 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <condition_variable>
+#include <string>
+
+#include "callmanager_interface.h"
+#include "manager.h"
+#include "sip/sipaccount.h"
+#include "../../test_runner.h"
+#include "dring.h"
+#include "dring/media_const.h"
+#include "call_const.h"
+#include "account_const.h"
+#include "sip/sipcall.h"
+#include "sip/sdp.h"
+using namespace DRing::Account;
+using namespace DRing::Call;
+
+namespace jami {
+namespace test {
+
+struct CallData
+{
+    struct Signal
+    {
+        Signal(const std::string& name, const std::string& event = {})
+            : name_(std::move(name))
+            , event_(std::move(event)) {};
+
+        std::string name_ {};
+        std::string event_ {};
+    };
+
+    std::string accountId_ {};
+    uint16_t listeningPort_ {0};
+    std::string userName_ {};
+    std::string alias_ {};
+    std::string callId_ {};
+    std::vector<Signal> signals_;
+    std::condition_variable cv_ {};
+    std::mutex mtx_;
+};
+
+/**
+ * Call tests for SIP accounts.
+ */
+class SipSrtpTest : public CppUnit::TestFixture
+{
+public:
+    SipSrtpTest()
+    {
+        // Init daemon
+        DRing::init(DRing::InitFlag(DRing::DRING_FLAG_DEBUG | DRing::DRING_FLAG_CONSOLE_LOG));
+        if (not Manager::instance().initialized)
+            CPPUNIT_ASSERT(DRing::start("dring-sample.yml"));
+    }
+    ~SipSrtpTest() { DRing::fini(); }
+
+    static std::string name() { return "SipSrtpTest"; }
+    void setUp();
+    void tearDown();
+
+private:
+    // Test cases.
+    void audio_video_srtp_enabled_test();
+
+    CPPUNIT_TEST_SUITE(SipSrtpTest);
+    CPPUNIT_TEST(audio_video_srtp_enabled_test);
+
+    CPPUNIT_TEST_SUITE_END();
+
+    // Event/Signal handlers
+    static void onCallStateChange(const std::string& callId,
+                                  const std::string& state,
+                                  CallData& callData);
+    static void onIncomingCallWithMedia(const std::string& accountId,
+                                        const std::string& callId,
+                                        const std::vector<DRing::MediaMap> mediaList,
+                                        CallData& callData);
+    static void onMediaNegotiationStatus(const std::string& callId,
+                                         const std::string& event,
+                                         CallData& callData);
+
+    // Helpers
+    void audio_video_call(std::vector<MediaAttribute> offer,
+                          std::vector<MediaAttribute> answer,
+                          bool validateMedia = true);
+    static void configureTest(CallData& bob, CallData& alice);
+    static std::string getUserAlias(const std::string& callId);
+    // Wait for a signal from the callbacks. Some signals also report the event that
+    // triggered the signal a like the StateChange signal.
+    static bool waitForSignal(CallData& callData,
+                              const std::string& signal,
+                              const std::string& expectedEvent = {});
+
+private:
+    CallData aliceData_;
+    CallData bobData_;
+};
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(SipSrtpTest, SipSrtpTest::name());
+
+void
+SipSrtpTest::setUp()
+{
+    aliceData_.listeningPort_ = 5080;
+    std::map<std::string, std::string> details = DRing::getAccountTemplate("SIP");
+    details[ConfProperties::TYPE] = "SIP";
+    details[ConfProperties::DISPLAYNAME] = "ALICE";
+    details[ConfProperties::ALIAS] = "ALICE";
+    details[ConfProperties::LOCAL_PORT] = std::to_string(aliceData_.listeningPort_);
+    details[ConfProperties::UPNP_ENABLED] = "false";
+    details[ConfProperties::SRTP::KEY_EXCHANGE] = "sdes";
+    aliceData_.accountId_ = Manager::instance().addAccount(details);
+
+    bobData_.listeningPort_ = 5082;
+    details = DRing::getAccountTemplate("SIP");
+    details[ConfProperties::TYPE] = "SIP";
+    details[ConfProperties::DISPLAYNAME] = "BOB";
+    details[ConfProperties::ALIAS] = "BOB";
+    details[ConfProperties::LOCAL_PORT] = std::to_string(bobData_.listeningPort_);
+    details[ConfProperties::UPNP_ENABLED] = "false";
+    details[ConfProperties::SRTP::KEY_EXCHANGE] = "sdes";
+    bobData_.accountId_ = Manager::instance().addAccount(details);
+
+    JAMI_INFO("Initialize accounts ...");
+    auto aliceAccount = Manager::instance().getAccount<SIPAccount>(aliceData_.accountId_);
+    aliceAccount->enableMultiStream(true);
+    auto bobAccount = Manager::instance().getAccount<SIPAccount>(bobData_.accountId_);
+    bobAccount->enableMultiStream(true);
+}
+
+void
+SipSrtpTest::tearDown()
+{
+    JAMI_INFO("Remove created accounts...");
+
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    auto currentAccSize = Manager::instance().getAccountList().size();
+    std::atomic_bool accountsRemoved {false};
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::AccountsChanged>([&]() {
+            if (Manager::instance().getAccountList().size() <= currentAccSize - 2) {
+                accountsRemoved = true;
+                cv.notify_one();
+            }
+        }));
+    DRing::registerSignalHandlers(confHandlers);
+
+    Manager::instance().removeAccount(aliceData_.accountId_, true);
+    Manager::instance().removeAccount(bobData_.accountId_, true);
+    // Because cppunit is not linked with dbus, just poll if removed
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(30), [&] { return accountsRemoved.load(); }));
+
+    DRing::unregisterSignalHandlers();
+}
+
+std::string
+SipSrtpTest::getUserAlias(const std::string& callId)
+{
+    auto call = Manager::instance().getCallFromCallID(callId);
+
+    if (not call) {
+        JAMI_WARN("Call with ID [%s] does not exist anymore!", callId.c_str());
+        return {};
+    }
+
+    auto const& account = call->getAccount().lock();
+    if (not account) {
+        return {};
+    }
+
+    return account->getAccountDetails()[ConfProperties::ALIAS];
+}
+
+void
+SipSrtpTest::onIncomingCallWithMedia(const std::string& accountId,
+                                     const std::string& callId,
+                                     const std::vector<DRing::MediaMap> mediaList,
+                                     CallData& callData)
+{
+    CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
+
+    JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
+              DRing::CallSignal::IncomingCallWithMedia::name,
+              callData.alias_.c_str(),
+              callId.c_str(),
+              mediaList.size());
+
+    // NOTE.
+    // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
+    // the client, and the client have no access to this type. But here, we only
+    // needed to check if the call exists. This is the most straightforward and
+    // reliable way to do it until we add a new API (like hasCall(id)).
+    if (not Manager::instance().getCallFromCallID(callId)) {
+        JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
+        callData.callId_ = {};
+        return;
+    }
+
+    std::unique_lock<std::mutex> lock {callData.mtx_};
+    callData.callId_ = callId;
+    callData.signals_.emplace_back(CallData::Signal(DRing::CallSignal::IncomingCallWithMedia::name));
+
+    callData.cv_.notify_one();
+}
+
+void
+SipSrtpTest::onCallStateChange(const std::string& callId,
+                               const std::string& state,
+                               CallData& callData)
+{
+    auto call = Manager::instance().getCallFromCallID(callId);
+    if (not call) {
+        JAMI_WARN("Call with ID [%s] does not exist anymore!", callId.c_str());
+        return;
+    }
+
+    auto account = call->getAccount().lock();
+    if (not account) {
+        JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
+        return;
+    }
+
+    JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
+              DRing::CallSignal::StateChange::name,
+              callData.alias_.c_str(),
+              callId.c_str(),
+              state.c_str());
+
+    if (account->getAccountID() != callData.accountId_)
+        return;
+
+    {
+        std::unique_lock<std::mutex> lock {callData.mtx_};
+        callData.signals_.emplace_back(
+            CallData::Signal(DRing::CallSignal::StateChange::name, state));
+    }
+    // NOTE. Only states that we are interested on will notify the CV. If this
+    // unit test is modified to process other states, they must be added here.
+    if (state == "CURRENT" or state == "OVER" or state == "HUNGUP" or state == "RINGING") {
+        callData.cv_.notify_one();
+    }
+}
+
+void
+SipSrtpTest::onMediaNegotiationStatus(const std::string& callId,
+                                      const std::string& event,
+                                      CallData& callData)
+{
+    auto call = Manager::instance().getCallFromCallID(callId);
+    if (not call) {
+        JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
+        return;
+    }
+
+    auto account = call->getAccount().lock();
+    if (not account) {
+        JAMI_WARN("Account owning the call [%s] does not exist!", callId.c_str());
+        return;
+    }
+
+    JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
+              DRing::CallSignal::MediaNegotiationStatus::name,
+              account->getAccountDetails()[ConfProperties::ALIAS].c_str(),
+              call->getCallId().c_str(),
+              event.c_str());
+
+    if (account->getAccountID() != callData.accountId_)
+        return;
+
+    {
+        std::unique_lock<std::mutex> lock {callData.mtx_};
+        callData.signals_.emplace_back(
+            CallData::Signal(DRing::CallSignal::MediaNegotiationStatus::name, event));
+    }
+
+    callData.cv_.notify_one();
+}
+
+bool
+SipSrtpTest::waitForSignal(CallData& callData,
+                           const std::string& expectedSignal,
+                           const std::string& expectedEvent)
+{
+    const std::chrono::seconds TIME_OUT {30};
+    std::unique_lock<std::mutex> lock {callData.mtx_};
+
+    // Combined signal + event (if any).
+    std::string sigEvent(expectedSignal);
+    if (not expectedEvent.empty())
+        sigEvent += "::" + expectedEvent;
+
+    JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
+
+    auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
+        // Search for the expected signal in list of received signals.
+        bool pred = false;
+        for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
+            // The predicate is true if the signal names match, and if the
+            // expectedEvent is not empty, the events must also match.
+            if (it->name_ == expectedSignal
+                and (expectedEvent.empty() or it->event_ == expectedEvent)) {
+                pred = true;
+                // Done with this signal.
+                callData.signals_.erase(it);
+                break;
+            }
+        }
+
+        return pred;
+    });
+
+    if (not res) {
+        JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
+                 callData.alias_.c_str(),
+                 sigEvent.c_str());
+
+        JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
+
+        for (auto const& sig : callData.signals_) {
+            JAMI_INFO() << "Signal [" << sig.name_
+                        << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
+        }
+    }
+
+    return res;
+}
+
+void
+SipSrtpTest::configureTest(CallData& aliceData, CallData& bobData)
+{
+    {
+        CPPUNIT_ASSERT(not aliceData.accountId_.empty());
+        auto const& account = Manager::instance().getAccount<SIPAccount>(aliceData.accountId_);
+        aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
+        aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
+        account->setLocalPort(aliceData.listeningPort_);
+        account->enableIceForMedia(true);
+    }
+
+    {
+        CPPUNIT_ASSERT(not bobData.accountId_.empty());
+        auto const& account = Manager::instance().getAccount<SIPAccount>(bobData.accountId_);
+        bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
+        bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
+        account->setLocalPort(bobData.listeningPort_);
+    }
+
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> signalHandlers;
+
+    // Insert needed signal handlers.
+    signalHandlers.insert(DRing::exportable_callback<DRing::CallSignal::IncomingCallWithMedia>(
+        [&](const std::string& accountId,
+            const std::string& callId,
+            const std::string&,
+            const std::vector<DRing::MediaMap> mediaList) {
+            auto user = getUserAlias(callId);
+            if (not user.empty())
+                onIncomingCallWithMedia(accountId,
+                                        callId,
+                                        mediaList,
+                                        user == aliceData.alias_ ? aliceData : bobData);
+        }));
+
+    signalHandlers.insert(DRing::exportable_callback<DRing::CallSignal::StateChange>(
+        [&](const std::string& callId, const std::string& state, signed) {
+            auto user = getUserAlias(callId);
+            if (not user.empty())
+                onCallStateChange(callId, state, user == aliceData.alias_ ? aliceData : bobData);
+        }));
+
+    signalHandlers.insert(DRing::exportable_callback<DRing::CallSignal::MediaNegotiationStatus>(
+        [&](const std::string& callId,
+            const std::string& event,
+            const std::vector<std::map<std::string, std::string>>& /* mediaList */) {
+            auto user = getUserAlias(callId);
+            if (not user.empty())
+                onMediaNegotiationStatus(callId,
+                                         event,
+                                         user == aliceData.alias_ ? aliceData : bobData);
+        }));
+
+    DRing::registerSignalHandlers(signalHandlers);
+}
+
+void
+SipSrtpTest::audio_video_call(std::vector<MediaAttribute> offer,
+                              std::vector<MediaAttribute> answer,
+                              bool validateMedia)
+{
+    JAMI_INFO("=== Begin test %s ===", __FUNCTION__);
+
+    configureTest(aliceData_, bobData_);
+
+    JAMI_INFO("=== Start a call and validate ===");
+
+    std::string bobUri = "127.0.0.1:" + std::to_string(bobData_.listeningPort_);
+
+    aliceData_.callId_ = DRing::placeCallWithMedia(aliceData_.accountId_,
+                                                   bobUri,
+                                                   MediaAttribute::mediaAttributesToMediaMaps(
+                                                       offer));
+
+    CPPUNIT_ASSERT(not aliceData_.callId_.empty());
+
+    JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
+              aliceData_.accountId_.c_str(),
+              bobData_.accountId_.c_str());
+
+    // Give it some time to ring
+    std::this_thread::sleep_for(std::chrono::seconds(2));
+
+    // Wait for call to be processed.
+    CPPUNIT_ASSERT(
+        waitForSignal(aliceData_, DRing::CallSignal::StateChange::name, StateEvent::RINGING));
+
+    // Wait for incoming call signal.
+    CPPUNIT_ASSERT(waitForSignal(bobData_, DRing::CallSignal::IncomingCallWithMedia::name));
+
+    // Answer the call.
+    DRing::acceptWithMedia(bobData_.callId_, MediaAttribute::mediaAttributesToMediaMaps(answer));
+
+    // Wait for media negotiation complete signal.
+    CPPUNIT_ASSERT(waitForSignal(bobData_,
+                                 DRing::CallSignal::MediaNegotiationStatus::name,
+                                 DRing::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
+
+    // Wait for the StateChange signal.
+    CPPUNIT_ASSERT(
+        waitForSignal(bobData_, DRing::CallSignal::StateChange::name, StateEvent::CURRENT));
+
+    JAMI_INFO("BOB answered the call [%s]", bobData_.callId_.c_str());
+
+    // Wait for media negotiation complete signal.
+    CPPUNIT_ASSERT(waitForSignal(aliceData_,
+                                 DRing::CallSignal::MediaNegotiationStatus::name,
+                                 DRing::Media::MediaNegotiationStatusEvents::NEGOTIATION_SUCCESS));
+
+    // Validate Alice's media
+    if (validateMedia) {
+        auto activeMediaList = Manager::instance().getMediaAttributeList(aliceData_.callId_);
+        CPPUNIT_ASSERT_EQUAL(offer.size(), activeMediaList.size());
+        // Audio
+        CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_AUDIO, activeMediaList[0].type_);
+        CPPUNIT_ASSERT_EQUAL(offer[0].enabled_, activeMediaList[0].enabled_);
+
+        // Video
+        if (offer.size() > 1) {
+            CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_VIDEO, activeMediaList[1].type_);
+            CPPUNIT_ASSERT_EQUAL(offer[1].enabled_, activeMediaList[1].enabled_);
+        }
+    }
+
+    // Validate Bob's media
+    if (validateMedia) {
+        auto activeMediaList = Manager::instance().getMediaAttributeList(bobData_.callId_);
+        CPPUNIT_ASSERT_EQUAL(answer.size(), activeMediaList.size());
+        // Audio
+        CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_AUDIO, activeMediaList[0].type_);
+        CPPUNIT_ASSERT_EQUAL(answer[0].enabled_, activeMediaList[0].enabled_);
+
+        // Video
+        if (offer.size() > 1) {
+            CPPUNIT_ASSERT_EQUAL(MediaType::MEDIA_VIDEO, activeMediaList[1].type_);
+            CPPUNIT_ASSERT_EQUAL(answer[1].enabled_, activeMediaList[1].enabled_);
+        }
+    }
+
+    // Give some time to media to start and flow
+    std::this_thread::sleep_for(std::chrono::seconds(3));
+
+    // Bob hang-up.
+    JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
+    DRing::hangUp(bobData_.callId_);
+
+    CPPUNIT_ASSERT(
+        waitForSignal(aliceData_, DRing::CallSignal::StateChange::name, StateEvent::HUNGUP));
+
+    JAMI_INFO("Call terminated on both sides");
+}
+
+void
+SipSrtpTest::audio_video_srtp_enabled_test()
+{
+    // Test with video enabled on Alice's side and disabled
+    // on Bob's side.
+
+    auto const aliceAcc = Manager::instance().getAccount<SIPAccount>(aliceData_.accountId_);
+    CPPUNIT_ASSERT(aliceAcc->isSrtpEnabled());
+
+    auto const bobAcc = Manager::instance().getAccount<SIPAccount>(bobData_.accountId_);
+    CPPUNIT_ASSERT(bobAcc->isSrtpEnabled());
+
+    std::vector<MediaAttribute> offer;
+    std::vector<MediaAttribute> answer;
+
+    MediaAttribute audio(MediaType::MEDIA_AUDIO);
+    MediaAttribute video(MediaType::MEDIA_VIDEO);
+
+    // Configure Alice
+    audio.enabled_ = true;
+    audio.label_ = "audio_0";
+    offer.emplace_back(audio);
+
+    video.enabled_ = true;
+    video.label_ = "video_0";
+    aliceAcc->enableVideo(true);
+    offer.emplace_back(video);
+
+    // Configure Bob
+    audio.enabled_ = true;
+    audio.label_ = "audio_0";
+    answer.emplace_back(audio);
+
+    video.enabled_ = false;
+    video.label_ = "video_0";
+    bobAcc->enableVideo(false);
+    answer.emplace_back(video);
+
+    // Run the scenario
+    audio_video_call(offer, answer);
+}
+
+} // namespace test
+} // namespace jami
+
+RING_TEST_RUNNER(jami::test::SipSrtpTest::name())