diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml
index 100e178c4738e7071a0d3afcd86d7833736e5ccf..a8adc1e659a337e3507f7c8044d44bfcd2e1d965 100644
--- a/bin/dbus/cx.ring.Ring.CallManager.xml
+++ b/bin/dbus/cx.ring.Ring.CallManager.xml
@@ -996,7 +996,7 @@
               <tp:docstring>
                 The acceptable states are:
                 <ul>
-                  <li>NEGOTIATION_SUCCESS: Media negitiation succeded</li>
+                  <li>NEGOTIATION_SUCCESS: Media negotiation succeeded</li>
                   <li>NEGOTIATION_FAIL: Media negotiation failed</li>
                 </ul>
               </tp:docstring>
diff --git a/src/conference.cpp b/src/conference.cpp
index 2c5c35eebb7112d04aa863902615fa0c7bdd692f..b92093ba1d2b94c6c664fe66ee16fded1003a786 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -52,15 +52,20 @@ using namespace std::literals;
 
 namespace jami {
 
-Conference::Conference()
+Conference::Conference(bool enableVideo)
     : id_(Manager::instance().callFactory.getNewCallID())
 #ifdef ENABLE_VIDEO
     , mediaInput_(Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice())
+    , videoEnabled_(enableVideo)
 #endif
 {
     JAMI_INFO("Create new conference %s", id_.c_str());
 
 #ifdef ENABLE_VIDEO
+    // We are done if the video is disabled.
+    if (not videoEnabled_)
+        return;
+
     getVideoMixer()->setOnSourcesUpdated([this](std::vector<video::SourceInfo>&& infos) {
         runOnMainThread([w = weak(), infos = std::move(infos)] {
             auto shared = w.lock();
@@ -376,7 +381,7 @@ Conference::takeOverMediaSourceControl(const std::string& callId)
 }
 
 void
-Conference::add(const std::string& participant_id)
+Conference::addParticipant(const std::string& participant_id)
 {
     JAMI_DBG("Adding call %s to conference %s", participant_id.c_str(), id_.c_str());
 
@@ -574,7 +579,7 @@ Conference::detachVideo(Observable<std::shared_ptr<MediaFrame>>* frame)
 }
 
 void
-Conference::remove(const std::string& participant_id)
+Conference::removeParticipant(const std::string& participant_id)
 {
     if (participants_.erase(participant_id)) {
         if (auto call = getCall(participant_id)) {
@@ -589,7 +594,7 @@ Conference::remove(const std::string& participant_id)
 }
 
 void
-Conference::attach()
+Conference::attachLocalParticipant()
 {
     JAMI_INFO("Attach local participant to conference %s", id_.c_str());
 
@@ -610,10 +615,12 @@ Conference::attach()
         rbPool.flush(RingBufferPool::DEFAULT_ID);
 
 #ifdef ENABLE_VIDEO
-        if (auto mixer = getVideoMixer()) {
-            mixer->switchInput(mediaInput_);
-            if (not mediaSecondaryInput_.empty())
-                mixer->switchSecondaryInput(mediaSecondaryInput_);
+        if (isVideoEnabled()) {
+            if (auto mixer = getVideoMixer()) {
+                mixer->switchInput(mediaInput_);
+                if (not mediaSecondaryInput_.empty())
+                    mixer->switchSecondaryInput(mediaSecondaryInput_);
+            }
         }
 #endif
         setState(State::ACTIVE_ATTACHED);
@@ -626,7 +633,7 @@ Conference::attach()
 }
 
 void
-Conference::detach()
+Conference::detachLocalParticipant()
 {
     JAMI_INFO("Detach local participant from conference %s", id_.c_str());
 
@@ -636,8 +643,10 @@ Conference::detach()
                                                                  RingBufferPool::DEFAULT_ID);
         }
 #ifdef ENABLE_VIDEO
-        if (auto mixer = getVideoMixer()) {
-            mixer->stopInput();
+        if (isVideoEnabled()) {
+            if (auto mixer = getVideoMixer()) {
+                mixer->stopInput();
+            }
         }
 #endif
         setState(State::ACTIVE_DETACHED);
@@ -747,6 +756,11 @@ Conference::switchInput(const std::string& input)
 {
 #ifdef ENABLE_VIDEO
     mediaInput_ = input;
+
+    // Done if the video is disabled
+    if (not isVideoEnabled())
+        return;
+
     if (auto mixer = getVideoMixer()) {
         mixer->switchInput(input);
 #ifdef ENABLE_PLUGIN
@@ -766,10 +780,18 @@ Conference::switchSecondaryInput(const std::string& input)
 {
 #ifdef ENABLE_VIDEO
     mediaSecondaryInput_ = input;
-    getVideoMixer()->switchSecondaryInput(input);
+    if (isVideoEnabled()) {
+        getVideoMixer()->switchSecondaryInput(input);
+    }
 #endif
 }
 
+bool
+Conference::isVideoEnabled() const
+{
+    return videoEnabled_;
+}
+
 #ifdef ENABLE_VIDEO
 std::shared_ptr<video::VideoMixer>
 Conference::getVideoMixer()
@@ -846,7 +868,7 @@ Conference::onConfOrder(const std::string& callId, const std::string& confOrder)
                       peerID.data());
             return;
         }
-        if (root.isMember("layout")) {
+        if (isVideoEnabled() and root.isMember("layout")) {
             setLayout(root["layout"].asUInt());
         }
         if (root.isMember("activeParticipant")) {
@@ -1107,6 +1129,11 @@ Conference::muteLocalHost(bool is_muted, const std::string& mediaType)
         return;
     } else if (mediaType.compare(DRing::Media::Details::MEDIA_TYPE_VIDEO) == 0) {
 #ifdef ENABLE_VIDEO
+        if (not isVideoEnabled()) {
+            JAMI_ERR("Cant't mute, the video is disabled!");
+            return;
+        }
+
         if (is_muted == (isMediaSourceMuted(MediaType::MEDIA_VIDEO))) {
             JAMI_DBG("Local video source already in [%s] state", is_muted ? "muted" : "un-muted");
             return;
@@ -1210,7 +1237,7 @@ Conference::mergeConfInfo(ConfInfo& newInfo, const std::string& peerURI)
         updateNeeded = true;
     }
     // Send confInfo only if needed to avoid loops
-    if (updateNeeded) {
+    if (updateNeeded and isVideoEnabled()) {
         // Trigger the layout update in the mixer because the frame resolution may
         // change from participant to conference and cause a mismatch between
         // confInfo layout and rendering layout.
diff --git a/src/conference.h b/src/conference.h
index 0a2b185240da84eb2b09d1a061a5c860c40179eb..143319f1cf077960f739746962747026e8203aea 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -183,7 +183,7 @@ public:
     /**
      * Constructor for this class, increment static counter
      */
-    Conference();
+    explicit Conference(bool enableVideo);
 
     /**
      * Destructor for this class, decrement static counter
@@ -246,22 +246,22 @@ public:
     /**
      * Add a new participant to the conference
      */
-    void add(const std::string& participant_id);
+    void addParticipant(const std::string& participant_id);
 
     /**
      * Remove a participant from the conference
      */
-    void remove(const std::string& participant_id);
+    void removeParticipant(const std::string& participant_id);
 
     /**
      * Attach local audio/video to the conference
      */
-    void attach();
+    void attachLocalParticipant();
 
     /**
      * Detach local audio/video from the conference
      */
-    void detach();
+    void detachLocalParticipant();
 
     /**
      * Bind a participant to the conference
@@ -304,6 +304,8 @@ public:
 
     void onConfOrder(const std::string& callId, const std::string& order);
 
+    bool isVideoEnabled() const;
+
 #ifdef ENABLE_VIDEO
     std::shared_ptr<video::VideoMixer> getVideoMixer();
     std::string getVideoInput() const { return mediaInput_; }
@@ -338,6 +340,7 @@ private:
     std::string id_;
     State confState_ {State::ACTIVE_ATTACHED};
     ParticipantSet participants_;
+    bool videoEnabled_ {false};
 
     mutable std::mutex confInfoMutex_ {};
     ConfInfo confInfo_ {};
diff --git a/src/manager.cpp b/src/manager.cpp
index a6922ce09bc91749621982445fb434e8c741067a..305cc12925f41c57823a6f8bbc03c24841fa1d0d 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -693,7 +693,7 @@ Manager::ManagerPimpl::bindCallToConference(Call& call, Conference& conf)
 
     base_.getRingBufferPool().unBindAll(call_id);
 
-    conf.add(call_id);
+    conf.addParticipant(call_id);
     call.setConfId(conf_id);
 
     if (state == "HOLD") {
@@ -1477,7 +1477,7 @@ Manager::holdConference(const std::string& id)
     JAMI_INFO("Hold conference %s", id.c_str());
 
     if (auto conf = getConferenceFromID(id)) {
-        conf->detach();
+        conf->detachLocalParticipant();
         emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr());
         return true;
     }
@@ -1567,7 +1567,7 @@ Manager::ManagerPimpl::addMainParticipant(Conference& conf)
 {
     {
         std::lock_guard<std::mutex> lock(audioLayerMutex_);
-        conf.attach();
+        conf.attachLocalParticipant();
     }
     emitSignal<DRing::CallSignal::ConferenceChanged>(conf.getConfID(), conf.getStateStr());
     switchCall(conf.getConfID());
@@ -1617,8 +1617,19 @@ Manager::joinParticipant(const std::string& callId1, const std::string& callId2,
         return false;
     }
 
-    auto conf = std::make_shared<Conference>();
+    // NOTE: The current API does not provide the account that owns
+    // the conference that is about to be created. Thus, the video
+    // will be enabled if enabled on one account.
+    bool videoEnabled {false};
+    {
+        auto acc1 = call1->getAccount().lock();
+        auto acc2 = call2->getAccount().lock();
+        if (acc1 and acc2) {
+            videoEnabled = acc1->isVideoEnabled() or acc2->isVideoEnabled();
+        }
+    }
 
+    auto conf = std::make_shared<Conference>(videoEnabled);
     pimpl_->conferenceMap_.emplace(conf->getConfID(), conf);
 
     // Bind calls according to their state
@@ -1630,7 +1641,7 @@ Manager::joinParticipant(const std::string& callId1, const std::string& callId2,
         pimpl_->switchCall(conf->getConfID());
         conf->setState(Conference::State::ACTIVE_ATTACHED);
     } else {
-        conf->detach();
+        conf->detachLocalParticipant();
     }
 
     emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID());
@@ -1646,7 +1657,21 @@ Manager::createConfFromParticipantList(const std::vector<std::string>& participa
         return;
     }
 
-    auto conf = std::make_shared<Conference>();
+    // Check if we need to enable video.
+    // NOTE: The current API does not provide the account that owns
+    // the conference that is about to be created. Thus, the video
+    // will be enabled if enabled on one account.
+    bool videoEnabled {false};
+    for (const auto& numberaccount : participantList) {
+        std::string tostr(numberaccount.substr(0, numberaccount.find(',')));
+        std::string accountId(numberaccount.substr(numberaccount.find(',') + 1, numberaccount.size()));
+        auto account = getAccount(accountId);
+        if (account) {
+            videoEnabled |= account->isVideoEnabled();
+        }
+    }
+
+    auto conf = std::make_shared<Conference>(videoEnabled);
 
     int successCounter = 0;
 
@@ -1662,7 +1687,7 @@ Manager::createConfFromParticipantList(const std::vector<std::string>& participa
             continue;
 
         // Manager methods may behave differently if the call id participates in a conference
-        conf->add(call_id);
+        conf->addParticipant(call_id);
         successCounter++;
     }
 
@@ -1677,7 +1702,8 @@ void
 Manager::setConferenceLayout(const std::string& confId, int layout)
 {
     if (auto conf = getConferenceFromID(confId)) {
-        conf->setLayout(layout);
+        if (conf->isVideoEnabled())
+            conf->setLayout(layout);
     } else if (auto call = getCallFromCallID(confId)) {
         std::map<std::string, std::string> messages;
         Json::Value root;
@@ -1718,7 +1744,7 @@ Manager::detachLocalParticipant(const std::string& conf_id)
     JAMI_INFO("Detach local participant from conference %s", conf_id.c_str());
 
     if (auto conf = getConferenceFromID(conf_id.empty() ? getCurrentCallId() : conf_id)) {
-        conf->detach();
+        conf->detachLocalParticipant();
         emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr());
         pimpl_->unsetCurrentCall();
         return true;
@@ -1770,7 +1796,7 @@ Manager::removeParticipant(const std::string& call_id)
         return;
     }
 
-    conf->remove(call_id);
+    conf->removeParticipant(call_id);
     call->setConfId("");
 
     removeAudio(*call);
@@ -1811,7 +1837,7 @@ Manager::joinConference(const std::string& conf_id1, const std::string& conf_id2
             continue;
         }
 
-        conf->remove(p);
+        conf->removeParticipant(p);
         call->setConfId("");
         removeAudio(*call);
     }
@@ -2802,13 +2828,20 @@ Manager::ManagerPimpl::processIncomingCall(Call& incomCall, const std::string& a
                     }
                 }
             }
+            auto acc = call->getAccount().lock();
+            if (not acc) {
+                JAMI_ERR("No account detected");
+                return;
+            }
+
             // First call
-            auto conf = std::make_shared<Conference>();
+            auto conf = std::make_shared<Conference>(acc->isVideoEnabled());
+
             conferenceMap_.emplace(conf->getConfID(), conf);
 
             // Bind calls according to their state
             bindCallToConference(*call, *conf);
-            conf->detach();
+            conf->detachLocalParticipant();
 
             emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID());
         });
@@ -2850,6 +2883,7 @@ Manager::ManagerPimpl::processIncomingCall(Call& incomCall, const std::string& a
     }
 }
 
+
 AudioFormat
 Manager::hardwareAudioFormatChanged(AudioFormat format)
 {
diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp
index f7f6950f8c85af3d9bce103167da00912233d538..9271534a7d77b0099de07fd77776b59d94359bcd 100644
--- a/src/media/video/video_mixer.cpp
+++ b/src/media/video/video_mixer.cpp
@@ -90,6 +90,8 @@ VideoMixer::VideoMixer(const std::string& id)
         videoLocal_->attach(this);
     loop_.start();
     nextProcess_ = std::chrono::steady_clock::now();
+
+    JAMI_DBG("[mixer:%s] New instance created", id_.c_str());
 }
 
 VideoMixer::~VideoMixer()
@@ -108,6 +110,8 @@ VideoMixer::~VideoMixer()
     }
 
     loop_.join();
+
+    JAMI_DBG("[mixer:%s] Instance destroyed", id_.c_str());
 }
 
 void
@@ -127,7 +131,7 @@ VideoMixer::switchInput(const std::string& input)
     }
 
     if (input.empty()) {
-        JAMI_DBG("Input is empty, don't add it in the mixer");
+        JAMI_DBG("[mixer:%s] Input is empty, don't add it in the mixer", id_.c_str());
         return;
     }
 
@@ -156,7 +160,7 @@ VideoMixer::switchSecondaryInput(const std::string& input)
     videoLocalSecondary_ = getVideoInput(input);
 
     if (input.empty()) {
-        JAMI_DBG("Input is empty, don't add it in the mixer");
+        JAMI_DBG("[mixer:%s] Input is empty, don't add it in the mixer", id_.c_str());
         return;
     }
 
@@ -244,7 +248,7 @@ VideoMixer::update(Observable<std::shared_ptr<MediaFrame>>* ob,
                                                             AV_PIX_FMT_NV12);
                 x->atomic_copy(*std::static_pointer_cast<VideoFrame>(frame));
             } catch (const std::runtime_error& e) {
-                JAMI_ERR("Accel failure: %s", e.what());
+                JAMI_ERR("[mixer:%s] Accel failure: %s", id_.c_str(), e.what());
                 return;
             }
 #else
@@ -267,7 +271,7 @@ VideoMixer::process()
     try {
         output.reserve(format_, width_, height_);
     } catch (const std::bad_alloc& e) {
-        JAMI_ERR("VideoFrame::allocBuffer() failed");
+        JAMI_ERR("[mixer:%s] VideoFrame::allocBuffer() failed", id_.c_str());
         return;
     }
 
@@ -329,7 +333,7 @@ VideoMixer::process()
                     if (fooInput)
                         successfullyRendered |= render_frame(output, fooInput, x);
                     else
-                        JAMI_WARN("Nothing to render for %p", x->source);
+                        JAMI_WARN("[mixer:%s] Nothing to render for %p", id_.c_str(), x->source);
                 }
 
                 x->hasVideo = !blackFrame && successfullyRendered;
@@ -502,12 +506,12 @@ VideoMixer::start_sink()
     stop_sink();
 
     if (width_ == 0 or height_ == 0) {
-        JAMI_WARN("MX: unable to start with zero-sized output");
+        JAMI_WARN("[mixer:%s] MX: unable to start with zero-sized output", id_.c_str());
         return;
     }
 
     if (not sink_->start()) {
-        JAMI_ERR("MX: sink startup failed");
+        JAMI_ERR("[mixer:%s] MX: sink startup failed", id_.c_str());
         return;
     }
 
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 1c2e1eeab03c62a30d99928625b280dedc4edad1..6d5d6af44d7186589ce1a6e1c216efc173607919 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -1979,7 +1979,7 @@ SIPCall::startAllMedia()
     readyToRecord_ = false;
     resetMediaReady();
 #ifdef ENABLE_VIDEO
-    bool isVideoEnabled = false;
+    bool hasActiveVideo = false;
 #endif
 
     int currentCompId = 1;
@@ -1991,7 +1991,7 @@ SIPCall::startAllMedia()
 
 #ifdef ENABLE_VIDEO
         if (iter->mediaAttribute_->type_ == MEDIA_VIDEO)
-            isVideoEnabled |= iter->mediaAttribute_->enabled_;
+            hasActiveVideo |= iter->mediaAttribute_->enabled_;
 #endif
 
         // Not restarting media loop on hold as it's a huge waste of CPU ressources
@@ -2014,9 +2014,10 @@ SIPCall::startAllMedia()
 
 #ifdef ENABLE_VIDEO
     // TODO. Move this elsewhere (when adding participant to conf?)
-    if (!isVideoEnabled && !getConfId().empty()) {
+    if (!hasActiveVideo && !getConfId().empty()) {
         auto conference = Manager::instance().getConferenceFromID(getConfId());
-        conference->attachVideo(getReceiveVideoFrameActiveWriter().get(), getCallId());
+        if (conference->isVideoEnabled())
+            conference->attachVideo(getReceiveVideoFrameActiveWriter().get(), getCallId());
     }
 #endif
 
@@ -2708,17 +2709,15 @@ SIPCall::enterConference(const std::string& confId)
         return;
     }
 
-    auto videoRtp = getVideoRtp();
-    if (not videoRtp) {
-        // In conference, we need to have a video RTP session even
-        // if it's an audio only call
-        videoRtp = addDummyVideoRtpSession();
+    if (conf->isVideoEnabled()) {
+        auto videoRtp = getVideoRtp();
         if (not videoRtp) {
-            throw std::runtime_error("Failed to create dummy RTP video session");
+            JAMI_ERR("[call:%s] Failed to get a valid video RTP session", getCallId().c_str());
+            throw std::runtime_error("Failed to get a valid video RTP session");
         }
+        videoRtp->enterConference(conf.get());
     }
 
-    videoRtp->enterConference(conf.get());
 #endif
 
 #ifdef ENABLE_PLUGIN