diff --git a/src/manager.cpp b/src/manager.cpp
index 3da74cb1b77957ecd024c891590ca2e0c7a055d5..b52d16e1bbc1ef68d1eff6cdec26e7660e8a86c8 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -983,6 +983,10 @@ Manager::answerCall(const std::string& call_id)
 
     addAudio(*call);
 
+    // Start recording if set in preference
+    if (audioPreference.getIsAlwaysRecording())
+        toggleRecordingCall(call_id);
+
     return result;
 }
 
@@ -1992,6 +1996,9 @@ Manager::peerAnsweredCall(Call& call)
         getRingBufferPool().flushAllBuffers();
         pimpl_->audiodriver_->flushUrgent();
     }
+
+    if (audioPreference.getIsAlwaysRecording())
+        toggleRecordingCall(call_id);
 }
 
 //THREAD=VoIP Call=Outgoing
diff --git a/src/media/audio/audio_receive_thread.cpp b/src/media/audio/audio_receive_thread.cpp
index e2c3ca0df70f78eb4656fcdafa5385f7b71e154d..5ba7bf7979e2a939ba20e4063995daf92f75338f 100644
--- a/src/media/audio/audio_receive_thread.cpp
+++ b/src/media/audio/audio_receive_thread.cpp
@@ -88,6 +88,10 @@ AudioReceiveThread::setup()
     Smartools::getInstance().setRemoteAudioCodec(audioDecoder_->getDecoderName());
 
     ringbuffer_ = Manager::instance().getRingBufferPool().getRingBuffer(id_);
+
+    if (onSetupSuccess_)
+        onSetupSuccess_(MEDIA_AUDIO);
+
     return true;
 }
 
@@ -135,8 +139,9 @@ AudioReceiveThread::getInfo() const
 }
 
 void
-AudioReceiveThread::startLoop()
+AudioReceiveThread::startLoop(const std::function<void(MediaType)>& cb)
 {
+    onSetupSuccess_ = cb;
     loop_.start();
 }
 
diff --git a/src/media/audio/audio_receive_thread.h b/src/media/audio/audio_receive_thread.h
index af1aea0732aeaefc42f65276f3cd6355ccfb4759..418efcb5b7089b6ad9e88854b5854d9a2e5999de 100644
--- a/src/media/audio/audio_receive_thread.h
+++ b/src/media/audio/audio_receive_thread.h
@@ -22,11 +22,13 @@
 #include "audiobuffer.h"
 #include "media_buffer.h"
 #include "media_device.h"
+#include "media_codec.h"
 #include "noncopyable.h"
 #include "observer.h"
 #include "socket_pair.h"
 #include "threadloop.h"
 
+#include <functional>
 #include <sstream>
 
 namespace jami {
@@ -48,7 +50,7 @@ public:
     MediaStream getInfo() const;
 
     void addIOContext(SocketPair &socketPair);
-    void startLoop();
+    void startLoop(const std::function<void(MediaType)>& cb);
 
 private:
     NON_COPYABLE(AudioReceiveThread);
@@ -78,6 +80,8 @@ private:
 
     uint16_t mtu_;
 
+    std::function<void(MediaType)> onSetupSuccess_;
+
     ThreadLoop loop_;
     bool setup();
     void process();
diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp
index 306157580bc3bbcbe4b8a9512f8acd4b4b9ef695..005dbde58721061ad3379d89bb00205541d7f217 100644
--- a/src/media/audio/audio_rtp_session.cpp
+++ b/src/media/audio/audio_rtp_session.cpp
@@ -135,7 +135,7 @@ AudioRtpSession::startReceiver()
                                                 receive_.receiving_sdp,
                                                 mtu_));
     receiveThread_->addIOContext(*socketPair_);
-    receiveThread_->startLoop();
+    receiveThread_->startLoop(onSuccessfulSetup_);
 }
 
 void
diff --git a/src/media/rtp_session.h b/src/media/rtp_session.h
index 5cf24a5721f2577bff8d63364703806fb20cbb24..7cba8b4529fc2abab501e320956cd23b6a052e69 100644
--- a/src/media/rtp_session.h
+++ b/src/media/rtp_session.h
@@ -3,6 +3,7 @@
  *
  *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
  *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -25,6 +26,7 @@
 #include "sip/sip_utils.h"
 #include "media/media_codec.h"
 
+#include <functional>
 #include <string>
 #include <memory>
 #include <mutex>
@@ -55,6 +57,11 @@ public:
 
     void setMtu(uint16_t mtu) { mtu_ = mtu; }
 
+    void setSuccessfulSetupCb(const std::function<void(MediaType)>& cb)
+    {
+        onSuccessfulSetup_ = cb;
+    }
+
     virtual void initRecorder(std::shared_ptr<MediaRecorder>& rec) = 0;
     virtual void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) = 0;
 
@@ -70,6 +77,8 @@ protected:
 
     uint16_t mtu_;
 
+    std::function<void(MediaType)> onSuccessfulSetup_;
+
     std::string getRemoteRtpUri() const {
         return "rtp://" + send_.addr.toString(true);
     }
diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp
index 14ac429f7d58d28d35cb83edd999d60d6b348fbc..563827497cc169e5b7fbc33fc453e58643148c92 100644
--- a/src/media/video/video_input.cpp
+++ b/src/media/video/video_input.cpp
@@ -3,6 +3,7 @@
  *
  *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
  *  Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -210,6 +211,7 @@ bool VideoInput::setup()
         JAMI_ERR("start sink failed");
 
     JAMI_DBG("VideoInput ready to capture");
+
     return true;
 }
 
diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index 476fe843ca802c2ce2cf65e63944b2aa719dd3d1..d3135d681711778d53465aea30c96458467005d6 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -3,6 +3,7 @@
  *
  *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
  *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -67,8 +68,9 @@ VideoReceiveThread::~VideoReceiveThread()
 }
 
 void
-VideoReceiveThread::startLoop()
+VideoReceiveThread::startLoop(const std::function<void(MediaType)>& cb)
 {
+    onSetupSuccess_ = cb;
     loop_.start();
 }
 
@@ -150,6 +152,9 @@ bool VideoReceiveThread::setup()
     // Send the resolution in smartInfo
     Smartools::getInstance().setResolution(id_, dstWidth_, dstHeight_);
 
+    if (onSetupSuccess_)
+        onSetupSuccess_(MEDIA_VIDEO);
+
     return true;
 }
 
diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h
index 8e85e50817dde64b0aaa006227113066474c5388..2591e2dc530dd13270d5d32d30e047449f09f8c7 100644
--- a/src/media/video/video_receive_thread.h
+++ b/src/media/video/video_receive_thread.h
@@ -3,6 +3,7 @@
  *
  *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
  *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -25,11 +26,13 @@
 #include "video_base.h"
 #include "media_codec.h"
 #include "media_io_handle.h"
+#include "media_codec.h"
 #include "media_device.h"
 #include "media_stream.h"
 #include "threadloop.h"
 #include "noncopyable.h"
 
+#include <functional>
 #include <map>
 #include <string>
 #include <climits>
@@ -49,7 +52,7 @@ class VideoReceiveThread : public VideoGenerator {
 public:
     VideoReceiveThread(const std::string &id, const std::string &sdp, uint16_t mtu);
     ~VideoReceiveThread();
-    void startLoop();
+    void startLoop(const std::function<void(MediaType)>& cb);
 
     void addIOContext(SocketPair& socketPair);
     void setRequestKeyFrameCallback(std::function<void (void)>);
@@ -98,6 +101,8 @@ private:
     static int interruptCb(void *ctx);
     static int readFunction(void *opaque, uint8_t *buf, int buf_size);
 
+    std::function<void(MediaType)> onSetupSuccess_;
+
     ThreadLoop loop_;
 
     // used by ThreadLoop
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index dad69d876ae461f00ec837a4843f88457631c92a..9afab5fa018a6d30295607a5173f0eee61be2251 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -164,7 +164,7 @@ void VideoRtpSession::startReceiver()
         // XXX keyframe requests can timeout if unanswered
         receiveThread_->setRequestKeyFrameCallback(requestKeyFrameCallback_);
         receiveThread_->addIOContext(*socketPair_);
-        receiveThread_->startLoop();
+        receiveThread_->startLoop(onSuccessfulSetup_);
     } else {
         JAMI_DBG("Video receiving disabled");
         if (receiveThread_)
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index e7c4b42a150609991e4479777d57a08d38f21d5f..f9ac13590ee7e1a07d7a1c69bf8a57bf7a4d62e9 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -6,6 +6,7 @@
  *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
  *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
  *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -974,6 +975,8 @@ SIPCall::startAllMedia()
 #endif
         rtp->updateMedia(remote, local);
 
+        rtp->setSuccessfulSetupCb([this](MediaType type){ rtpSetupSuccess(type); });
+
         // Not restarting media loop on hold as it's a huge waste of CPU ressources
         // because of the audio loop
         if (getState() != CallState::HOLD) {
@@ -1263,6 +1266,10 @@ SIPCall::getDetails() const
 bool
 SIPCall::toggleRecording()
 {
+    pendingRecord_ = true;
+    if (not readyToRecord_)
+        return true;
+
     // add streams to recorder before starting the record
     if (not Call::isRecording()) {
         std::stringstream ss;
@@ -1278,6 +1285,7 @@ SIPCall::toggleRecording()
     } else {
         deinitRecorder();
     }
+    pendingRecord_ = false;
     return Call::toggleRecording();
 }
 
@@ -1388,4 +1396,15 @@ SIPCall::newIceSocket(unsigned compId)
     return std::unique_ptr<IceSocket> {new IceSocket(mediaTransport_, compId)};
 }
 
+void
+SIPCall::rtpSetupSuccess(MediaType type)
+{
+    if ((not isAudioOnly() && type == MEDIA_VIDEO)
+        || (isAudioOnly() && type == MEDIA_AUDIO))
+        readyToRecord_ = true;
+
+    if (pendingRecord_ && readyToRecord_)
+        toggleRecording();
+}
+
 } // namespace jami
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 31fe17ad5a29eae532cb76bbb5381fc1b38b9668..ab44cab6d88170da15d4a05820f6c156c8dc7c17 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -5,6 +5,7 @@
  *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
  *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
  *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+ *  Author: Philippe Gorley <philippe.gorley@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
@@ -28,6 +29,7 @@
 #endif
 
 #include "call.h"
+#include "media_codec.h" // for MediaType enum
 #include "sip_utils.h"
 
 #ifdef ENABLE_VIDEO
@@ -241,6 +243,8 @@ public: // NOT SIP RELATED (good candidates to be moved elsewhere)
 
     void deinitRecorder();
 
+    void rtpSetupSuccess(MediaType type);
+
 private:
     NON_COPYABLE(SIPCall);
 
@@ -331,6 +335,9 @@ private:
     std::shared_ptr<IceTransport> tmpMediaTransport_;
 
     std::string peerUri_{};
+
+    bool readyToRecord_ {false};
+    bool pendingRecord_ {false};
 };
 
 // Helpers