diff --git a/src/conference.cpp b/src/conference.cpp
index 92cecdfe561f16208e563d5876f649f656b6b6e5..09bf7734a8a2f018408f7b255547e5e536d2dcbd 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -35,6 +35,10 @@
 #include "video/video_mixer.h"
 #endif
 
+#ifdef ENABLE_PLUGIN
+#include "plugin/jamipluginmanager.h"
+#endif
+
 #include "call_factory.h"
 
 #include "logger.h"
@@ -117,8 +121,8 @@ Conference::Conference()
             }
             // Add host in confInfo with audio and video muted if detached
             if (shared->getState() == State::ACTIVE_DETACHED)
-                newInfo.emplace_back(ParticipantInfo {
-                    "", "", false, 0, 0, 0, 0, true, true, false, true});
+                newInfo.emplace_back(
+                    ParticipantInfo {"", "", false, 0, 0, 0, 0, true, true, false, true});
 
             shared->updateConferenceInfo(std::move(newInfo));
         });
@@ -164,6 +168,18 @@ Conference::~Conference()
         }
     }
 #endif // ENABLE_VIDEO
+#ifdef ENABLE_PLUGIN
+    {
+        std::lock_guard<std::mutex> lk(avStreamsMtx_);
+        jami::Manager::instance()
+            .getJamiPluginManager()
+            .getCallServicesManager()
+            .clearCallHandlerMaps(getConfID());
+        Manager::instance().getJamiPluginManager().getCallServicesManager().clearAVSubject(
+            getConfID());
+        confAVStreams.clear();
+    }
+#endif // ENABLE_PLUGIN
 }
 
 Conference::State
@@ -178,6 +194,64 @@ Conference::setState(State state)
     confState_ = state;
 }
 
+#ifdef ENABLE_PLUGIN
+void
+Conference::createConfAVStreams()
+{
+    auto audioMap = [](const std::shared_ptr<jami::MediaFrame>& m) -> AVFrame* {
+        return std::static_pointer_cast<AudioFrame>(m)->pointer();
+    };
+
+    // Preview and Received
+    if ((audioMixer_ = jami::getAudioInput(getConfID()))) {
+        auto audioSubject = std::make_shared<MediaStreamSubject>(audioMap);
+        StreamData previewStreamData {getConfID(), false, StreamType::audio, getConfID()};
+        createConfAVStream(previewStreamData, *audioMixer_, audioSubject);
+        StreamData receivedStreamData {getConfID(), true, StreamType::audio, getConfID()};
+        createConfAVStream(receivedStreamData, *audioMixer_, audioSubject);
+    }
+
+#ifdef ENABLE_VIDEO
+
+    if (videoMixer_) {
+        // Review
+        auto receiveSubject = std::make_shared<MediaStreamSubject>(pluginVideoMap_);
+        StreamData receiveStreamData {getConfID(), true, StreamType::video, getConfID()};
+        createConfAVStream(receiveStreamData, *videoMixer_, receiveSubject);
+
+        // Preview
+        if (auto& videoPreview = videoMixer_->getVideoLocal()) {
+            auto previewSubject = std::make_shared<MediaStreamSubject>(pluginVideoMap_);
+            StreamData previewStreamData {getConfID(), false, StreamType::video, getConfID()};
+            createConfAVStream(previewStreamData, *videoPreview, previewSubject);
+        }
+    }
+#endif // ENABLE_VIDEO
+}
+
+void
+Conference::createConfAVStream(const StreamData& StreamData,
+                               AVMediaStream& streamSource,
+                               const std::shared_ptr<MediaStreamSubject>& mediaStreamSubject,
+                               bool force)
+{
+    std::lock_guard<std::mutex> lk(avStreamsMtx_);
+    const std::string AVStreamId = StreamData.id + std::to_string(static_cast<int>(StreamData.type))
+                                   + std::to_string(StreamData.direction);
+    auto it = confAVStreams.find(AVStreamId);
+    if (!force && it != confAVStreams.end())
+        return;
+
+    confAVStreams.erase(AVStreamId);
+    confAVStreams[AVStreamId] = mediaStreamSubject;
+    streamSource.attachPriorityObserver(mediaStreamSubject);
+    jami::Manager::instance()
+        .getJamiPluginManager()
+        .getCallServicesManager()
+        .createAVSubject(StreamData, mediaStreamSubject);
+}
+#endif // ENABLE_PLUGIN
+
 void
 Conference::add(const std::string& participant_id)
 {
@@ -228,6 +302,9 @@ Conference::add(const std::string& participant_id)
         } else
             JAMI_ERR("no call associate to participant %s", participant_id.c_str());
 #endif // ENABLE_VIDEO
+#ifdef ENABLE_PLUGIN
+        createConfAVStreams();
+#endif
     }
 }
 
@@ -294,7 +371,7 @@ ConfInfo::toString() const
     for (const auto& info : *this) {
         jsonArray.append(info.toJson());
     }
-    return Json::writeString(Json::StreamWriterBuilder{}, jsonArray);
+    return Json::writeString(Json::StreamWriterBuilder {}, jsonArray);
 }
 
 void
@@ -310,19 +387,20 @@ Conference::sendConferenceInfos()
             if (!account)
                 continue;
 
-            dht::ThreadPool::io().run([
-                call,
-                confInfo = getConfInfoHostUri(account->getUsername()+ "@ring.dht",
-                                                call->getPeerNumber()),
-                from = account->getFromUri()
-            ] {
+            dht::ThreadPool::io().run([call,
+                                       confInfo = getConfInfoHostUri(account->getUsername()
+                                                                         + "@ring.dht",
+                                                                     call->getPeerNumber()),
+                                       from = account->getFromUri()] {
                 call->sendTextMessage({{"application/confInfo+json", confInfo.toString()}}, from);
             });
         }
     }
 
     // Inform client that layout has changed
-    jami::emitSignal<DRing::CallSignal::OnConferenceInfosUpdated>(id_, getConfInfoHostUri("", "").toVectorMapStringString());
+    jami::emitSignal<DRing::CallSignal::OnConferenceInfosUpdated>(id_,
+                                                                  getConfInfoHostUri("", "")
+                                                                      .toVectorMapStringString());
 }
 
 void
@@ -395,7 +473,8 @@ Conference::detach()
 
     if (getState() == State::ACTIVE_ATTACHED) {
         for (const auto& p : participants_) {
-            Manager::instance().getRingBufferPool().unBindCallID(getCall(p)->getCallId(), RingBufferPool::DEFAULT_ID);
+            Manager::instance().getRingBufferPool().unBindCallID(getCall(p)->getCallId(),
+                                                                 RingBufferPool::DEFAULT_ID);
         }
 #ifdef ENABLE_VIDEO
         if (auto mixer = getVideoMixer()) {
@@ -513,7 +592,17 @@ Conference::switchInput(const std::string& input)
 {
 #ifdef ENABLE_VIDEO
     mediaInput_ = input;
-    getVideoMixer()->switchInput(input);
+    if (auto mixer = getVideoMixer()) {
+        mixer->switchInput(input);
+#ifdef ENABLE_PLUGIN
+        // Preview
+        if (auto& videoPreview = mixer->getVideoLocal()) {
+            auto previewSubject = std::make_shared<MediaStreamSubject>(pluginVideoMap_);
+            StreamData previewStreamData {getConfID(), false, StreamType::video, getConfID()};
+            createConfAVStream(previewStreamData, *videoPreview, previewSubject, true);
+        }
+#endif
+    }
 #endif
 }
 
@@ -845,6 +934,17 @@ Conference::muteLocalHost(bool is_muted, const std::string& mediaType)
         } else {
             if (auto mixer = getVideoMixer()) {
                 mixer->switchInput(mediaInput_);
+#ifdef ENABLE_PLUGIN
+                // Preview
+                if (auto& videoPreview = mixer->getVideoLocal()) {
+                    auto previewSubject = std::make_shared<MediaStreamSubject>(pluginVideoMap_);
+                    StreamData previewStreamData {getConfID(),
+                                                  false,
+                                                  StreamType::video,
+                                                  getConfID()};
+                    createConfAVStream(previewStreamData, *videoPreview, previewSubject, true);
+                }
+#endif
             }
         }
         videoMuted_ = is_muted;
@@ -892,7 +992,6 @@ Conference::resizeRemoteParticipant(const std::string& peerURI, ParticipantInfo&
     remoteCell.y = remoteCell.y / zoomY + localCell.y;
     remoteCell.w = remoteCell.w / zoomX;
     remoteCell.h = remoteCell.h / zoomY;
-
 }
 
 std::string
@@ -902,7 +1001,7 @@ Conference::confInfo2str(const ConfInfo& confInfo)
     for (const auto& info : confInfo) {
         jsonArray.append(info.toJson());
     }
-    return Json::writeString(Json::StreamWriterBuilder{}, jsonArray);
+    return Json::writeString(Json::StreamWriterBuilder {}, jsonArray);
 }
 
 void
diff --git a/src/conference.h b/src/conference.h
index 76dc6ec0ea086a8ee100e088ab9920c51a7e18b6..928485c63e3d956320b109debce294f4b0440030 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -30,6 +30,7 @@
 #include <vector>
 #include <string_view>
 #include <map>
+#include <functional>
 
 #include "audio/audio_input.h"
 
@@ -37,6 +38,10 @@
 
 #include "recordable.h"
 
+#ifdef ENABLE_PLUGIN
+#include "plugin/streamdata.h"
+#endif
+
 namespace jami {
 
 class Call;
@@ -110,18 +115,11 @@ struct ParticipantInfo
 
     friend bool operator==(const ParticipantInfo& p1, const ParticipantInfo& p2)
     {
-        return
-            p1.uri == p2.uri
-            and p1.device == p2.device
-            and p1.active == p2.active
-            and p1.x == p2.x
-            and p1.y == p2.y
-            and p1.w == p2.w
-            and p1.h == p2.h
-            and p1.videoMuted == p2.videoMuted
-            and p1.audioLocalMuted == p2.audioLocalMuted
-            and p1.audioModeratorMuted == p2.audioModeratorMuted
-            and p1.isModerator == p2.isModerator;
+        return p1.uri == p2.uri and p1.device == p2.device and p1.active == p2.active
+               and p1.x == p2.x and p1.y == p2.y and p1.w == p2.w and p1.h == p2.h
+               and p1.videoMuted == p2.videoMuted and p1.audioLocalMuted == p2.audioLocalMuted
+               and p1.audioModeratorMuted == p2.audioModeratorMuted
+               and p1.isModerator == p2.isModerator;
     }
 
     friend bool operator!=(const ParticipantInfo& p1, const ParticipantInfo& p2)
@@ -138,8 +136,9 @@ struct ConfInfo : public std::vector<ParticipantInfo>
     friend bool operator==(const ConfInfo& c1, const ConfInfo& c2)
     {
         for (auto& p1 : c1) {
-            auto it = std::find_if(c2.begin(), c2.end(),
-                [p1] (const ParticipantInfo& p2) { return p1 == p2; });
+            auto it = std::find_if(c2.begin(), c2.end(), [p1](const ParticipantInfo& p2) {
+                return p1 == p2;
+            });
             if (it != c2.end())
                 continue;
             else
@@ -148,14 +147,10 @@ struct ConfInfo : public std::vector<ParticipantInfo>
         return true;
     }
 
-    friend bool operator!=(const ConfInfo& c1, const ConfInfo& c2)
-    {
-        return !(c1 == c2);
-    }
+    friend bool operator!=(const ConfInfo& c1, const ConfInfo& c2) { return !(c1 == c2); }
 
     std::vector<std::map<std::string, std::string>> toVectorMapStringString() const;
     std::string toString() const;
-
 };
 
 using ParticipantSet = std::set<std::string>;
@@ -345,6 +340,44 @@ private:
     std::map<std::string, ConfInfo> remoteHosts_;
     std::string confInfo2str(const ConfInfo& confInfo);
     std::string_view findHostforRemoteParticipant(std::string_view uri);
+
+#ifdef ENABLE_PLUGIN
+    /**
+     * Call Streams and some typedefs
+     */
+    using AVMediaStream = Observable<std::shared_ptr<MediaFrame>>;
+    using MediaStreamSubject = PublishMapSubject<std::shared_ptr<MediaFrame>, AVFrame*>;
+
+#ifdef ENABLE_VIDEO
+    /**
+     *   Map: maps the VideoFrame to an AVFrame
+     **/
+    std::function<AVFrame*(const std::shared_ptr<jami::MediaFrame>&)> pluginVideoMap_ =
+        [](const std::shared_ptr<jami::MediaFrame>& m) -> AVFrame* {
+        return std::static_pointer_cast<VideoFrame>(m)->pointer();
+    };
+#endif // ENABLE_VIDEO
+
+    /**
+     * @brief createConfAVStream
+     * Creates a conf AV stream like video input, video receive, audio input or audio receive
+     * @param StreamData
+     * @param streamSource
+     * @param mediaStreamSubject
+     */
+    void createConfAVStream(const StreamData& StreamData,
+                            AVMediaStream& streamSource,
+                            const std::shared_ptr<MediaStreamSubject>& mediaStreamSubject,
+                            bool force = false);
+    /**
+     * @brief createConfAVStreams
+     * Creates all Conf AV Streams (2 if audio, 4 if audio video)
+     */
+    void createConfAVStreams();
+
+    std::mutex avStreamsMtx_ {};
+    std::map<std::string, std::shared_ptr<MediaStreamSubject>> confAVStreams;
+#endif // ENABLE_PLUGIN
 };
 
 } // namespace jami
diff --git a/src/media/video/video_mixer.h b/src/media/video/video_mixer.h
index c55b9225bcee9dc386f790813522ed2c9663081d..c1470c2af44801a071fafaf193c3f8f98a488a3c 100644
--- a/src/media/video/video_mixer.h
+++ b/src/media/video/video_mixer.h
@@ -85,7 +85,9 @@ public:
 
     void setOnSourcesUpdated(OnSourcesUpdatedCb&& cb) { onSourcesUpdated_ = std::move(cb); }
 
-    MediaStream getStream(const std::string& name)  const;
+    MediaStream getStream(const std::string& name) const;
+
+    std::shared_ptr<VideoFrameActiveWriter>& getVideoLocal() { return videoLocal_; }
 
 private:
     NON_COPYABLE(VideoMixer);
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index cb0e6c5ca939df950fc2691ec56305a2c2cb11c9..2dce08b767f43f07c78812705fad6fa150073fde 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -29,6 +29,10 @@
 #include "socket_pair.h"
 #include "sip/sipvoiplink.h" // for enqueueKeyframeRequest
 #include "manager.h"
+#ifdef ENABLE_PLUGIN
+#include "plugin/streamdata.h"
+#include "plugin/jamipluginmanager.h"
+#endif
 #include "logger.h"
 #include "string_utils.h"
 #include "call.h"
@@ -104,7 +108,8 @@ VideoRtpSession::startSender()
         if (not conference_) {
             videoLocal_ = getVideoCamera();
             if (auto input = Manager::instance().getVideoManager().videoInput.lock()) {
-                std::static_pointer_cast<VideoInput>(videoLocal_)->setSuccessfulSetupCb(onSuccessfulSetup_);
+                std::static_pointer_cast<VideoInput>(videoLocal_)
+                    ->setSuccessfulSetupCb(onSuccessfulSetup_);
                 auto newParams = input->switchInput(input_);
                 try {
                     if (newParams.valid()
@@ -146,21 +151,18 @@ VideoRtpSession::startSender()
         try {
             sender_.reset();
             socketPair_->stopSendOp(false);
-            MediaStream ms = !conference_ ?
-                        MediaStream("video sender",
-                            AV_PIX_FMT_YUV420P,
-                            1 / static_cast<rational<int>>(localVideoParams_.framerate),
-                            localVideoParams_.width,
-                            localVideoParams_.height,
-                            send_.bitrate,
-                            static_cast<rational<int>>(localVideoParams_.framerate)) :
-                        conference_->getVideoMixer()->getStream("Video Sender");
-            sender_.reset(new VideoSender(getRemoteRtpUri(),
-                                        ms,
-                                        send_,
-                                        *socketPair_,
-                                        initSeqVal_ + 1,
-                                        mtu_));
+            MediaStream ms
+                = !conference_
+                      ? MediaStream("video sender",
+                                    AV_PIX_FMT_YUV420P,
+                                    1 / static_cast<rational<int>>(localVideoParams_.framerate),
+                                    localVideoParams_.width,
+                                    localVideoParams_.height,
+                                    send_.bitrate,
+                                    static_cast<rational<int>>(localVideoParams_.framerate))
+                      : conference_->getVideoMixer()->getStream("Video Sender");
+            sender_.reset(
+                new VideoSender(getRemoteRtpUri(), ms, send_, *socketPair_, initSeqVal_ + 1, mtu_));
             if (changeOrientationCallback_)
                 sender_->setChangeOrientationCallback(changeOrientationCallback_);
             if (socketPair_)
@@ -351,15 +353,15 @@ VideoRtpSession::enterConference(Conference* conference)
     // TODO is this correct? The video Mixer should be enabled for a detached conference even if we
     // are not sending values
     videoMixer_ = conference->getVideoMixer();
-    auto conf_res = split_string_to_unsigned(jami::Manager::instance().videoPreferences.getConferenceResolution(), 'x');
+    auto conf_res = split_string_to_unsigned(jami::Manager::instance()
+                                                 .videoPreferences.getConferenceResolution(),
+                                             'x');
     if (conf_res.size() != 2 or conf_res[0] <= 0 or conf_res[1] <= 0) {
         JAMI_ERR("Conference resolution is invalid");
         return;
     }
 #if defined(__APPLE__) && TARGET_OS_MAC
-    videoMixer_->setParameters(conf_res[0],
-                               conf_res[1],
-                               AV_PIX_FMT_NV12);
+    videoMixer_->setParameters(conf_res[0], conf_res[1], AV_PIX_FMT_NV12);
 #else
     videoMixer_->setParameters(conf_res[0], conf_res[1]);
 #endif
@@ -760,6 +762,5 @@ VideoRtpSession::delayMonitor(int gradient, int deltaT)
         }
     }
 }
-
 } // namespace video
 } // namespace jami
diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h
index d4894a2b98944be633492015a1294b367efb5510..0243a0927c8ea070e305410ea3adf4c1dc38b06f 100644
--- a/src/media/video/video_rtp_session.h
+++ b/src/media/video/video_rtp_session.h
@@ -98,8 +98,12 @@ public:
     void initRecorder(std::shared_ptr<MediaRecorder>& rec) override;
     void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) override;
 
+    bool hasConference() { return conference_; }
+
     std::shared_ptr<VideoFrameActiveWriter>& getVideoLocal() { return videoLocal_; }
 
+    std::shared_ptr<VideoMixer>& getVideoMixer() { return videoMixer_; }
+
     std::unique_ptr<VideoReceiveThread>& getVideoReceive() { return receiveThread_; }
 
 private:
diff --git a/src/observer.h b/src/observer.h
index a0daaa3bf6c0f829d30e6617b292bf860ec2cdd2..b6f2ba7c0d54eba95780621b786abfb5f106e8af 100644
--- a/src/observer.h
+++ b/src/observer.h
@@ -90,10 +90,12 @@ public:
     void detachPriorityObserver(Observer<T>* o)
     {
         std::lock_guard<std::mutex> lk(mutex_);
-        for (auto it = priority_observers_.begin(); it != priority_observers_.end(); ++it) {
+        for (auto it = priority_observers_.begin(); it != priority_observers_.end(); it++) {
             if (auto so = it->lock()) {
                 if (so.get() == o) {
+                    so->detached(this);
                     priority_observers_.erase(it);
+                    return;
                 }
             }
         }
@@ -121,9 +123,9 @@ protected:
         std::lock_guard<std::mutex> lk(mutex_);
         for (auto it = priority_observers_.begin(); it != priority_observers_.end();) {
             if (auto so = it->lock()) {
+                it++;
                 try {
                     so->update(this, data);
-                    ++it;
                 } catch (std::exception& e) {
                     JAMI_ERR() << e.what();
                 }
@@ -192,13 +194,7 @@ public:
         : map_ {f}
     {}
 
-    void update(Observable<T1>*, const T1& t) override
-    {
-        std::lock_guard<std::mutex> lk(this->mutex_);
-        for (const auto& observer : this->observers_) {
-            observer->update(this, map_(t));
-        }
-    }
+    void update(Observable<T1>*, const T1& t) override { this->notify(map_(t)); }
 
     /**
      * @brief attached
@@ -222,7 +218,11 @@ public:
     virtual void detached(Observable<T1>*) override
     {
         std::lock_guard<std::mutex> lk(this->mutex_);
-        JAMI_WARN() << "PublishMapSubject: detaching observers";
+        for (auto& pobs : this->priority_observers_) {
+            if (auto so = pobs.lock()) {
+                so->detached(this);
+            }
+        }
         for (auto& o : this->observers_)
             o->detached(this);
     }
@@ -232,11 +232,7 @@ public:
      * Detach all observers to avoid making them call this observable when
      * destroyed
      **/
-    ~PublishMapSubject()
-    {
-        JAMI_WARN() << "~PublishMapSubject()";
-        detached(nullptr);
-    }
+    ~PublishMapSubject() { detached(nullptr); }
 
 private:
     F map_;
diff --git a/src/plugin/callservicesmanager.cpp b/src/plugin/callservicesmanager.cpp
index 00f4f755f85a4a6763c9020f0d04eaaede657317..29a3424a60ab34a1c4f7ff57ac383da43502e5e4 100644
--- a/src/plugin/callservicesmanager.cpp
+++ b/src/plugin/callservicesmanager.cpp
@@ -45,6 +45,12 @@ CallServicesManager::~CallServicesManager()
 void
 CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
 {
+    auto predicate = [&data](std::pair<const StreamData, AVSubjectSPtr> item) {
+        return data.id == item.first.id && data.direction == item.first.direction
+               && data.type == item.first.type;
+    };
+    callAVsubjects_[data.id].remove_if(predicate);
+
     // callAVsubjects_ emplaces data and subject with callId key to easy of access
     // When call is ended, subjects from this call are erased.
     callAVsubjects_[data.id].emplace_back(data, subject);
@@ -87,7 +93,8 @@ void
 CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
 {
     // registerMediaHandler may be called by the PluginManager upon loading a plugin.
-    auto registerMediaHandler = [this](void* data) {
+    auto registerMediaHandler = [this](void* data, std::mutex& pmMtx_) {
+        std::lock_guard<std::mutex> lk(pmMtx_);
         CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
 
         if (!ptr)
@@ -102,7 +109,8 @@ CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginMa
     };
 
     // unregisterMediaHandler may be called by the PluginManager while unloading.
-    auto unregisterMediaHandler = [this](void* data) {
+    auto unregisterMediaHandler = [this](void* data, std::mutex& pmMtx_) {
+        std::lock_guard<std::mutex> lk(pmMtx_);
         auto handlerIt = std::find_if(callMediaHandlers_.begin(),
                                       callMediaHandlers_.end(),
                                       [data](CallMediaHandlerPtr& handler) {
@@ -273,9 +281,9 @@ CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId,
     }
 #ifndef __ANDROID__
     if (applyRestart) {
-        if (auto call = Manager::instance().callFactory.getCall<SIPCall>(callId)) {
+        auto call = Manager::instance().callFactory.getCall<SIPCall>(callId);
+        if (call && call->getConfId().empty())
             call->getVideoRtp().restartSender();
-        }
     }
 #endif
 }
diff --git a/src/plugin/chatservicesmanager.cpp b/src/plugin/chatservicesmanager.cpp
index 96e4581dddd7a421ae367c82077243f5abf2c96c..e84b0a6b39fe330041d6761ae7bf9e40dc17be88 100644
--- a/src/plugin/chatservicesmanager.cpp
+++ b/src/plugin/chatservicesmanager.cpp
@@ -35,7 +35,8 @@ void
 ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
 {
     // registerChatHandler may be called by the PluginManager upon loading a plugin.
-    auto registerChatHandler = [this](void* data) {
+    auto registerChatHandler = [this](void* data, std::mutex& pmMtx_) {
+        std::lock_guard<std::mutex> lk(pmMtx_);
         ChatHandlerPtr ptr {(static_cast<ChatHandler*>(data))};
 
         if (!ptr)
@@ -50,7 +51,8 @@ ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginMa
     };
 
     // unregisterChatHandler may be called by the PluginManager while unloading.
-    auto unregisterChatHandler = [this](void* data) {
+    auto unregisterChatHandler = [this](void* data, std::mutex& pmMtx_) {
+        std::lock_guard<std::mutex> lk(pmMtx_);
         auto handlerIt = std::find_if(chatHandlers_.begin(),
                                       chatHandlers_.end(),
                                       [data](ChatHandlerPtr& handler) {
diff --git a/src/plugin/pluginmanager.cpp b/src/plugin/pluginmanager.cpp
index ae510607231096725761ad82919fa43f0c834c64..eb99d226356fbec5b3b3398373d7087226bdcf59 100644
--- a/src/plugin/pluginmanager.cpp
+++ b/src/plugin/pluginmanager.cpp
@@ -33,9 +33,9 @@ PluginManager::PluginManager()
 
 PluginManager::~PluginManager()
 {
-    for (auto func : exitFuncVec_) {
+    for (auto& func : exitFunc_) {
         try {
-            (*func)();
+            func.second();
         } catch (...) {
             JAMI_ERR() << "Exception caught during plugin exit";
         }
@@ -44,7 +44,7 @@ PluginManager::~PluginManager()
     dynPluginMap_.clear();
     exactMatchMap_.clear();
     wildCardVec_.clear();
-    exitFuncVec_.clear();
+    exitFunc_.clear();
 }
 
 bool
@@ -85,7 +85,9 @@ PluginManager::unload(const std::string& path)
     destroyPluginComponents(path);
     auto it = dynPluginMap_.find(path);
     if (it != dynPluginMap_.end()) {
-        it->second.second = false;
+        std::lock_guard<std::mutex> lk(mtx_);
+        exitFunc_[path]();
+        dynPluginMap_.erase(it);
     }
 
     return true;
@@ -120,7 +122,7 @@ PluginManager::destroyPluginComponents(const std::string& path)
         for (auto pairIt = itComponents->second.begin(); pairIt != itComponents->second.end();) {
             auto clcm = componentsLifeCycleManagers_.find(pairIt->first);
             if (clcm != componentsLifeCycleManagers_.end()) {
-                clcm->second.destroyComponent(pairIt->second);
+                clcm->second.destroyComponent(pairIt->second, mtx_);
                 pairIt = itComponents->second.erase(pairIt);
             }
         }
@@ -209,7 +211,7 @@ PluginManager::registerPlugin(std::unique_ptr<Plugin>& plugin)
         return false;
     }
 
-    exitFuncVec_.push_back(exitFunc);
+    exitFunc_[pluginPtr->getPath()] = exitFunc;
     return true;
 }
 
@@ -259,7 +261,7 @@ PluginManager::manageComponent(const DLPlugin* plugin, const std::string& name,
     const auto& componentLifecycleManager = iter->second;
 
     try {
-        int32_t r = componentLifecycleManager.takeComponentOwnership(data);
+        int32_t r = componentLifecycleManager.takeComponentOwnership(data, mtx_);
         if (r == 0) {
             pluginComponentsMap_[plugin->getPath()].emplace_back(name, data);
         }
diff --git a/src/plugin/pluginmanager.h b/src/plugin/pluginmanager.h
index 16f95b867c084aa77d8f196fc65afbf54890060e..703f8835bb7ec57cbaf88fe77b06a85fc45bbaeb 100644
--- a/src/plugin/pluginmanager.h
+++ b/src/plugin/pluginmanager.h
@@ -56,7 +56,7 @@ private:
 
     // A Component is either a MediaHandler or a ChatHandler.
     // A ComponentFunction is a function that may start or end a component life.
-    using ComponentFunction = std::function<int32_t(void*)>;
+    using ComponentFunction = std::function<int32_t(void*, std::mutex&)>;
 
     // A list of component type (MediaHandler or ChatHandler), and component pointer pairs
     using ComponentPtrList = std::list<std::pair<std::string, void*>>;
@@ -86,8 +86,8 @@ private:
     // Map between plugins' library path and their components list
     using PluginComponentsMap = std::map<std::string, ComponentPtrList>;
 
-    // Vector with plugins' destruction functions
-    using ExitFuncVec = std::vector<JAMI_PluginExitFunc>;
+    // Map with plugins' destruction functions
+    using ExitFuncMap = std::map<std::string, JAMI_PluginExitFunc>;
     using ObjectFactoryVec = std::vector<ObjectFactory>;
     using ObjectFactoryMap = std::map<std::string, ObjectFactory>;
 
@@ -215,7 +215,7 @@ private:
     PluginMap dynPluginMap_ {};
 
     // Should keep reference to plugins' destruction functions read during library loading.
-    ExitFuncVec exitFuncVec_ {};
+    ExitFuncMap exitFunc_ {};
 
     ObjectFactoryMap exactMatchMap_ {};
     ObjectFactoryVec wildCardVec_ {};
@@ -228,5 +228,7 @@ private:
 
     // Keeps a map between plugins' library path and their components list.
     PluginComponentsMap pluginComponentsMap_ {};
+
+    std::mutex mtx_;
 };
 } // namespace jami
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 022e0d29d9afd1577609af9e166ed0b16bec4769..74bf4bb9257a3456be4b58c98ddcb448efd6a51c 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -41,8 +41,8 @@
 #include "dring/media_const.h"
 #include "client/ring_signal.h"
 #include "ice_transport.h"
+
 #ifdef ENABLE_PLUGIN
-// Plugin manager
 #include "plugin/jamipluginmanager.h"
 #endif
 
@@ -128,24 +128,29 @@ SIPCall::getSIPAccount() const
 void
 SIPCall::createCallAVStreams()
 {
+    if (videortp_->hasConference()) {
+        clearCallAVStreams();
+        return;
+    }
+    auto baseId = getCallId();
     /**
      *   Map: maps the AudioFrame to an AVFrame
      **/
-    auto audioMap = [](const std::shared_ptr<jami::MediaFrame> m) -> AVFrame* {
+    auto audioMap = [](const std::shared_ptr<jami::MediaFrame>& m) -> AVFrame* {
         return std::static_pointer_cast<AudioFrame>(m)->pointer();
     };
 
     // Preview
     if (auto& localAudio = avformatrtp_->getAudioLocal()) {
         auto previewSubject = std::make_shared<MediaStreamSubject>(audioMap);
-        StreamData microStreamData {getCallId(), false, StreamType::audio, getPeerNumber()};
+        StreamData microStreamData {baseId, false, StreamType::audio, getPeerNumber()};
         createCallAVStream(microStreamData, *localAudio, previewSubject);
     }
 
     // Receive
     if (auto& audioReceive = avformatrtp_->getAudioReceive()) {
         auto receiveSubject = std::make_shared<MediaStreamSubject>(audioMap);
-        StreamData phoneStreamData {getCallId(), true, StreamType::audio, getPeerNumber()};
+        StreamData phoneStreamData {baseId, true, StreamType::audio, getPeerNumber()};
         createCallAVStream(phoneStreamData, (AVMediaStream&) *audioReceive, receiveSubject);
     }
 #ifdef ENABLE_VIDEO
@@ -153,21 +158,21 @@ SIPCall::createCallAVStreams()
         /**
          *   Map: maps the VideoFrame to an AVFrame
          **/
-        auto videoMap = [](const std::shared_ptr<jami::MediaFrame> m) -> AVFrame* {
+        auto videoMap = [](const std::shared_ptr<jami::MediaFrame>& m) -> AVFrame* {
             return std::static_pointer_cast<VideoFrame>(m)->pointer();
         };
 
         // Preview
         if (auto& videoPreview = videortp_->getVideoLocal()) {
             auto previewSubject = std::make_shared<MediaStreamSubject>(videoMap);
-            StreamData previewStreamData {getCallId(), false, StreamType::video, getPeerNumber()};
+            StreamData previewStreamData {baseId, false, StreamType::video, getPeerNumber()};
             createCallAVStream(previewStreamData, *videoPreview, previewSubject);
         }
 
         // Receive
         if (auto& videoReceive = videortp_->getVideoReceive()) {
             auto receiveSubject = std::make_shared<MediaStreamSubject>(videoMap);
-            StreamData receiveStreamData {getCallId(), true, StreamType::video, getPeerNumber()};
+            StreamData receiveStreamData {baseId, true, StreamType::video, getPeerNumber()};
             createCallAVStream(receiveStreamData, *videoReceive, receiveSubject);
         }
     }
@@ -192,6 +197,13 @@ SIPCall::createCallAVStream(const StreamData& StreamData,
         .getCallServicesManager()
         .createAVSubject(StreamData, it->second);
 }
+
+void
+SIPCall::clearCallAVStreams()
+{
+    std::lock_guard<std::mutex> lk(avStreamsMtx_);
+    callAVStreams.clear();
+}
 #endif // ENABLE_PLUGIN
 
 void
@@ -1238,11 +1250,10 @@ SIPCall::startAllMedia()
 #endif
         rtp->updateMedia(remote, local);
 
-        rtp->setSuccessfulSetupCb(
-            [wthis = weak()](MediaType type, bool isRemote) {
-                if (auto this_ = wthis.lock())
-                    this_->rtpSetupSuccess(type, isRemote);
-            });
+        rtp->setSuccessfulSetupCb([wthis = weak()](MediaType type, bool isRemote) {
+            if (auto this_ = wthis.lock())
+                this_->rtpSetupSuccess(type, isRemote);
+        });
 
 #ifdef ENABLE_VIDEO
         videortp_->setRequestKeyFrameCallback([wthis = weak()] {
@@ -1337,14 +1348,11 @@ SIPCall::stopAllMedia()
 #endif
 #ifdef ENABLE_PLUGIN
     {
+        clearCallAVStreams();
         std::lock_guard<std::mutex> lk(avStreamsMtx_);
-        callAVStreams.erase(getCallId() + "00"); // audio out
-        callAVStreams.erase(getCallId() + "01"); // audio in
-        callAVStreams.erase(getCallId() + "10"); // video out
-        callAVStreams.erase(getCallId() + "11"); // video in
+        Manager::instance().getJamiPluginManager().getCallServicesManager().clearAVSubject(
+            getCallId());
     }
-    jami::Manager::instance().getJamiPluginManager().getCallServicesManager().clearAVSubject(
-        getCallId());
 #endif
 }
 
@@ -1394,8 +1402,7 @@ SIPCall::onMediaUpdate()
             std::lock_guard<std::recursive_mutex> lk {this_->callMutex_};
             // The call is already ended, so we don't need to restart medias
             if (not this_->inviteSession_
-                or this_->inviteSession_->state == PJSIP_INV_STATE_DISCONNECTED
-                or not this_->sdp_)
+                or this_->inviteSession_->state == PJSIP_INV_STATE_DISCONNECTED or not this_->sdp_)
                 return;
             // If ICE is not used, start medias now
             auto rem_ice_attrs = this_->sdp_->getIceAttributes();
@@ -1580,6 +1587,9 @@ SIPCall::enterConference(const std::string& confId)
     auto conf = Manager::instance().getConferenceFromID(confId);
     getVideoRtp().enterConference(conf.get());
 #endif
+#ifdef ENABLE_PLUGIN
+    clearCallAVStreams();
+#endif
 }
 
 void
@@ -1588,6 +1598,9 @@ SIPCall::exitConference()
 #ifdef ENABLE_VIDEO
     getVideoRtp().exitConference();
 #endif
+#ifdef ENABLE_PLUGIN
+    createCallAVStreams();
+#endif
 }
 
 bool
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 619769accb12f1d988a4f2288282d1e4e694f35d..d41676304e32135223052e0bcc01c2a6267f9027 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -261,13 +261,13 @@ private:
         return tmpMediaTransport_ ? tmpMediaTransport_.get() : mediaTransport_.get();
     }
 
+#ifdef ENABLE_PLUGIN
     /**
      * Call Streams and some typedefs
      */
     using AVMediaStream = Observable<std::shared_ptr<MediaFrame>>;
     using MediaStreamSubject = PublishMapSubject<std::shared_ptr<MediaFrame>, AVFrame*>;
 
-#ifdef ENABLE_PLUGIN
     /**
      * @brief createCallAVStream
      * Creates a call AV stream like video input, video receive, audio input or audio receive
@@ -283,10 +283,15 @@ private:
      * Creates all Call AV Streams (2 if audio, 4 if audio video)
      */
     void createCallAVStreams();
-#endif // ENABLE_PLUGIN
+
+    /**
+     * @brief Detach all plugins from call streams;
+     */
+    void clearCallAVStreams();
 
     std::mutex avStreamsMtx_ {};
     std::map<std::string, std::shared_ptr<MediaStreamSubject>> callAVStreams;
+#endif // ENABLE_PLUGIN
 
     void setCallMediaLocal();