diff --git a/src/call.cpp b/src/call.cpp index c036d56c7c32e5377f120a1dcbdec4557df4c627..87dfa762b790278f19826a5c72bbeefd3b07dcc5 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -676,28 +676,9 @@ Call::setConferenceInfo(const std::string& msg) // confID_ empty -> participant set confInfo with the received one confInfo_ = std::move(newInfo); // Inform client that layout has changed - jami::emitSignal<DRing::CallSignal::OnConferenceInfosUpdated>( - id_, confInfo_.toVectorMapStringString()); - } else { - // confID_ not empty -> host merge confInfo with the received confInfo - for (auto& newI : newInfo) { - bool isNewParticipant = true; - for (auto& oldI : confInfo_) { - if (newI.uri == oldI.uri) { - oldI = newI; - isNewParticipant = false; - break; - } - } - if (isNewParticipant) { - // ParticipantInfo not present in confInfo -> the sender of newInfo ... - // is currently hosting another conference. Add the unknown participant ... - // to the confInfo - confInfo_.emplace_back(newI); - } - } - if (auto conf = Manager::instance().getConferenceFromID(confID_)) - conf->updateConferenceInfo(confInfo_); + jami::emitSignal<DRing::CallSignal::OnConferenceInfosUpdated>(id_, confInfo_.toVectorMapStringString()); + } else if (auto conf = Manager::instance().getConferenceFromID(confID_)) { + conf->mergeConfInfo(newInfo, getPeerNumber()); } } } diff --git a/src/conference.cpp b/src/conference.cpp index 3228946a48066b303cc4ddf8474fdf28da19dc5c..5651bc8440f6671d6bb53975d538f93d554cf57a 100644 --- a/src/conference.cpp +++ b/src/conference.cpp @@ -40,6 +40,9 @@ #include "logger.h" #include "dring/media_const.h" #include "audio/ringbufferpool.h" +#include "sip/sipcall.h" + +#include <opendht/thread_pool.h> using namespace std::literals; @@ -275,6 +278,16 @@ ConfInfo::toVectorMapStringString() const return infos; } +std::string +ConfInfo::toString() const +{ + Json::Value jsonArray = {}; + for (const auto& info : *this) { + jsonArray.append(info.toJson()); + } + return Json::writeString(Json::StreamWriterBuilder{}, jsonArray); +} + void Conference::sendConferenceInfos() { @@ -288,25 +301,19 @@ Conference::sendConferenceInfos() if (!account) continue; - ConfInfo confInfo = getConfInfoHostUri(account->getUsername() + "@ring.dht"); - Json::Value jsonArray = {}; - for (const auto& info : confInfo) { - jsonArray.append(info.toJson()); - } - - runOnMainThread( - [call, - confInfoStr = Json::writeString(Json::StreamWriterBuilder {}, jsonArray), - from = account->getFromUri()] { - call->sendTextMessage({{"application/confInfo+json", confInfoStr}}, from); - }); + 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_, - confInfo_ - .toVectorMapStringString()); + jami::emitSignal<DRing::CallSignal::OnConferenceInfosUpdated>(id_, getConfInfoHostUri("", "").toVectorMapStringString()); } void @@ -721,15 +728,34 @@ Conference::updateMuted() } ConfInfo -Conference::getConfInfoHostUri(std::string_view uri) +Conference::getConfInfoHostUri(std::string_view localHostURI, std::string_view destURI) { ConfInfo newInfo = confInfo_; - for (auto& info : newInfo) { - if (info.uri.empty()) { - info.uri = uri; - break; + + for (auto it = newInfo.begin(); it != newInfo.end();) { + bool isRemoteHost = remoteHosts_.find(it->uri) != remoteHosts_.end(); + if (it->uri.empty() and not destURI.empty()) { + // fill the empty uri with the local host URI, let void for local client + it->uri = localHostURI; + } + if (isRemoteHost) { + // Don't send back the ParticipantInfo for remote Host + // For other than remote Host, the new info is in remoteHosts_ + it = newInfo.erase(it); + } else { + ++it; } } + // Add remote Host info + for (const auto& [hostUri, confInfo] : remoteHosts_) { + // Add remote info for remote host destination + // Example: ConfA, ConfB & ConfC + // ConfA send ConfA and ConfB for ConfC + // ConfA send ConfA and ConfC for ConfB + // ... + if (destURI != hostUri) + newInfo.insert(newInfo.end(), confInfo.begin(), confInfo.end()); + } return newInfo; } @@ -817,4 +843,100 @@ Conference::muteLocalHost(bool is_muted, const std::string& mediaType) } } +void +Conference::resizeRemoteParticipant(const std::string& peerURI, ParticipantInfo& remoteCell) +{ + int remoteFrameHeight {0}; + int remoteFrameWidth {0}; + ParticipantInfo localCell; + + // get the size of the remote frame + for (const auto& item : participants_) { + auto sipCall = std::dynamic_pointer_cast<SIPCall>( + Manager::instance().callFactory.getCall(item, Call::LinkType::SIP)); + if (sipCall && sipCall->getPeerNumber().find(peerURI) != std::string::npos) { + remoteFrameHeight = sipCall->getVideoRtp().getVideoReceive()->getHeight(); + remoteFrameWidth = sipCall->getVideoRtp().getVideoReceive()->getWidth(); + break; + } + } + + if (remoteFrameHeight == 0 or remoteFrameWidth == 0) { + JAMI_WARN("Remote frame size not found."); + return; + } + + // get the size of the local frame + for (const auto& p : confInfo_) { + if (p.uri == peerURI) { + localCell = p; + break; + } + } + + const float zoomX = (float) remoteFrameWidth / localCell.w; + const float zoomY = (float) remoteFrameHeight / localCell.h; + + remoteCell.x = remoteCell.x / zoomX + localCell.x; + remoteCell.y = remoteCell.y / zoomY + localCell.y; + remoteCell.w = remoteCell.w / zoomX; + remoteCell.h = remoteCell.h / zoomY; + +} + +std::string +Conference::confInfo2str(const ConfInfo& confInfo) +{ + Json::Value jsonArray = {}; + for (const auto& info : confInfo) { + jsonArray.append(info.toJson()); + } + return Json::writeString(Json::StreamWriterBuilder{}, jsonArray); +} + +void +Conference::mergeConfInfo(ConfInfo& newInfo, const std::string& peerURI) +{ + if (newInfo.empty()) { + JAMI_DBG("confInfo empty, remove remoteHost"); + remoteHosts_.erase(peerURI); + return; + } + + for (auto& partInfo : newInfo) { + resizeRemoteParticipant(peerURI, partInfo); + } + + bool updateNeeded = false; + auto it = remoteHosts_.find(peerURI); + if (it != remoteHosts_.end()) { + // Compare confInfo before update + if (it->second != newInfo) { + it->second = newInfo; + updateNeeded = true; + } else + JAMI_WARN("No change in confInfo, don't update"); + } else { + remoteHosts_.emplace(peerURI, newInfo); + updateNeeded = true; + } + // Send confInfo only if needed to avoid loops + if (updateNeeded) { + std::lock_guard<std::mutex> lk(confInfoMutex_); + sendConferenceInfos(); + } +} + +std::string_view +Conference::findHostforRemoteParticipant(std::string_view uri) +{ + for (const auto& host : remoteHosts_) { + for (const auto& p : host.second) { + if (uri == string_remove_suffix(p.uri, '@')) + return host.first; + } + } + return ""; +} + } // namespace jami diff --git a/src/conference.h b/src/conference.h index 61aaefcede3d5558ee897de170d3471fd3dd0d5d..76dc6ec0ea086a8ee100e088ab9920c51a7e18b6 100644 --- a/src/conference.h +++ b/src/conference.h @@ -29,6 +29,7 @@ #include <memory> #include <vector> #include <string_view> +#include <map> #include "audio/audio_input.h" @@ -106,13 +107,55 @@ struct ParticipantInfo {"audioModeratorMuted", audioModeratorMuted ? "true" : "false"}, {"isModerator", isModerator ? "true" : "false"}}; } + + 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; + } + + friend bool operator!=(const ParticipantInfo& p1, const ParticipantInfo& p2) + { + return !(p1 == p2); + } }; struct ConfInfo : public std::vector<ParticipantInfo> { - std::vector<std::map<std::string, std::string>> toVectorMapStringString() const; int h {0}; int w {0}; + + 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; }); + if (it != c2.end()) + continue; + else + return false; + } + return true; + } + + 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>; @@ -250,6 +293,9 @@ public: void hangupParticipant(const std::string& participant_id); void updateMuted(); void muteLocalHost(bool is_muted, const std::string& mediaType); + bool isRemoteParticipant(const std::string& uri); + void resizeRemoteParticipant(const std::string& peerURI, ParticipantInfo& remoteCell); + void mergeConfInfo(ConfInfo& newInfo, const std::string& peerURI); private: std::weak_ptr<Conference> weak() @@ -289,12 +335,16 @@ private: bool isMuted(std::string_view uri) const; - ConfInfo getConfInfoHostUri(std::string_view uri); + ConfInfo getConfInfoHostUri(std::string_view localHostURI, std::string_view destURI); bool isHost(std::string_view uri) const; bool audioMuted_ {false}; bool videoMuted_ {false}; bool localModAdded_ {false}; + + std::map<std::string, ConfInfo> remoteHosts_; + std::string confInfo2str(const ConfInfo& confInfo); + std::string_view findHostforRemoteParticipant(std::string_view uri); }; } // namespace jami