diff --git a/src/conference.cpp b/src/conference.cpp
index ec0e2569cf62c24e5a40b7fd7663f0091bd7378b..68f8b02fda2b35d790fe47e40c3944e00d41ab30 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -87,11 +87,14 @@ Conference::Conference(const std::shared_ptr<Account>& account)
     setLocalHostDefaultMediaSource();
 
 #ifdef ENABLE_VIDEO
+    auto itVideo = std::find_if(hostSources_.begin(), hostSources_.end(), [&](auto attr) {
+        return attr.type_ == MediaType::MEDIA_VIDEO;
+    });
     // We are done if the video is disabled.
-    if (not videoEnabled_)
+    if (not videoEnabled_ || itVideo == hostSources_.end())
         return;
 
-    videoMixer_ = std::make_shared<video::VideoMixer>(id_, hostVideoSource_.sourceUri_);
+    videoMixer_ = std::make_shared<video::VideoMixer>(id_, itVideo->sourceUri_);
     videoMixer_->setOnSourcesUpdated([this](std::vector<video::SourceInfo>&& infos) {
         runOnMainThread([w = weak(), infos = std::move(infos)] {
             auto shared = w.lock();
@@ -306,36 +309,37 @@ Conference::setState(State state)
 void
 Conference::setLocalHostDefaultMediaSource()
 {
+    hostSources_.clear();
     // Setup local audio source
+    MediaAttribute audioAttr;
     if (confState_ == State::ACTIVE_ATTACHED) {
-        hostAudioSource_ = {MediaType::MEDIA_AUDIO, false, false, true, {}, "audio_0"};
-        hostAudioSource_.sourceType_ = MediaSourceType::CAPTURE_DEVICE;
-    } else {
-        hostAudioSource_ = {};
+        audioAttr = {MediaType::MEDIA_AUDIO, false, false, true, {}, "audio_0"};
+        audioAttr.sourceType_ = MediaSourceType::CAPTURE_DEVICE;
     }
 
     JAMI_DBG("[conf %s] Setting local host audio source to [%s]",
              id_.c_str(),
-             hostAudioSource_.toString().c_str());
+             audioAttr.toString().c_str());
+    hostSources_.emplace_back(audioAttr);
 
 #ifdef ENABLE_VIDEO
     if (isVideoEnabled()) {
+        MediaAttribute videoAttr;
         // Setup local video source
         if (confState_ == State::ACTIVE_ATTACHED) {
-            hostVideoSource_
+            videoAttr
                 = {MediaType::MEDIA_VIDEO,
                    false,
                    false,
                    true,
                    Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice(),
                    "video_0"};
-            hostVideoSource_.sourceType_ = MediaSourceType::CAPTURE_DEVICE;
-        } else {
-            hostVideoSource_ = {};
+            videoAttr.sourceType_ = MediaSourceType::CAPTURE_DEVICE;
         }
         JAMI_DBG("[conf %s] Setting local host video source to [%s]",
                  id_.c_str(),
-                 hostVideoSource_.toString().c_str());
+                 videoAttr.toString().c_str());
+        hostSources_.emplace_back(videoAttr);
     }
 #endif
 }
@@ -407,15 +411,9 @@ Conference::createConfAVStream(const StreamData& StreamData,
 void
 Conference::setLocalHostMuteState(MediaType type, bool muted)
 {
-    if (type == MediaType::MEDIA_AUDIO) {
-        hostAudioSource_.muted_ = muted;
-#ifdef ENABLE_VIDEO
-    } else if (type == MediaType::MEDIA_VIDEO) {
-        hostVideoSource_.muted_ = muted;
-#endif
-    } else {
-        JAMI_ERR("Unsupported media type");
-    }
+    for (auto& source : hostSources_)
+        if (source.type_ == type)
+            source.muted_ = muted;
 }
 
 bool
@@ -431,19 +429,17 @@ Conference::isMediaSourceMuted(MediaType type) const
         return true;
     }
 
-#ifdef ENABLE_VIDEO
-    auto const& mediaAttr = type == MediaType::MEDIA_AUDIO ? hostAudioSource_ : hostVideoSource_;
-#else
-    auto const& mediaAttr = hostAudioSource_;
-#endif
-    if (mediaAttr.type_ == MediaType::MEDIA_NONE) {
-        JAMI_WARN("The host source for %s is not set. The mute state is meaningless",
-                  mediaAttr.mediaTypeToString(mediaAttr.type_));
-        // Assume muted if the media is not present.
-        return true;
+    for (const auto& source : hostSources_) {
+        if (source.muted_)
+            return true;
+        if (source.type_ == MediaType::MEDIA_NONE) {
+            JAMI_WARN("The host source for %s is not set. The mute state is meaningless",
+                      source.mediaTypeToString(source.type_));
+            // Assume muted if the media is not present.
+            return true;
+        }
     }
-
-    return mediaAttr.muted_;
+    return false;
 }
 
 void
@@ -538,48 +534,35 @@ Conference::requestMediaChange(const std::vector<DRing::MediaMap>& mediaList)
                  mediaAttr.toString(true).c_str());
     }
 
-    uint32_t videoIdx = 0;
-    for (auto const& mediaAttr : mediaAttrList) {
-#ifdef ENABLE_VIDEO
-        auto& mediaSource = mediaAttr.type_ == MediaType::MEDIA_AUDIO ? hostAudioSource_
-                                                                      : hostVideoSource_;
-#else
-        auto& mediaSource = hostAudioSource_;
-#endif
-        if (not mediaAttr.sourceUri_.empty() and mediaSource.sourceUri_ != mediaAttr.sourceUri_) {
-            // For now, only video source URI can be changed by the client,
-            // so it's an error if we get here and the type is not video.
-            if (mediaAttr.type_ != MediaType::MEDIA_VIDEO) {
-                JAMI_ERR("[conf %s] Media source can be changed only for video!",
-                         getConfId().c_str());
-                return false;
-            }
-
-            mediaSource.sourceUri_ = mediaAttr.sourceUri_;
-            mediaSource.sourceType_ = mediaAttr.sourceType_;
+    if (videoMixer_)
+        videoMixer_->stopInputs();
 
-            if (mediaSource.muted_ != mediaAttr.muted_) {
+    std::vector<std::string> newVideoInputs;
+    for (auto const& mediaAttr : mediaAttrList) {
+        // Find media
+        auto oldIdx = std::find_if(hostSources_.begin(), hostSources_.end(), [&](auto oldAttr) {
+            return oldAttr.sourceUri_ == mediaAttr.sourceUri_;
+        });
+        // If video, add to newVideoInputs
+        // NOTE: For now, only supports video
+        if (mediaAttr.type_ == MediaType::MEDIA_VIDEO)
+            newVideoInputs.emplace_back(mediaAttr.sourceUri_);
+        if (oldIdx != hostSources_.end()) {
+            // Check if muted status changes
+            if (mediaAttr.muted_ != oldIdx->muted_) {
                 // If the current media source is muted, just call un-mute, it
                 // will set the new source as input.
                 muteLocalHost(mediaAttr.muted_,
                               mediaAttr.type_ == MediaType::MEDIA_AUDIO
                                   ? DRing::Media::Details::MEDIA_TYPE_AUDIO
                                   : DRing::Media::Details::MEDIA_TYPE_VIDEO);
-            } else {
-                videoMixer_->switchInput(mediaAttr.sourceUri_, videoIdx);
             }
-            videoIdx++;
-        }
-
-        // Update the mute state if changed.
-        if (mediaSource.muted_ != mediaAttr.muted_) {
-            muteLocalHost(mediaAttr.muted_,
-                          mediaAttr.type_ == MediaType::MEDIA_AUDIO
-                              ? DRing::Media::Details::MEDIA_TYPE_AUDIO
-                              : DRing::Media::Details::MEDIA_TYPE_VIDEO);
         }
     }
 
+    if (videoMixer_)
+        videoMixer_->switchInputs(newVideoInputs);
+    hostSources_ = mediaAttrList; // New medias
     return true;
 }
 
@@ -893,9 +876,11 @@ Conference::attachLocalParticipant()
 
 #ifdef ENABLE_VIDEO
         if (videoMixer_) {
-            std::vector<std::string> videoInputs = {hostVideoSource_.sourceUri_};
-            if (not mediaSecondaryInput_.empty())
-                videoInputs.emplace_back(mediaSecondaryInput_);
+            std::vector<std::string> videoInputs;
+            for (const auto& source : hostSources_) {
+                if (source.type_ == MediaType::MEDIA_VIDEO)
+                    videoInputs.emplace_back(source.sourceUri_);
+            }
             videoMixer_->switchInputs(videoInputs);
         }
 #endif
@@ -918,16 +903,11 @@ Conference::detachLocalParticipant()
                                                                  RingBufferPool::DEFAULT_ID);
         });
 
-        // Reset local audio source
-        hostAudioSource_ = {};
-
 #ifdef ENABLE_VIDEO
         if (videoMixer_)
             videoMixer_->stopInputs();
-
-        // Reset local video source
-        hostVideoSource_ = {};
 #endif
+        hostSources_.clear();
         setState(State::ACTIVE_DETACHED);
     } else {
         JAMI_WARN(
@@ -1036,8 +1016,21 @@ Conference::switchInput(const std::string& input)
 {
 #ifdef ENABLE_VIDEO
     JAMI_DBG("[Conf:%s] Setting video input to %s", id_.c_str(), input.c_str());
-
-    hostVideoSource_.sourceUri_ = input;
+    std::vector<MediaAttribute> newSources;
+    auto firstVideo = true;
+    // Rewrite hostSources (remove all except one video input)
+    // This method is replaced by requestMediaChange
+    for (auto& source : hostSources_) {
+        if (source.type_ == MediaType::MEDIA_VIDEO) {
+            if (firstVideo) {
+                firstVideo = false;
+                source.sourceUri_ = input;
+                newSources.emplace_back(source);
+            }
+        } else {
+            newSources.emplace_back(source);
+        }
+    }
 
     // Done if the video is disabled
     if (not isVideoEnabled())
@@ -1075,6 +1068,16 @@ Conference::getVideoMixer()
 {
     return videoMixer_;
 }
+
+std::string
+Conference::getVideoInput() const
+{
+    for (const auto& source : hostSources_) {
+        if (source.type_ == MediaType::MEDIA_VIDEO)
+            return source.sourceUri_;
+    }
+    return {};
+}
 #endif
 
 void
@@ -1502,8 +1505,13 @@ Conference::muteLocalHost(bool is_muted, const std::string& mediaType)
             }
         } else {
             if (auto mixer = videoMixer_) {
-                JAMI_DBG("Un-muting local video source");
-                mixer->switchInputs({hostVideoSource_.sourceUri_});
+                JAMI_DBG("Un-muting local video sources");
+                std::vector<std::string> videoInputs;
+                for (const auto& source : hostSources_) {
+                    if (source.type_ == MediaType::MEDIA_VIDEO)
+                        videoInputs.emplace_back(source.sourceUri_);
+                }
+                mixer->switchInputs(videoInputs);
             }
         }
         emitSignal<DRing::CallSignal::VideoMuted>(id_, is_muted);
diff --git a/src/conference.h b/src/conference.h
index 2d695a8156004a893d1301088b4adcecd294b55e..5f8b915111f0e83fc2fd5ff3977700b4089ff649 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -334,7 +334,7 @@ public:
 #ifdef ENABLE_VIDEO
     void createSinks(const ConfInfo& infos);
     std::shared_ptr<video::VideoMixer> getVideoMixer();
-    std::string getVideoInput() const { return hostVideoSource_.sourceUri_; }
+    std::string getVideoInput() const;
 #endif
 
     std::vector<std::map<std::string, std::string>> getConferenceInfos() const
@@ -400,7 +400,6 @@ private:
 
 #ifdef ENABLE_VIDEO
     bool videoEnabled_;
-    std::string mediaSecondaryInput_ {};
     std::shared_ptr<video::VideoMixer> videoMixer_;
     std::map<std::string, std::shared_ptr<video::SinkClient>> confSinksMap_ {};
 #endif
@@ -421,19 +420,10 @@ private:
 
     /**
      * If the local host is participating in the conference (attached
-     * mode ), these two  variables will hold the media source states
+     * mode ), this variable will hold the media source states
      * of the local host.
-     *
-     * NOTE:
-     * Currently, the conference and the client support only one stream
-     * per media type, even if the call supports an arbitrary number of
-     * streams per media type. Thus, these two variables will hold the
-     * current media source attributes
      */
-    MediaAttribute hostAudioSource_ {};
-#ifdef ENABLE_VIDEO
-    MediaAttribute hostVideoSource_ {};
-#endif
+    std::vector<MediaAttribute> hostSources_;
 
     bool localModAdded_ {false};