From 1ed5bb1e1f2ff740d0d5d9f4ff0f302e1278bdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 24 Mar 2023 11:47:25 -0400 Subject: [PATCH] participantsmodel: move to current call Not useful in calladapter as we need to check if it's for the current call. Also, fix some race-confitions where the callparticipantsmodel was empty (in swarm-call with only the host in general) https://git.jami.net/savoirfairelinux/jami-client-qt/-/issues/639 Change-Id: I69df1aa9f7d2d5b198c0fdc01e88e36f9c2c0bd7 --- src/app/calladapter.cpp | 233 +++--------------------------------- src/app/calladapter.h | 11 +- src/app/currentcall.cpp | 115 +++++++++++++++++- src/app/currentcall.h | 7 ++ src/libclient/callmodel.cpp | 29 ++--- 5 files changed, 155 insertions(+), 240 deletions(-) diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp index 5d45ce8fe..07bf243df 100644 --- a/src/app/calladapter.cpp +++ b/src/app/calladapter.cpp @@ -32,8 +32,8 @@ #include <QTimer> #include <QJsonObject> +#include <api/callmodel.h> #include <api/callparticipantsmodel.h> -#include <api/devicemodel.h> #include <media_const.h> @@ -47,9 +47,6 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation); - participantsModel_.reset(new CallParticipantsModel(lrcInstance_, this)); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, participantsModel_.get(), "CallParticipantsModel"); - overlayModel_.reset(new CallOverlayModel(lrcInstance_, this)); QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, overlayModel_.get(), "CallOverlayModel"); @@ -164,69 +161,6 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId #endif } -void -CallAdapter::onParticipantAdded(const QString& callId, int index) -{ - auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_); - auto& callModel = accInfo.callModel; - try { - if (lrcInstance_->get_selectedConvUid().isEmpty()) - return; - - const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( - lrcInstance_->get_selectedConvUid()); - if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { - qDebug() << "trying to update not current conf"; - return; - } - auto infos = getConferencesInfos(); - if (index < infos.size()) - participantsModel_->addParticipant(index, infos[index]); - } catch (...) { - } -} - -void -CallAdapter::onParticipantRemoved(const QString& callId, int index) -{ - auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_); - auto& callModel = accInfo.callModel; - try { - if (lrcInstance_->get_selectedConvUid().isEmpty()) - return; - - const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( - lrcInstance_->get_selectedConvUid()); - if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { - qDebug() << "trying to update not current conf"; - return; - } - participantsModel_->removeParticipant(index); - } catch (...) { - } -} - -void -CallAdapter::onParticipantUpdated(const QString& callId, int index) -{ - auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_); - auto& callModel = accInfo.callModel; - try { - if (lrcInstance_->get_selectedConvUid().isEmpty()) - return; - const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( - lrcInstance_->get_selectedConvUid()); - if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { - qDebug() << "trying to update not current conf"; - return; - } - auto infos = getConferencesInfos(); - if (index < infos.size()) - participantsModel_->updateParticipant(index, infos[index]); - } catch (...) { - } -} - void CallAdapter::onCallStarted(const QString& callId) { @@ -336,30 +270,6 @@ CallAdapter::onCallStatusChanged(const QString& callId, int code) } } -void -CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId) -{ - auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); - auto& callModel = accInfo.callModel; - - try { - const auto call = callModel->getCall(callId); - /* - * Change status label text. - */ - const auto& convInfo = lrcInstance_->getConversationFromCallId(callId); - if (!convInfo.uid.isEmpty()) { - if (!convInfo.confId.isEmpty() && callId != convInfo.confId) { - // In this case the conv has a confId, ignore subcalls changes. - return; - } - Q_EMIT callInfosChanged(call.isAudioOnly, accountId, convInfo.uid); - participantsModel_->setConferenceLayout(static_cast<int>(call.layout), callId); - } - } catch (...) { - } -} - void CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId) { @@ -525,73 +435,6 @@ CallAdapter::updateCall(const QString& convUid, const QString& accountId, bool f } } } - - participantsModel_->setParticipants(call->id, getConferencesInfos()); - participantsModel_->setConferenceLayout(static_cast<int>(call->layout), call->id); -} - -void -CallAdapter::fillParticipantData(QJsonObject& participant) const -{ - // TODO: getCurrentDeviceId should be part of CurrentAccount, and Current<thing> - // should be read accessible through LRCInstance ?? - auto getCurrentDeviceId = [](const account::Info& accInfo) -> QString { - const auto& deviceList = accInfo.deviceModel->getAllDevices(); - auto devIt = std::find_if(std::cbegin(deviceList), - std::cend(deviceList), - [](const Device& dev) { return dev.isCurrent; }); - return devIt != deviceList.cend() ? devIt->id : QString(); - }; - - auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_); - using namespace lrc::api::ParticipantsInfosStrings; - - // If both the URI and the device Id match, we set the "is local" flag - // used to filter out the participant. - // TODO: - // - This filter should always be applied, and any local streams should render - // using local sinks. Local non-preview participants should have proxy participant - // items replaced into this model using their local sink Ids. - // - The app setting should remain and be used to control whether or not the preview - // sink partipcant is rendered. - auto uri = participant[URI].toString(); - participant[ISLOCAL] = false; - if (uri == accInfo.profileInfo.uri && participant[DEVICE] == getCurrentDeviceId(accInfo)) { - participant[BESTNAME] = tr("Me"); - participant[ISLOCAL] = true; - } else { - try { - participant[BESTNAME] = accInfo.contactModel->bestNameForContact(uri); - } catch (...) { - } - } -} - -QVariantList -CallAdapter::getConferencesInfos() const -{ - QVariantList map; - const auto& convInfo = lrcInstance_->getConversationFromConvUid( - lrcInstance_->get_selectedConvUid()); - if (convInfo.uid.isEmpty()) - return map; - auto callId = convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; - if (!callId.isEmpty()) { - try { - auto& participantsModel = lrcInstance_->accountModel() - .getAccountInfo(accountId_) - .callModel.get() - ->getParticipantsInfos(callId); - for (int index = 0; index < participantsModel.getParticipants().size(); index++) { - auto participant = participantsModel.toQJsonObject(index); - fillParticipantData(participant); - map.push_back(QVariant(participant)); - } - return map; - } catch (...) { - } - } - return map; } void @@ -629,25 +472,6 @@ void CallAdapter::connectCallModel(const QString& accountId) { auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); - - connect(accInfo.callModel.get(), - &CallModel::participantAdded, - this, - &CallAdapter::onParticipantAdded, - Qt::UniqueConnection); - - connect(accInfo.callModel.get(), - &CallModel::participantRemoved, - this, - &CallAdapter::onParticipantRemoved, - Qt::UniqueConnection); - - connect(accInfo.callModel.get(), - &CallModel::participantUpdated, - this, - &CallAdapter::onParticipantUpdated, - Qt::UniqueConnection); - connect(accInfo.callModel.get(), &CallModel::callStarted, this, @@ -671,11 +495,6 @@ CallAdapter::connectCallModel(const QString& accountId) this, &CallAdapter::onCallAddedToConference, Qt::UniqueConnection); - - connect(accInfo.callModel.get(), - &CallModel::callInfosChanged, - this, - QOverload<const QString&, const QString&>::of(&CallAdapter::onCallInfosChanged)); } void @@ -719,24 +538,19 @@ CallAdapter::setActiveStream(const QString& uri, const QString& deviceId, const confId = convInfo.callId; try { const auto call = callModel->getCall(confId); - auto participants = getConferencesInfos(); - std::vector<QJsonObject> activeParticipants = {}; + auto& participantsModel = callModel->getParticipantsInfos(call.id); + auto participants = participantsModel.getParticipants(); + decltype(participants) activeParticipants = {}; bool removeActive = false; for (auto part : participants) { - auto participant = part.toJsonObject(); - - auto puri = participant[lrc::api::ParticipantsInfosStrings::URI].toString(); - auto pdeviceId = participant[lrc::api::ParticipantsInfosStrings::DEVICE].toString(); - auto pstreamId = participant[lrc::api::ParticipantsInfosStrings::STREAMID].toString(); - - auto isParticipant = puri == uri && pdeviceId == deviceId && pstreamId == streamId; - auto active = participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool(); - if (active && !isParticipant) - activeParticipants.push_back(participant); + auto isParticipant = part.uri == uri && part.device == deviceId + && part.sinkId == streamId; + if (part.active && !isParticipant) + activeParticipants.push_back(part); if (isParticipant) { // Else, continue. - if (!active) { + if (!part.active) { callModel->setActiveStream(confId, uri, deviceId, streamId, true); callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); } else if (call.layout == lrc::api::call::Layout::ONE_WITH_SMALL) { @@ -747,12 +561,8 @@ CallAdapter::setActiveStream(const QString& uri, const QString& deviceId, const } if (removeActive) { // If in Big, we can remove other actives - for (const auto& p : activeParticipants) { - auto puri = p[lrc::api::ParticipantsInfosStrings::URI].toString(); - auto deviceId = p[lrc::api::ParticipantsInfosStrings::DEVICE].toString(); - auto streamId = p[lrc::api::ParticipantsInfosStrings::STREAMID].toString(); - callModel->setActiveStream(confId, puri, deviceId, streamId, false); - } + for (const auto& part : activeParticipants) + callModel->setActiveStream(confId, part.uri, part.device, part.sinkId, false); } } catch (...) { } @@ -770,31 +580,24 @@ CallAdapter::minimizeParticipant(const QString& uri) confId = convInfo.callId; try { const auto call = callModel->getCall(confId); - auto participants = getConferencesInfos(); + auto& participantsModel = callModel->getParticipantsInfos(call.id); + auto participants = participantsModel.getParticipants(); auto activeParticipants = 0; for (auto& part : participants) { - auto participant = part.toJsonObject(); - if (participant[lrc::api::ParticipantsInfosStrings::ACTIVE].toBool()) { + if (part.active) { activeParticipants += 1; - if (participant[lrc::api::ParticipantsInfosStrings::URI].toString() == uri - && call.layout == lrc::api::call::Layout::ONE_WITH_SMALL) { - auto deviceId = participant[lrc::api::ParticipantsInfosStrings::DEVICE] - .toString(); - auto streamId = participant[lrc::api::ParticipantsInfosStrings::STREAMID] - .toString(); - callModel->setActiveStream(confId, uri, deviceId, streamId, false); + if (part.uri == uri && call.layout == lrc::api::call::Layout::ONE_WITH_SMALL) { + callModel->setActiveStream(confId, part.uri, part.device, part.sinkId, false); } } } if (activeParticipants == 1) { // only one active left, we can change the layout. - if (call.layout == lrc::api::call::Layout::ONE) { + if (call.layout == lrc::api::call::Layout::ONE) callModel->setConferenceLayout(confId, lrc::api::call::Layout::ONE_WITH_SMALL); - } else { + else callModel->setConferenceLayout(confId, lrc::api::call::Layout::GRID); - } } - } catch (...) { } } diff --git a/src/app/calladapter.h b/src/app/calladapter.h index ff1112bc9..a2d554ce0 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -24,7 +24,6 @@ #include "qmladapterbase.h" #include "screensaver.h" #include "calloverlaymodel.h" -#include "callparticipantsmodel.h" #include <QObject> #include <QString> @@ -80,7 +79,6 @@ public: Q_INVOKABLE void muteAudioToggle(); Q_INVOKABLE void muteCameraToggle(); Q_INVOKABLE bool isRecordingThisCall(); - Q_INVOKABLE QVariantList getConferencesInfos() const; Q_INVOKABLE void muteParticipant(const QString& accountUri, const QString& deviceId, const QString& sinkId, @@ -95,11 +93,10 @@ public: Q_INVOKABLE void setCallInfo(); Q_INVOKABLE void updateAdvancedInformation(); - Q_INVOKABLE bool takeScreenshot(const QImage &image, const QString &path); + Q_INVOKABLE bool takeScreenshot(const QImage& image, const QString& path); Q_SIGNALS: void callStatusChanged(int index, const QString& accountId, const QString& convUid); - void callInfosChanged(const QVariant& infos, const QString& accountId, const QString& convUid); // For Call Overlay void updateTimeText(const QString& time); @@ -109,18 +106,13 @@ public Q_SLOTS: void onShowCallView(const QString& accountId, const QString& convUid); void onAccountChanged(); void onCallStatusChanged(const QString& accountId, const QString& callId); - void onCallInfosChanged(const QString& accountId, const QString& callId); void onCallStatusChanged(const QString& callId, int code); void onCallAddedToConference(const QString& callId, const QString& confId); - void onParticipantAdded(const QString& callId, int index); - void onParticipantRemoved(const QString& callId, int index); - void onParticipantUpdated(const QString& callId, int index); void onCallStarted(const QString& callId); void onCallEnded(const QString& callId); private: void showNotification(const QString& accountId, const QString& convUid); - void fillParticipantData(QJsonObject& participant) const; void preventScreenSaver(bool state); void saveConferenceSubcalls(); @@ -129,7 +121,6 @@ private: ScreenSaver screenSaver; SystemTray* systemTray_; QScopedPointer<CallOverlayModel> overlayModel_; - QScopedPointer<CallParticipantsModel> participantsModel_; VectorString currentConfSubcalls_; std::unique_ptr<CallInformationListModel> callInformationListModel_; diff --git a/src/app/currentcall.cpp b/src/app/currentcall.cpp index 9213d04ff..8406633e7 100644 --- a/src/app/currentcall.cpp +++ b/src/app/currentcall.cpp @@ -16,13 +16,18 @@ */ #include "currentcall.h" +#include "qmlregister.h" #include <api/callparticipantsmodel.h> +#include <api/devicemodel.h> CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent) : QObject(parent) , lrcInstance_(lrcInstance) { + participantsModel_.reset(new CallParticipantsModel(lrcInstance_, this)); + QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, participantsModel_.get(), "CallParticipantsModel"); + connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, this, @@ -72,7 +77,17 @@ CurrentCall::updateId(QString callId) } } // Set the current id_ if there is a call. - set_id((accInfo.callModel->hasCall(callId) ? callId : QString())); + auto hasCall = accInfo.callModel->hasCall(callId); + set_id((hasCall ? callId : QString())); + + if (hasCall) { + try { + auto callInfo = accInfo.callModel->getCall(callId); + participantsModel_->setParticipants(id_, getConferencesInfos()); + participantsModel_->setConferenceLayout(static_cast<int>(callInfo.layout), id_); + } catch (...) { + } + } } void @@ -105,6 +120,88 @@ CurrentCall::updateParticipants() set_isConference(uris.size()); } +void +CurrentCall::onParticipantAdded(const QString& callId, int index) +{ + if (callId != id_) + return; + auto infos = getConferencesInfos(); + if (index < infos.size()) + participantsModel_->addParticipant(index, infos[index]); +} + +void +CurrentCall::onParticipantRemoved(const QString& callId, int index) +{ + if (callId != id_) + return; + participantsModel_->removeParticipant(index); +} + +void +CurrentCall::onParticipantUpdated(const QString& callId, int index) +{ + if (callId != id_) + return; + auto infos = getConferencesInfos(); + if (index < infos.size()) + participantsModel_->updateParticipant(index, infos[index]); +} + +void +CurrentCall::fillParticipantData(QJsonObject& participant) const +{ + // TODO: getCurrentDeviceId should be part of CurrentAccount, and Current<thing> + // should be read accessible through LRCInstance ?? + auto getCurrentDeviceId = [](const account::Info& accInfo) -> QString { + const auto& deviceList = accInfo.deviceModel->getAllDevices(); + auto devIt = std::find_if(std::cbegin(deviceList), + std::cend(deviceList), + [](const Device& dev) { return dev.isCurrent; }); + return devIt != deviceList.cend() ? devIt->id : QString(); + }; + + auto& accInfo = lrcInstance_->getCurrentAccountInfo(); + using namespace lrc::api::ParticipantsInfosStrings; + + // If both the URI and the device Id match, we set the "is local" flag + // used to filter out the participant. + // TODO: + // - This filter should always be applied, and any local streams should render + // using local sinks. Local non-preview participants should have proxy participant + // items replaced into this model using their local sink Ids. + // - The app setting should remain and be used to control whether or not the preview + // sink partipcant is rendered. + auto uri = participant[URI].toString(); + participant[ISLOCAL] = false; + if (uri == accInfo.profileInfo.uri && participant[DEVICE] == getCurrentDeviceId(accInfo)) { + participant[BESTNAME] = tr("Me"); + participant[ISLOCAL] = true; + } else { + try { + participant[BESTNAME] = accInfo.contactModel->bestNameForContact(uri); + } catch (...) { + } + } +} + +QVariantList +CurrentCall::getConferencesInfos() const +{ + QVariantList map; + try { + auto callModel = lrcInstance_->getCurrentCallModel(); + auto& participantsModel = callModel->getParticipantsInfos(id_); + for (int index = 0; index < participantsModel.getParticipants().size(); index++) { + auto participant = participantsModel.toQJsonObject(index); + fillParticipantData(participant); + map.push_back(QVariant(participant)); + } + } catch (...) { + } + return map; +} + void CurrentCall::updateCallInfo() { @@ -162,6 +259,7 @@ CurrentCall::updateCallInfo() QStringList recorders {}; if (callModel->hasCall(id_)) { auto callInfo = callModel->getCall(id_); + participantsModel_->setConferenceLayout(static_cast<int>(callInfo.layout), id_); recorders = callInfo.recordingPeers; } updateRecordingState(callModel->isRecording(id_)); @@ -230,6 +328,21 @@ CurrentCall::connectModel() this, &CurrentCall::onRecordingStateChanged, Qt::UniqueConnection); + connect(callModel, + &CallModel::participantAdded, + this, + &CurrentCall::onParticipantAdded, + Qt::UniqueConnection); + connect(callModel, + &CallModel::participantRemoved, + this, + &CurrentCall::onParticipantRemoved, + Qt::UniqueConnection); + connect(callModel, + &CallModel::participantUpdated, + this, + &CurrentCall::onParticipantUpdated, + Qt::UniqueConnection); } void diff --git a/src/app/currentcall.h b/src/app/currentcall.h index 3a0c6bdbe..7a4eea808 100644 --- a/src/app/currentcall.h +++ b/src/app/currentcall.h @@ -19,6 +19,7 @@ #include "lrcinstance.h" #include "qtutils.h" +#include "callparticipantsmodel.h" #include <QObject> #include <QString> @@ -56,6 +57,7 @@ class CurrentCall final : public QObject public: explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr); ~CurrentCall() = default; + Q_INVOKABLE QVariantList getConferencesInfos() const; private: void updateId(QString callId = {}); @@ -65,6 +67,7 @@ private: void updateRemoteRecorders(const QStringList& recorders); void updateRecordingState(bool state); void connectModel(); + void fillParticipantData(QJsonObject& participant) const; private Q_SLOTS: void onCurrentConvIdChanged(); @@ -73,10 +76,14 @@ private Q_SLOTS: void onCallInfosChanged(const QString& accountId, const QString& callId); void onCurrentCallChanged(const QString& callId); void onParticipantsChanged(const QString& callId); + void onParticipantAdded(const QString& callId, int index); + void onParticipantRemoved(const QString& callId, int index); + void onParticipantUpdated(const QString& callId, int index); void onRemoteRecordersChanged(const QString& callId, const QStringList& recorders); void onRecordingStateChanged(const QString& callId, bool state); void onShowIncomingCallView(const QString& accountId, const QString& convUid); private: LRCInstance* lrcInstance_; + QScopedPointer<CallParticipantsModel> participantsModel_; }; diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp index 530458d6b..449184f0e 100644 --- a/src/libclient/callmodel.cpp +++ b/src/libclient/callmodel.cpp @@ -1628,6 +1628,21 @@ CallModelPimpl::slotOnConferenceInfosUpdated(const QString& confId, if (it == calls.end() or not it->second) return; + // TODO: remove when the rendez-vous UI will be done + // For now, the rendez-vous account can see ongoing calls + // And must be notified when a new + QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId); + Q_FOREACH (const auto& call, callList) { + Q_EMIT linked.callAddedToConference(call, confId); + if (calls.find(call) == calls.end()) { + qWarning() << "Call not found"; + } else { + calls[call]->videoMuted = it->second->videoMuted; + calls[call]->audioMuted = it->second->audioMuted; + Q_EMIT linked.callInfosChanged(linked.owner.id, call); + } + } + auto participantIt = participantsModel.find(confId); if (participantIt == participantsModel.end()) participantIt = participantsModel @@ -1657,20 +1672,6 @@ CallModelPimpl::slotOnConferenceInfosUpdated(const QString& confId, } } - // TODO: remove when the rendez-vous UI will be done - // For now, the rendez-vous account can see ongoing calls - // And must be notified when a new - QStringList callList = CallManager::instance().getParticipantList(linked.owner.id, confId); - Q_FOREACH (const auto& call, callList) { - Q_EMIT linked.callAddedToConference(call, confId); - if (calls.find(call) == calls.end()) { - qWarning() << "Call not found"; - } else { - calls[call]->videoMuted = it->second->videoMuted; - calls[call]->audioMuted = it->second->audioMuted; - Q_EMIT linked.callInfosChanged(linked.owner.id, call); - } - } Q_EMIT linked.callInfosChanged(linked.owner.id, confId); Q_EMIT linked.participantsChanged(confId); } -- GitLab