diff --git a/src/call.h b/src/call.h
index 49b77bcf6f2437663ff34591d71d263a9381280e..55caa1430df24e74a33512edb544a27f27cae076 100644
--- a/src/call.h
+++ b/src/call.h
@@ -357,18 +357,6 @@ public:
     virtual std::vector<MediaAttribute> getMediaAttributeList() const = 0;
 
 #ifdef ENABLE_VIDEO
-    /**
-     * Add a dummy video stream with the attached sink.
-     * Typically needed in conference to display infos for participants
-     * that have joined the conference without video (audio only).
-     */
-    virtual bool addDummyVideoRtpSession() = 0;
-
-    /**
-     * Remove all dummy video streams.
-     */
-    virtual void removeDummyVideoRtpSessions() = 0;
-
     virtual std::shared_ptr<Observable<std::shared_ptr<MediaFrame>>>
     getReceiveVideoFrameActiveWriter() = 0;
     virtual void createSinks(const ConfInfo& infos) = 0;
diff --git a/src/conference.cpp b/src/conference.cpp
index 5054a2f7e1590e13ede037ae2a1d411f3bd64f23..be023394b9e9de47edeb5ddb90edcbf891d039bb 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -106,52 +106,81 @@ Conference::Conference(const std::shared_ptr<Account>& account)
             std::unique_lock<std::mutex> lk(shared->videoToCallMtx_);
             for (const auto& info : infos) {
                 std::string uri {};
-                std::string deviceId {};
-                auto it = shared->videoToCall_.find(info.source);
-                if (it == shared->videoToCall_.end())
-                    it = shared->videoToCall_.emplace_hint(it, info.source, std::string());
                 bool isLocalMuted = false;
-                // If not local
-                if (!it->second.empty()) {
-                    // Retrieve calls participants
-                    // TODO: this is a first version, we assume that the peer is not
-                    // a master of a conference and there is only one remote
-                    // In the future, we should retrieve confInfo from the call
-                    // To merge layouts informations
-                    if (auto call = std::dynamic_pointer_cast<SIPCall>(getCall(it->second))) {
+                std::string deviceId {};
+                auto active = false;
+                if (!info.id.empty()) {
+                    if (auto call = std::dynamic_pointer_cast<SIPCall>(getCall(info.id))) {
                         uri = call->getPeerNumber();
                         isLocalMuted = call->isPeerMuted();
                         if (auto* transport = call->getTransport())
                             deviceId = transport->deviceId();
                     }
+                    if (auto videoMixer = shared->videoMixer_)
+                        active = videoMixer->verifyActive(info.id);
+                    std::string_view peerId = string_remove_suffix(uri, '@');
+                    auto isModerator = shared->isModerator(peerId);
+                    auto isHandRaised = shared->isHandRaised(peerId);
+                    auto isModeratorMuted = shared->isMuted(peerId);
+                    auto sinkId = shared->getConfId() + peerId;
+                    newInfo.emplace_back(ParticipantInfo {std::move(uri),
+                                                        deviceId,
+                                                        std::move(sinkId),
+                                                        active,
+                                                        info.x,
+                                                        info.y,
+                                                        info.w,
+                                                        info.h,
+                                                        !info.hasVideo,
+                                                        isLocalMuted,
+                                                        isModeratorMuted,
+                                                        isModerator,
+                                                        isHandRaised});
+                } else {
+                    auto it = shared->videoToCall_.find(info.source);
+                    if (it == shared->videoToCall_.end())
+                        it = shared->videoToCall_.emplace_hint(it, info.source, std::string());
+                    // If not local
+                    if (!it->second.empty()) {
+                        // Retrieve calls participants
+                        // TODO: this is a first version, we assume that the peer is not
+                        // a master of a conference and there is only one remote
+                        // In the future, we should retrieve confInfo from the call
+                        // To merge layouts informations
+                        if (auto call = std::dynamic_pointer_cast<SIPCall>(getCall(it->second))) {
+                            uri = call->getPeerNumber();
+                            isLocalMuted = call->isPeerMuted();
+                            if (auto* transport = call->getTransport())
+                                deviceId = transport->deviceId();
+                        }
+                    }
+                    if (auto videoMixer = shared->videoMixer_)
+                        active = videoMixer->verifyActive(info.source);
+                    std::string_view peerId = string_remove_suffix(uri, '@');
+                    auto isModerator = shared->isModerator(peerId);
+                    if (uri.empty() && !hostAdded) {
+                        hostAdded = true;
+                        peerId = "host"sv;
+                        deviceId = acc->currentDeviceId();
+                        isLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
+                    }
+                    auto isHandRaised = shared->isHandRaised(peerId);
+                    auto isModeratorMuted = shared->isMuted(peerId);
+                    auto sinkId = shared->getConfId() + peerId;
+                    newInfo.emplace_back(ParticipantInfo {std::move(uri),
+                                                        deviceId,
+                                                        std::move(sinkId),
+                                                        active,
+                                                        info.x,
+                                                        info.y,
+                                                        info.w,
+                                                        info.h,
+                                                        !info.hasVideo,
+                                                        isLocalMuted,
+                                                        isModeratorMuted,
+                                                        isModerator,
+                                                        isHandRaised});
                 }
-                auto active = false;
-                if (auto videoMixer = shared->videoMixer_)
-                    active = info.source == videoMixer->getActiveParticipant();
-                std::string_view peerId = string_remove_suffix(uri, '@');
-                auto isModerator = shared->isModerator(peerId);
-                if (uri.empty()) {
-                    hostAdded = true;
-                    peerId = "host"sv;
-                    deviceId = acc->currentDeviceId();
-                    isLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
-                }
-                auto isHandRaised = shared->isHandRaised(peerId);
-                auto isModeratorMuted = shared->isMuted(peerId);
-                auto sinkId = shared->getConfId() + peerId;
-                newInfo.emplace_back(ParticipantInfo {std::move(uri),
-                                                      deviceId,
-                                                      std::move(sinkId),
-                                                      active,
-                                                      info.x,
-                                                      info.y,
-                                                      info.w,
-                                                      info.h,
-                                                      !info.hasVideo,
-                                                      isLocalMuted,
-                                                      isModeratorMuted,
-                                                      isModerator,
-                                                      isHandRaised});
             }
             if (auto videoMixer = shared->videoMixer_) {
                 newInfo.h = videoMixer->getHeight();
@@ -537,12 +566,12 @@ Conference::handleMediaChangeRequest(const std::shared_ptr<Call>& call,
     JAMI_DBG("Conf [%s] Answer to media change request", getConfId().c_str());
 
 #ifdef ENABLE_VIDEO
-    // If the new media list has video, remove existing dummy
-    // video sessions if any.
+    // If the new media list has video, remove the participant from audioonlylist.
     if (MediaAttribute::hasMediaType(MediaAttribute::buildMediaAttributesList(remoteMediaList,
                                                                               false),
                                      MediaType::MEDIA_VIDEO)) {
-        call->removeDummyVideoRtpSessions();
+        if (videoMixer_)
+            videoMixer_->removeAudioOnlySource(call->getCallId());
     }
 #endif
 
@@ -627,13 +656,12 @@ Conference::addParticipant(const std::string& participant_id)
     }
 #ifdef ENABLE_VIDEO
     if (auto call = getCall(participant_id)) {
-        // In conference, all participants need to have video session
-        // (with a sink) in order to display the participant info in
-        // the layout. So, if a participant joins with an audio only
-        // call, a dummy video stream is added to the call.
+        // In conference, if a participant joins with an audio only
+        // call, it must be listed in the audioonlylist.
         auto mediaList = call->getMediaAttributeList();
         if (not MediaAttribute::hasMediaType(mediaList, MediaType::MEDIA_VIDEO)) {
-            call->addDummyVideoRtpSession();
+            if (videoMixer_)
+                videoMixer_->addAudioOnlySource(call->getCallId());
         }
         call->enterConference(shared_from_this());
         // Continue the recording for the conference if one participant was recording
@@ -667,6 +695,8 @@ Conference::setActiveParticipant(const std::string& participant_id)
     if (auto call = getCallFromPeerID(participant_id)) {
         if (auto videoRecv = call->getReceiveVideoFrameActiveWriter())
             videoMixer_->setActiveParticipant(videoRecv.get());
+        else
+            videoMixer_->setActiveParticipant(call->getCallId());
         return;
     }
 
@@ -677,7 +707,7 @@ Conference::setActiveParticipant(const std::string& participant_id)
         return;
     }
     // Unset active participant by default
-    videoMixer_->setActiveParticipant(nullptr);
+    videoMixer_->resetActiveParticipant();
 #endif
 }
 
@@ -689,8 +719,7 @@ Conference::setLayout(int layout)
     case 0:
         videoMixer_->setVideoLayout(video::Layout::GRID);
         // The layout shouldn't have an active participant
-        if (videoMixer_->getActiveParticipant())
-            videoMixer_->setActiveParticipant(nullptr);
+        videoMixer_->resetActiveParticipant();
         break;
     case 1:
         videoMixer_->setVideoLayout(video::Layout::ONE_BIG_WITH_SMALL);
@@ -802,6 +831,8 @@ Conference::removeParticipant(const std::string& participant_id)
             return;
     }
     if (auto call = getCall(participant_id)) {
+        if (videoMixer_->verifyActive(call->getCallId()))
+            videoMixer_->resetActiveParticipant();
         participantsMuted_.erase(std::string(string_remove_suffix(call->getPeerNumber(), '@')));
         handsRaised_.erase(std::string(string_remove_suffix(call->getPeerNumber(), '@')));
 #ifdef ENABLE_VIDEO
diff --git a/src/manager.cpp b/src/manager.cpp
index b07364e482b9a456d7988d0b5ecf68d0c3b34953..4ce503fa2e480b8c1c1410ea9c43283fd58984c2 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -3044,7 +3044,7 @@ Manager::createSinkClients(const std::string& callId,
             sinkId = callId;
             sinkId += string_remove_suffix(participant.uri, '@') + participant.device;
         }
-        if (participant.w && participant.h) {
+        if (participant.w && participant.h && !participant.videoMuted) {
             auto currentSink = getSinkClient(sinkId);
             if (currentSink) {
                 currentSink->setCrop(participant.x, participant.y, participant.w, participant.h);
diff --git a/src/media/rtp_session.h b/src/media/rtp_session.h
index 5eedaef20ce33474e048666b3b4061137fccd842..715b8841b3df95473f5f11edc92c21dc2ff15e6c 100644
--- a/src/media/rtp_session.h
+++ b/src/media/rtp_session.h
@@ -62,8 +62,12 @@ public:
         receive_ = receive;
     }
 
-    bool isSending() const noexcept { return send_.enabled; }
-    bool isReceiving() const noexcept { return receive_.enabled; }
+    bool isReceiving() const noexcept
+    {
+        return receive_.enabled
+               && (receive_.direction_ == MediaDirection::RECVONLY
+                   || receive_.direction_ == MediaDirection::SENDRECV);
+    }
 
     void setMtu(uint16_t mtu) { mtu_ = mtu; }
 
diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp
index f21dd169f001f611adbee4a831b32cd2703556c5..e6506d7ceb8aaf487dffefb50879cbbba1f46d70 100644
--- a/src/media/video/video_mixer.cpp
+++ b/src/media/video/video_mixer.cpp
@@ -179,6 +179,7 @@ VideoMixer::stopInput()
 void
 VideoMixer::setActiveHost()
 {
+    activeAudioOnly_ = "";
     activeSource_ = videoLocalSecondary_ ? videoLocalSecondary_.get() : videoLocal_.get();
     updateLayout();
 }
@@ -186,10 +187,19 @@ VideoMixer::setActiveHost()
 void
 VideoMixer::setActiveParticipant(Observable<std::shared_ptr<MediaFrame>>* ob)
 {
+    activeAudioOnly_ = "";
     activeSource_ = ob;
     updateLayout();
 }
 
+void
+VideoMixer::setActiveParticipant(const std::string& id)
+{
+    activeAudioOnly_ = id;
+    activeSource_ = nullptr;
+    updateLayout();
+}
+
 void
 VideoMixer::updateLayout()
 {
@@ -219,9 +229,7 @@ VideoMixer::detached(Observable<std::shared_ptr<MediaFrame>>* ob)
         if (x->source == ob) {
             // Handle the case where the current shown source leave the conference
             if (activeSource_ == ob) {
-                currentLayout_ = Layout::GRID;
-                activeSource_ = videoLocalSecondary_ ? videoLocalSecondary_.get()
-                                                     : videoLocal_.get();
+                resetActiveParticipant();
             }
             JAMI_DBG("Remove source [%p]", x.get());
             sources_.remove(x);
@@ -283,12 +291,24 @@ VideoMixer::process()
     libav_utils::fillWithBlack(output.pointer());
 
     {
+        std::lock_guard<std::mutex> lk(audioOnlySourcesMtx_);
         auto lock(rwMutex_.read());
 
         int i = 0;
         bool activeFound = false;
         bool needsUpdate = layoutUpdated_ > 0;
         bool successfullyRendered = false;
+        std::vector<SourceInfo> sourcesInfo;
+        sourcesInfo.reserve(sources_.size() + audioOnlySources_.size());
+        // add all audioonlysources
+        for (auto& id : audioOnlySources_) {
+            if (currentLayout_ != Layout::ONE_BIG or activeAudioOnly_ == id) {
+                sourcesInfo.emplace_back(SourceInfo {{}, 0, 0, 10, 10, false, id});
+            }
+            if (currentLayout_ == Layout::ONE_BIG and activeAudioOnly_ == id)
+                successfullyRendered = true;
+        }
+        // add video sources
         for (auto& x : sources_) {
             /* thread stop pending? */
             if (!loop_.isRunning())
@@ -359,11 +379,9 @@ VideoMixer::process()
         if (needsUpdate and successfullyRendered) {
             layoutUpdated_ -= 1;
             if (layoutUpdated_ == 0) {
-                std::vector<SourceInfo> sourcesInfo;
-                sourcesInfo.reserve(sources_.size());
                 for (auto& x : sources_) {
                     sourcesInfo.emplace_back(
-                        SourceInfo {x->source, x->x, x->y, x->w, x->h, x->hasVideo});
+                        SourceInfo {x->source, x->x, x->y, x->w, x->h, x->hasVideo, {}});
                 }
                 if (onSourcesUpdated_)
                     onSourcesUpdated_(std::move(sourcesInfo));
diff --git a/src/media/video/video_mixer.h b/src/media/video/video_mixer.h
index 591f4d09210dce488dfa1dd12127d811f61e811f..4f391b7e76274e6f98370427cf9e949c79b3c3bf 100644
--- a/src/media/video/video_mixer.h
+++ b/src/media/video/video_mixer.h
@@ -45,6 +45,7 @@ struct SourceInfo
     int w;
     int h;
     bool hasVideo;
+    std::string id;
 };
 using OnSourcesUpdatedCb = std::function<void(std::vector<SourceInfo>&&)>;
 
@@ -73,9 +74,23 @@ public:
     void stopInput();
 
     void setActiveParticipant(Observable<std::shared_ptr<MediaFrame>>* ob);
+    void setActiveParticipant(const std::string& id);
+    void resetActiveParticipant() {
+        activeAudioOnly_ = "";
+        activeSource_ = nullptr;
+        updateLayout();
+    }
     void setActiveHost();
 
-    Observable<std::shared_ptr<MediaFrame>>* getActiveParticipant() { return activeSource_; }
+    bool verifyActive(const std::string& id)
+    {
+        return id == activeAudioOnly_;
+    }
+
+    bool verifyActive(Observable<std::shared_ptr<MediaFrame>>* ob)
+    {
+        return ob == activeSource_;
+    }
 
     void setVideoLayout(Layout newLayout)
     {
@@ -93,6 +108,20 @@ public:
 
     std::shared_ptr<SinkClient>& getSink() { return sink_; }
 
+    void addAudioOnlySource(const std::string& id)
+    {
+        std::lock_guard<std::mutex> lk(audioOnlySourcesMtx_);
+        audioOnlySources_.insert(id);
+        updateLayout();
+    }
+
+    void removeAudioOnlySource(const std::string& id)
+    {
+        std::lock_guard<std::mutex> lk(audioOnlySourcesMtx_);
+        audioOnlySources_.erase(id);
+        updateLayout();
+    }
+
 private:
     NON_COPYABLE(VideoMixer);
     struct VideoMixerSource;
@@ -130,6 +159,10 @@ private:
     Observable<std::shared_ptr<MediaFrame>>* activeSource_ {nullptr};
     std::list<std::unique_ptr<VideoMixerSource>> sources_;
 
+    std::mutex audioOnlySourcesMtx_;
+    std::set<std::string> audioOnlySources_;
+    std::string activeAudioOnly_{};
+
     std::atomic_int layoutUpdated_ {0};
     OnSourcesUpdatedCb onSourcesUpdated_ {};
 
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index 86286a045d97681dd2f2ff6de2ce638518ee8b4c..193d2006d1e0a358006f2414cc22797a444dc7dd 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -255,10 +255,23 @@ VideoRtpSession::startReceiver()
         receiveThread_->startLoop();
         receiveThread_->setRequestKeyFrameCallback([this]() { cbKeyFrameRequest_(); });
         receiveThread_->setRotation(rotation_.load());
+        if (videoMixer_) {
+            auto activeParticipant = videoMixer_->verifyActive(receiveThread_.get())
+                                     || videoMixer_->verifyActive(callID_);
+            videoMixer_->removeAudioOnlySource(callID_);
+            if (activeParticipant)
+                videoMixer_->setActiveParticipant(receiveThread_.get());
+        }
+
     } else {
         JAMI_DBG("[%p] Video receiver disabled", this);
         if (receiveThread_ and videoMixer_) {
+            auto activeParticipant = videoMixer_->verifyActive(receiveThread_.get())
+                                     || videoMixer_->verifyActive(callID_);
+            videoMixer_->addAudioOnlySource(callID_);
             receiveThread_->detach(videoMixer_.get());
+            if (activeParticipant)
+                videoMixer_->setActiveParticipant(callID_);
         }
     }
     if (socketPair_)
@@ -276,7 +289,11 @@ VideoRtpSession::stopReceiver()
         return;
 
     if (videoMixer_) {
+        auto activeParticipant = videoMixer_->verifyActive(receiveThread_.get()) || videoMixer_->verifyActive(callID_);
+        videoMixer_->addAudioOnlySource(callID_);
         receiveThread_->detach(videoMixer_.get());
+        if (activeParticipant)
+            videoMixer_->setActiveParticipant(callID_);
     }
 
     // We need to disable the read operation, otherwise the
@@ -463,7 +480,6 @@ VideoRtpSession::setupConferenceVideoPipeline(Conference& conference, Direction
                  conference.getConfId().c_str(),
                  callID_.c_str());
         if (receiveThread_) {
-            conference.detachVideo(dummyVideoReceive_.get());
             receiveThread_->stopSink();
             conference.attachVideo(receiveThread_.get(), callID_);
         } else {
@@ -472,6 +488,14 @@ VideoRtpSession::setupConferenceVideoPipeline(Conference& conference, Direction
     }
 }
 
+std::shared_ptr<VideoFrameActiveWriter>
+VideoRtpSession::getReceiveVideoFrameActiveWriter()
+{
+    if (isReceiving() && receiveThread_ && conference_)
+        return std::static_pointer_cast<VideoFrameActiveWriter>(receiveThread_);
+    return {};
+}
+
 void
 VideoRtpSession::enterConference(Conference& conference)
 {
@@ -521,10 +545,11 @@ VideoRtpSession::exitConference()
             videoMixer_->detach(sender_.get());
 
         if (receiveThread_) {
+            auto activetParticipant = videoMixer_->verifyActive(receiveThread_.get());
             conference_->detachVideo(receiveThread_.get());
             receiveThread_->startSink();
-        } else {
-            conference_->detachVideo(dummyVideoReceive_.get());
+            if (activetParticipant)
+                videoMixer_->setActiveParticipant(callID_);
         }
 
         videoMixer_.reset();
diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h
index 687102b07cdd454183d597b613911aaf0998f5b2..16277f51aa13f61bcfb74ba0564f5a2140fcfc19 100644
--- a/src/media/video/video_rtp_session.h
+++ b/src/media/video/video_rtp_session.h
@@ -108,12 +108,7 @@ public:
 
     std::shared_ptr<VideoReceiveThread>& getVideoReceive() { return receiveThread_; }
 
-    std::shared_ptr<VideoFrameActiveWriter> getReceiveVideoFrameActiveWriter()
-    {
-        if (isReceiving() && receiveThread_)
-            return std::static_pointer_cast<VideoFrameActiveWriter>(receiveThread_);
-        return dummyVideoReceive_;
-    }
+    std::shared_ptr<VideoFrameActiveWriter> getReceiveVideoFrameActiveWriter();
 
 private:
     void setupConferenceVideoPipeline(Conference& conference, Direction dir);
@@ -129,8 +124,6 @@ private:
 
     std::unique_ptr<VideoSender> sender_;
     std::shared_ptr<VideoReceiveThread> receiveThread_;
-    std::shared_ptr<VideoFrameActiveWriter> dummyVideoReceive_
-        = std::make_shared<VideoFrameActiveWriter>();
     Conference* conference_ {nullptr};
     std::shared_ptr<VideoMixer> videoMixer_;
     std::shared_ptr<VideoInput> videoLocal_;
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index a253ed344eb33a14b6375dfc719daba3ec5f0a9c..a12f98f62359c8fa3de36c725f12c1784e34b48d 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -54,6 +54,7 @@
 #include <chrono>
 #include <libavutil/display.h>
 #include <video/sinkclient.h>
+#include "media/video/video_mixer.h"
 #endif
 #include "audio/ringbufferpool.h"
 #include "jamidht/channeled_transport.h"
@@ -91,8 +92,6 @@ static constexpr auto REUSE_ICE_IN_REINVITE_REQUIRED_VERSION_STR = "11.0.2"sv;
 static const std::vector<unsigned> REUSE_ICE_IN_REINVITE_REQUIRED_VERSION
     = split_string_to_unsigned(REUSE_ICE_IN_REINVITE_REQUIRED_VERSION_STR, '.');
 
-constexpr auto DUMMY_VIDEO_STR = "dummy video session";
-
 SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account,
                  const std::string& callId,
                  Call::CallType type,
@@ -1042,6 +1041,11 @@ SIPCall::hangup(int reason)
 
     // Stop all RTP streams
     stopAllMedia();
+    if (auto conf = getConference()) {
+        if (auto mixer = conf->getVideoMixer()) {
+            mixer->removeAudioOnlySource(getCallId());
+        }
+    }
     setState(Call::ConnectionState::DISCONNECTED, reason);
     dht::ThreadPool::io().run([w = weak()] {
         if (auto shared = w.lock())
@@ -1397,6 +1401,11 @@ SIPCall::peerHungup()
 
     if (inviteSession_)
         terminateSipSession(PJSIP_SC_NOT_FOUND);
+    if (auto conf = getConference()) {
+        if (auto mixer = conf->getVideoMixer()) {
+            mixer->removeAudioOnlySource(getCallId());
+        }
+    }
 
     Call::peerHungup();
 }
@@ -2046,20 +2055,12 @@ SIPCall::startAllMedia()
     // reset
     readyToRecord_ = false;
     resetMediaReady();
-#ifdef ENABLE_VIDEO
-    bool hasActiveVideo = false;
-#endif
 
     for (auto iter = rtpStreams_.begin(); iter != rtpStreams_.end(); iter++) {
         if (not iter->mediaAttribute_) {
             throw std::runtime_error("Missing media attribute");
         }
 
-#ifdef ENABLE_VIDEO
-        if (iter->mediaAttribute_->type_ == MEDIA_VIDEO)
-            hasActiveVideo |= iter->mediaAttribute_->enabled_;
-#endif
-
         // Not restarting media loop on hold as it's a huge waste of CPU ressources
         // because of the audio loop
         if (getState() != CallState::HOLD) {
@@ -2071,16 +2072,6 @@ SIPCall::startAllMedia()
         }
     }
 
-#ifdef ENABLE_VIDEO
-    // TODO. Move this elsewhere (when adding participant to conf?)
-    if (not hasActiveVideo) {
-        if (auto conference = conf_.lock())
-            if (conference->isVideoEnabled())
-                if (auto recv = getReceiveVideoFrameActiveWriter())
-                    conference->attachVideo(recv.get(), getCallId());
-    }
-#endif
-
     // Media is restarted, we can process the last holding request.
     isWaitingForIceAndMedia_ = false;
     if (remainingRequest_ != Request::NoRequest) {
@@ -2900,15 +2891,8 @@ SIPCall::enterConference(std::shared_ptr<Conference> conference)
 #ifdef ENABLE_VIDEO
     if (conference->isVideoEnabled()) {
         auto videoRtp = getVideoRtp();
-        if (not videoRtp) {
-            // In conference, we need to have a video RTP session even
-            // if it's an audio only call
-            if (not addDummyVideoRtpSession()) {
-                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(*conference);
+        if (videoRtp)
+            videoRtp->enterConference(*conference);
     }
 #endif
 
@@ -2960,47 +2944,6 @@ SIPCall::getVideoRtp() const
     return {};
 }
 
-bool
-SIPCall::addDummyVideoRtpSession()
-{
-    JAMI_DBG("[call:%s] Add dummy video stream", getCallId().c_str());
-
-    MediaAttribute mediaAttr(MediaType::MEDIA_VIDEO,
-                             true,
-                             true,
-                             false,
-                             "dummy source",
-                             DUMMY_VIDEO_STR);
-
-    addMediaStream(mediaAttr);
-    auto& stream = rtpStreams_.back();
-    createRtpSession(stream);
-    return stream.rtpSession_ != nullptr;
-}
-
-void
-SIPCall::removeDummyVideoRtpSessions()
-{
-    // It's not expected to have more than one dummy video stream, but
-    // check just in case.
-    auto removed = std::remove_if(rtpStreams_.begin(),
-                                  rtpStreams_.end(),
-                                  [](const RtpStream& stream) {
-                                      return stream.mediaAttribute_->label_ == DUMMY_VIDEO_STR;
-                                  });
-    auto count = std::distance(removed, rtpStreams_.end());
-    rtpStreams_.erase(removed, rtpStreams_.end());
-
-    if (count > 0) {
-        JAMI_DBG("[call:%s] Removed %lu dummy video stream(s)", getCallId().c_str(), count);
-        if (count > 1) {
-            JAMI_WARN("[call:%s] Expected to find 1 dummy video stream, found %lu",
-                      getCallId().c_str(),
-                      count);
-        }
-    }
-}
-
 void
 SIPCall::setRotation(int rotation)
 {
@@ -3017,6 +2960,8 @@ SIPCall::createSinks(const ConfInfo& infos)
 
     std::lock_guard<std::mutex> lk(sinksMtx_);
     auto videoRtp = getVideoRtp();
+    if (!videoRtp)
+        return;
     auto& videoReceive = videoRtp->getVideoReceive();
     if (!videoReceive)
         return;
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 4ccb5427f1ef4ee35253b7f4d7d850f7226cdada..7ec0f2bb23de233e86f95f7c571d0d0a7eb9bcca 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -271,8 +271,6 @@ public:
      * Returns a pointer to the VideoRtp object
      */
     std::shared_ptr<video::VideoRtpSession> getVideoRtp() const;
-    bool addDummyVideoRtpSession() override;
-    void removeDummyVideoRtpSessions() override;
     void setRotation(int rotation);
 #endif
     // Get the list of current RTP sessions
diff --git a/test/unitTest/call/conference.cpp b/test/unitTest/call/conference.cpp
index 4450604ee11b8ea4da06ca7986d35ed862e8a93c..447a90b27a1db1d11b43dc3bf066ecbc6842717e 100644
--- a/test/unitTest/call/conference.cpp
+++ b/test/unitTest/call/conference.cpp
@@ -46,6 +46,7 @@ struct CallData
     std::string hostState {};
     std::atomic_bool moderatorMuted {false};
     std::atomic_bool raisedHand {false};
+    std::atomic_bool active {false};
 
     void reset()
     {
@@ -54,6 +55,7 @@ struct CallData
         device = "";
         hostState = "";
         moderatorMuted = false;
+        active = false;
         raisedHand = false;
     }
 };
@@ -79,6 +81,7 @@ private:
     void testAudioVideoMutedStates();
     void testCreateParticipantsSinks();
     void testMuteStatusAfterRemove();
+    void testActiveStatusAfterRemove();
     void testHandsUp();
     void testPeerLeaveConference();
     void testJoinCallFromOtherAccount();
@@ -90,6 +93,7 @@ private:
     CPPUNIT_TEST(testAudioVideoMutedStates);
     CPPUNIT_TEST(testCreateParticipantsSinks);
     CPPUNIT_TEST(testMuteStatusAfterRemove);
+    CPPUNIT_TEST(testActiveStatusAfterRemove);
     CPPUNIT_TEST(testHandsUp);
     CPPUNIT_TEST(testPeerLeaveConference);
     CPPUNIT_TEST(testJoinCallFromOtherAccount);
@@ -212,14 +216,17 @@ ConferenceTest::registerSignalHandlers()
             const std::vector<std::map<std::string, std::string>> participantsInfos) {
             for (const auto& infos : participantsInfos) {
                 if (infos.at("uri").find(bobUri) != std::string::npos) {
+                    bobCall.active = infos.at("active") == "true";
                     bobCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
                     bobCall.raisedHand = infos.at("handRaised") == "true";
                     bobCall.device = infos.at("device");
                 } else if (infos.at("uri").find(carlaUri) != std::string::npos) {
+                    carlaCall.active = infos.at("active") == "true";
                     carlaCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
                     carlaCall.raisedHand = infos.at("handRaised") == "true";
                     carlaCall.device = infos.at("device");
                 } else if (infos.at("uri").find(daviUri) != std::string::npos) {
+                    daviCall.active = infos.at("active") == "true";
                     daviCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
                     daviCall.raisedHand = infos.at("handRaised") == "true";
                     daviCall.device = infos.at("device");
@@ -451,6 +458,64 @@ ConferenceTest::testMuteStatusAfterRemove()
     DRing::unregisterSignalHandlers();
 }
 
+
+void
+ConferenceTest::testActiveStatusAfterRemove()
+{
+    registerSignalHandlers();
+
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobUri = bobAccount->getUsername();
+    auto daviAccount = Manager::instance().getAccount<JamiAccount>(daviId);
+    auto daviUri = daviAccount->getUsername();
+
+    startConference();
+
+    MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
+    defaultAudio.label_ = "audio_0";
+    defaultAudio.enabled_ = true;
+
+    JAMI_INFO("Start call between Alice and Davi");
+    auto call1 = DRing::placeCallWithMedia(aliceId,
+                                           daviUri,
+                                           MediaAttribute::mediaAttributesToMediaMaps(
+                                                       {defaultAudio}));
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return !daviCall.callId.empty(); }));
+    Manager::instance().answerCall(daviId, daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.hostState == "CURRENT"; }));
+    Manager::instance().addParticipant(aliceId, call1, aliceId, confId);
+
+    DRing::setActiveParticipant(aliceId, confId, daviUri);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return daviCall.active.load(); }));
+
+    Manager::instance().hangupCall(daviId, daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "OVER"; }));
+    daviCall.reset();
+
+    auto call2 = DRing::placeCallWithMedia(aliceId, daviUri, MediaAttribute::mediaAttributesToMediaMaps({defaultAudio}));
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return !daviCall.callId.empty(); }));
+    Manager::instance().answerCall(daviId, daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.hostState == "CURRENT"; }));
+    Manager::instance().addParticipant(aliceId, call2, aliceId, confId);
+
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(5), [&] { return !daviCall.active.load(); }));
+
+    Manager::instance().hangupCall(daviId, daviCall.callId);
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, std::chrono::seconds(20), [&] { return daviCall.state == "OVER"; }));
+    hangupConference();
+
+    DRing::unregisterSignalHandlers();
+}
+
 void
 ConferenceTest::testHandsUp()
 {