diff --git a/daemon/src/call.h b/daemon/src/call.h
index 1d6f231a8f64bb8b59691b5609fefb621bffcc7a..b93b051c6517d68695ab7829168a791b9f513160 100644
--- a/daemon/src/call.h
+++ b/daemon/src/call.h
@@ -184,8 +184,6 @@ class Call : public Recordable {
             isIPToIP_ = IPToIP;
         }
 
-        virtual void answer() = 0;
-
         /**
          * Set my IP [not protected]
          * @param ip  The local IP address
@@ -239,6 +237,11 @@ class Call : public Recordable {
 
         virtual VoIPLink* getVoIPLink() const = 0;
 
+        /**
+         * Answer the call
+         */
+        virtual void answer() = 0;
+
         /**
          * Hang up the call
          * @param reason
diff --git a/daemon/src/iax/iaxcall.cpp b/daemon/src/iax/iaxcall.cpp
index 0dc26e6cd130a27c93d67fa61ce4dc5a45f2edab..a8cd5ab140a7930ff7f0a349c3177c924a89ef43 100644
--- a/daemon/src/iax/iaxcall.cpp
+++ b/daemon/src/iax/iaxcall.cpp
@@ -124,15 +124,26 @@ int IAXCall::getAudioCodec() const
     }
 }
 
-void IAXCall::answer()
-{
-    iax_answer(session);
-}
-
 VoIPLink*
 IAXCall::getVoIPLink() const
 { return link_; }
 
+void
+IAXCall::answer()
+{
+    Manager::instance().addStream(getCallId());
+
+    {
+        std::lock_guard<std::mutex> lock(IAXVoIPLink::mutexIAX);
+        iax_answer(session);
+    }
+
+    setState(Call::ACTIVE);
+    setConnectionState(Call::CONNECTED);
+
+    Manager::instance().getMainBuffer().flushAllBuffers();
+}
+
 void
 IAXCall::hangup(int reason UNUSED)
 {
diff --git a/daemon/src/iax/iaxcall.h b/daemon/src/iax/iaxcall.h
index f8969f1a7961ac787d807f4f7f7a518e5f6efa7a..21921475c9fbd6c0442b5fee46211c29148e44ad 100644
--- a/daemon/src/iax/iaxcall.h
+++ b/daemon/src/iax/iaxcall.h
@@ -80,11 +80,11 @@ class IAXCall : public Call {
 
         VoIPLink* getVoIPLink() const;
 
+        void answer();
+
         void hangup(int reason);
 
     private:
-        void answer();
-
         NON_COPYABLE(IAXCall);
 
         IAXVoIPLink* link_;
diff --git a/daemon/src/iax/iaxvoiplink.cpp b/daemon/src/iax/iaxvoiplink.cpp
index ec3d3184bea794b06240e51e89397d32170efc21..c7d6fedaa99579b5dc9a7388aa6cec71b1e894f5 100644
--- a/daemon/src/iax/iaxvoiplink.cpp
+++ b/daemon/src/iax/iaxvoiplink.cpp
@@ -313,24 +313,6 @@ IAXVoIPLink::newOutgoingCall(const std::string& id, const std::string& toUrl, co
     return call;
 }
 
-
-void
-IAXVoIPLink::answer(Call *call)
-{
-    Manager::instance().addStream(call->getCallId());
-
-    {
-        std::lock_guard<std::mutex> lock(mutexIAX);
-        call->answer();
-    }
-
-    call->setState(Call::ACTIVE);
-    call->setConnectionState(Call::CONNECTED);
-
-    Manager::instance().getMainBuffer().flushAllBuffers();
-}
-
-
 void
 IAXVoIPLink::peerHungup(const std::string& id)
 {
@@ -348,8 +330,6 @@ IAXVoIPLink::peerHungup(const std::string& id)
     removeIaxCall(id);
 }
 
-
-
 void
 IAXVoIPLink::onhold(const std::string& id)
 {
diff --git a/daemon/src/iax/iaxvoiplink.h b/daemon/src/iax/iaxvoiplink.h
index 533c01d2b910d98d270bea774c0152c1e0bd51c5..b65be4d9ef99775047a465ab4d4efe685513dee9 100644
--- a/daemon/src/iax/iaxvoiplink.h
+++ b/daemon/src/iax/iaxvoiplink.h
@@ -121,12 +121,6 @@ class IAXVoIPLink : public VoIPLink {
          */
         virtual std::shared_ptr<Call> newOutgoingCall(const std::string& id, const std::string& toUrl, const std::string &account_id);
 
-        /**
-         * Answer a call
-         * @param c The call
-         */
-        virtual void answer(Call *c);
-
         /**
          * Peer Hungup a call
          * @param id The ID of the call
diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp
index ff0a253c470be0367ddb1e0af575a0e7d3d8caaf..b8cfcb18ad9bd094dab82c235e0e7739d71e1aa9 100644
--- a/daemon/src/managerimpl.cpp
+++ b/daemon/src/managerimpl.cpp
@@ -418,7 +418,7 @@ bool ManagerImpl::answerCall(const std::string& call_id)
     }
 
     try {
-        call->getVoIPLink()->answer(call.get());
+        call->answer();
     } catch (const std::runtime_error &e) {
         ERROR("%s", e.what());
         result = false;
diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp
index 1a4d4d564efbd49141d30a5d365f808685d770da..81c38fb275b6504a43fb01a555bb163c6c2e35a2 100644
--- a/daemon/src/sip/sipcall.cpp
+++ b/daemon/src/sip/sipcall.cpp
@@ -51,6 +51,26 @@ getSettings()
 static const int INITIAL_SIZE = 16384;
 static const int INCREMENT_SIZE = INITIAL_SIZE;
 
+static void
+updateSDPFromSTUN(SIPCall &call, SIPAccount &account, const SipTransport &transport)
+{
+    std::vector<long> socketDescriptors(call.getAudioRtp().getSocketDescriptors());
+
+    try {
+        std::vector<pj_sockaddr> stunPorts(transport.getSTUNAddresses(account, socketDescriptors));
+
+        // FIXME: get video sockets
+        //stunPorts.resize(4);
+
+        account.setPublishedAddress(stunPorts[0]);
+        // published IP MUST be updated first, since RTCP depends on it
+        call.getLocalSDP()->setPublishedIP(account.getPublishedAddress());
+        call.getLocalSDP()->updatePorts(stunPorts);
+    } catch (const std::runtime_error &e) {
+        ERROR("%s", e.what());
+    }
+}
+
 SIPCall::SIPCall(const std::string& id, Call::CallType type,
         pj_caching_pool *caching_pool, const std::string &account_id) :
     Call(id, type, account_id)
@@ -77,29 +97,6 @@ void SIPCall::setContactHeader(pj_str_t *contact)
     pj_strcpy(&contactHeader_, contact);
 }
 
-void SIPCall::answer()
-{
-    pjsip_tx_data *tdata;
-    if (!inv->last_answer)
-        throw std::runtime_error("Should only be called for initial answer");
-
-    // answer with SDP if no SDP was given in initial invite (i.e. inv->neg is NULL)
-    if (pjsip_inv_answer(inv, PJSIP_SC_OK, NULL, !inv->neg ? local_sdp_->getLocalSdpSession() : NULL, &tdata) != PJ_SUCCESS)
-        throw std::runtime_error("Could not init invite request answer (200 OK)");
-
-    // contactStr must stay in scope as long as tdata
-    if (contactHeader_.slen) {
-        DEBUG("Answering with contact header: %.*s", contactHeader_.slen, contactHeader_.ptr);
-        sip_utils::addContactHeader(&contactHeader_, tdata);
-    }
-
-    if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS)
-        throw std::runtime_error("Could not send invite request answer (200 OK)");
-
-    setConnectionState(CONNECTED);
-    setState(ACTIVE);
-}
-
 std::map<std::string, std::string>
 SIPCall::createHistoryEntry() const
 {
@@ -226,6 +223,49 @@ VoIPLink*
 SIPCall::getVoIPLink() const
 { return &SIPVoIPLink::instance(); }
 
+void SIPCall::answer()
+{
+    SIPAccount *account = Manager::instance().getSipAccount(getAccountId());
+    if (!account) {
+        ERROR("Could not find account %s", getAccountId().c_str());
+        return;
+    }
+
+    if (!inv->neg) {
+        SIPVoIPLink& siplink = SIPVoIPLink::instance();
+
+        WARN("Negotiator is NULL, we've received an INVITE without an SDP");
+        pjmedia_sdp_session *dummy = 0;
+        siplink.createSDPOffer(inv, &dummy);
+
+        if (account->isStunEnabled())
+            updateSDPFromSTUN(*this, *account, *siplink.sipTransport);
+    }
+
+    pj_str_t contact(account->getContactHeader());
+    setContactHeader(&contact);
+
+    pjsip_tx_data *tdata;
+    if (!inv->last_answer)
+        throw std::runtime_error("Should only be called for initial answer");
+
+    // answer with SDP if no SDP was given in initial invite (i.e. inv->neg is NULL)
+    if (pjsip_inv_answer(inv, PJSIP_SC_OK, NULL, !inv->neg ? local_sdp_->getLocalSdpSession() : NULL, &tdata) != PJ_SUCCESS)
+        throw std::runtime_error("Could not init invite request answer (200 OK)");
+
+    // contactStr must stay in scope as long as tdata
+    if (contactHeader_.slen) {
+        DEBUG("Answering with contact header: %.*s", contactHeader_.slen, contactHeader_.ptr);
+        sip_utils::addContactHeader(&contactHeader_, tdata);
+    }
+
+    if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS)
+        throw std::runtime_error("Could not send invite request answer (200 OK)");
+
+    setConnectionState(CONNECTED);
+    setState(ACTIVE);
+}
+
 void
 SIPCall::hangup(int reason)
 {
diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h
index 558aa39985c9dc3e6e69283030ea1448ed1438e9..974ac10deb0b6bdf06cafded515a3150bc97aa0f 100644
--- a/daemon/src/sip/sipcall.h
+++ b/daemon/src/sip/sipcall.h
@@ -103,8 +103,6 @@ class SIPCall : public Call {
             return pool_;
         }
 
-        void answer();
-
         /**
          * The invite session to be reused in case of transfer
          */
@@ -117,6 +115,8 @@ class SIPCall : public Call {
 
         VoIPLink* getVoIPLink() const;
 
+        void answer();
+
         void hangup(int reason);
 
     private:
diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp
index 09814478831fd7e1e5742c7423ccf5fbe9f662a7..b91cdb02ae085ef9eae27685503dd2094a39e6e3 100644
--- a/daemon/src/sip/sipvoiplink.cpp
+++ b/daemon/src/sip/sipvoiplink.cpp
@@ -1020,34 +1020,6 @@ std::shared_ptr<Call> SIPVoIPLink::newRegisteredAccountCall(const std::string& i
     return call;
 }
 
-void
-SIPVoIPLink::answer(Call *call)
-{
-    if (!call)
-        return;
-
-    SIPCall *sipCall = static_cast<SIPCall*>(call);
-    SIPAccount *account = Manager::instance().getSipAccount(sipCall->getAccountId());
-
-    if (!account) {
-        ERROR("Could not find account %s", sipCall->getAccountId().c_str());
-        return;
-    }
-
-    if (!sipCall->inv->neg) {
-        WARN("Negotiator is NULL, we've received an INVITE without an SDP");
-        pjmedia_sdp_session *dummy = 0;
-        sdp_create_offer_cb(sipCall->inv, &dummy);
-
-        if (account->isStunEnabled())
-            updateSDPFromSTUN(*sipCall, *account, *SIPVoIPLink::instance().sipTransport);
-    }
-
-    pj_str_t contact(account->getContactHeader());
-    sipCall->setContactHeader(&contact);
-    sipCall->answer();
-}
-
 static void
 stopRtpIfCurrent(const std::string &id, SIPCall &call)
 {
@@ -2359,3 +2331,6 @@ void SIPVoIPLink::loadIP2IPSettings()
         ERROR("%s", e.what());
     }
 }
+
+void SIPVoIPLink::createSDPOffer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
+{ sdp_create_offer_cb(inv, p_offer); }
diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h
index 97364130900f53c0356cee812e5ef5994b3a6f1e..f727245aae9c89d4d10b6b5b5102fbf392c659ca 100644
--- a/daemon/src/sip/sipvoiplink.h
+++ b/daemon/src/sip/sipvoiplink.h
@@ -159,12 +159,6 @@ class SIPVoIPLink : public VoIPLink {
                                                        const std::string& toUrl,
                                                        const std::string &account_id);
 
-        /**
-         * Answer the call
-         * @param c The call
-         */
-        virtual void answer(Call *c);
-
         /**
          * Hang up the call
          * @param id The call identifier
@@ -283,6 +277,9 @@ class SIPVoIPLink : public VoIPLink {
     public:
         void loadIP2IPSettings();
 
+        static void createSDPOffer(pjsip_inv_session *inv,
+                                   pjmedia_sdp_session **p_offer);
+
         /**
          * Instance that maintain and manage transport (UDP, TLS)
          */
diff --git a/daemon/src/voiplink.h b/daemon/src/voiplink.h
index acdb080a8b0fe8f446b66cb261a0d5af70a65217..1cd5e17133da9b94adfa03230a85c7934a4e97f9 100644
--- a/daemon/src/voiplink.h
+++ b/daemon/src/voiplink.h
@@ -93,12 +93,6 @@ class VoIPLink {
          */
         virtual std::vector<std::shared_ptr<Call> > getCalls(const std::string &account_id) const = 0;
 
-        /**
-         * Answer the call
-         * @param c The call
-         */
-        virtual void answer(Call *c) = 0;
-
         /**
         * Peer Hung up a call
         * @param id The call identifier