diff --git a/src/api/call.h b/src/api/call.h index 3a2edec2a2ae1ce5f8bc505fc3c372e79335a3c7..b05d320b31ada3750184a0ab7b0ff2feb2d80d1e 100644 --- a/src/api/call.h +++ b/src/api/call.h @@ -21,6 +21,7 @@ // std #include <string> #include <ctime> +#include <chrono> // Qt #include <QObject> @@ -87,11 +88,38 @@ to_string(const call::Status& status) } } +/** + * Convert status from daemon into a Status + * @warning status is a string from the daemon, not from to_string() + * @param status + * @return + */ +static const Status +to_status(const std::string& status) +{ + if (status == "INCOMING") + return Status::INCOMING_RINGING; + else if (status == "CONNECTING") + return Status::CONNECTING; + else if (status == "RINGING") + return Status::OUTGOING_RINGING; + else if (status == "HUNGUP") + return Status::TERMINATING; + else if (status == "HOLD") + return Status::PAUSED; + else if (status == "UNHOLD" || status == "CURRENT") + return Status::IN_PROGRESS; + else if (status == "INACTIVE" || status == "BUSY") + return Status::INACTIVE; + else if (status == "OVER" || status == "FAILURE") + return Status::ENDED; + return Status::INVALID; +} struct Info { std::string id; - std::time_t startTime = 0; + std::chrono::steady_clock::time_point startTime; Status status = Status::INVALID; std::string peer; bool audioMuted = false; diff --git a/src/api/newcallmodel.h b/src/api/newcallmodel.h index ae79c135d26c466b2c997408b5a1ad0903c85c17..ec6bd2c4bc9edb20b7b50f2900f321c3daa42854 100644 --- a/src/api/newcallmodel.h +++ b/src/api/newcallmodel.h @@ -26,9 +26,18 @@ // Qt #include <qobject.h> +// Data +#include "api/call.h" +#include "api/account.h" + +namespace Video { +class Renderer; +} + namespace lrc { +class CallbacksHandler; class NewCallModelPimpl; namespace api @@ -38,8 +47,12 @@ namespace account { struct Info; } namespace call { struct Info; } class NewAccountModel; +/** + * @brief Class that manages call informations. + */ class NewCallModel : public QObject { Q_OBJECT + public: using CallInfoMap = std::map<std::string, std::shared_ptr<call::Info>>; @@ -51,20 +64,131 @@ public: VIDEO }; - NewCallModel(const account::Info& owner); + NewCallModel(const account::Info& owner, const CallbacksHandler& callbacksHandler); ~NewCallModel(); - const call::Info& createCall(const std::string& contactUri); + /** + * Create a new call with a contact + * @param url of the contact to call + * @return the call uid created + */ + std::string createCall(const std::string& url); + /** + * Get the call from its call id + * @param uid + * @return the callInfo + * @throw out_of_range exception if not found + */ + const call::Info& getCall(const std::string& uid) const; + /** + * Get the call from the peer uri + * @param uri + * @return the callInfo + * @throw out_of_range exception if not found + */ const call::Info& getCallFromURI(const std::string& uri) const; + /** + * @param callId to test + * @return true if callId is presend else false. + */ + bool hasCall(const std::string& callId); + /** + * Send a text message to a SIP call + * @param callId + * @param body of the message + */ + void sendSipMessage(const std::string& callId, const std::string& body) const; + /** + * Accept a call + * @param callId + */ + void accept(const std::string& callId) const; + /** + * Hang up a call + * @param callId + */ void hangUp(const std::string& callId) const; + /** + * Toggle audio record on a call + * @param callId + */ + void toggleAudioRecord(const std::string& callId) const; + /** + * Play DTMF in a call + * @param callId + * @param value to play + */ + void playDTMF(const std::string& callId, const std::string& value) const; + /** + * Toggle pause on a call + * @param callId + */ void togglePause(const std::string& callId) const; - void toggleMedia(const std::string& callId, const Media media) const; - void toggleRecoringdAudio(const std::string& callId) const; + /** + * Toggle a media on a call + * @param callId + * @param media {AUDIO, VIDEO} + * @param flag is muted + */ + void toggleMedia(const std::string& callId, const NewCallModel::Media media, bool flag) const; + /** + * Not implemented yet + */ void setQuality(const std::string& callId, const double quality) const; + /** + * Not implemented yet + */ void transfer(const std::string& callId, const std::string& to) const; - void addParticipant(const std::string& callId, const std::string& participant); - void removeParticipant(const std::string& callId, const std::string& participant); + /** + * Not implemented yet + */ + void addParticipant(const std::string& callId, const std::string& participant) const; + /** + * Not implemented yet + */ + void removeParticipant(const std::string& callId, const std::string& participant) const; + + /** + * @param callId + * @return the renderer linked to a call + */ + Video::Renderer* getRenderer(const std::string& callId) const; + + /** + * @param callId + * @return a human readable call duration (M:ss) + */ + std::string getFormattedCallDuration(const std::string& callId) const; + +Q_SIGNALS: + /** + * Emitted when a call state changes + * @param callId + */ + void callStatusChanged(const std::string& callId) const; + /** + * Emitted when a call starts + * @param callId + */ + void callStarted(const std::string& callId) const; + /** + * Emitted when a call is over + * @param callId + */ + void callEnded(const std::string& callId) const; + /** + * Emitted when a call is incoming + * @param callId + * @param fromId the peer uri + */ + void newIncomingCall(const std::string& callId, const std::string& fromId) const; + /** + * Emitted when the renderer starts + * @param callId + * @param renderer + */ + void remotePreviewStarted(const std::string& callId, Video::Renderer* renderer) const; private: std::unique_ptr<NewCallModelPimpl> pimpl_; diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp index 03fbdc2c1b87842e7a2d32aea8cfa165a7fbce12..dbadc19d3a0af0a92532eaedd08a91377346e635 100644 --- a/src/contactmodel.cpp +++ b/src/contactmodel.cpp @@ -344,6 +344,8 @@ ContactModelPimpl::ContactModelPimpl(const ContactModel& linked, this, &ContactModelPimpl::slotIncomingContactRequest); connect(&callbacksHandler, &CallbacksHandler::registeredNameFound, this, &ContactModelPimpl::slotRegisteredNameFound); + connect(&*linked.owner.callModel, &NewCallModel::newIncomingCall, + this, &ContactModelPimpl::slotIncomingCall); connect(&callbacksHandler, &lrc::CallbacksHandler::newAccountMessage, this, &ContactModelPimpl::slotNewAccountMessage); diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp index 2e5d3b89c77a233b461bd6bdd33dc37b3a4effd4..543c872790e1726289c5119a5b8122a91633bc93 100644 --- a/src/conversationmodel.cpp +++ b/src/conversationmodel.cpp @@ -266,7 +266,38 @@ ConversationModel::selectConversation(const std::string& uid) const return; } - // fully functional behaviour not implemented in this patch + auto& conversation = pimpl_->conversations.at(conversationIdx); + try { + auto call = owner.callModel->getCall(conversation.callId); + switch (call.status) { + case call::Status::INCOMING_RINGING: + case call::Status::OUTGOING_RINGING: + case call::Status::CONNECTING: + case call::Status::SEARCHING: + // We are currently in a call + // TODO fully functional behaviour not implemented in this patch + break; + case call::Status::PAUSED: + case call::Status::PEER_PAUSED: + case call::Status::CONNECTED: + case call::Status::IN_PROGRESS: + // We are currently receiving a call + // TODO fully functional behaviour not implemented in this patch + break; + case call::Status::INVALID: + case call::Status::OUTGOING_REQUESTED: + case call::Status::INACTIVE: + case call::Status::ENDED: + case call::Status::TERMINATING: + case call::Status::AUTO_ANSWERING: + default: + // We are not in a call, show the chatview + // TODO fully functional behaviour not implemented in this patch + break; + } + } catch (const std::out_of_range&) { + // TODO fully functional behaviour not implemented in this patch + } } void @@ -316,7 +347,7 @@ ConversationModel::placeCall(const std::string& uid) if (owner.profileInfo.type != profile::Type::SIP) { url = "ring:" + url; // Add the ring: before or it will fail. } - // at this point we are not yet ready to do a call + conversation.callId = owner.callModel->createCall(url); if (convId.empty()) { // The conversation has changed because it was with the temporary item auto contactProfileId = database::getProfileId(pimpl_->db, contactInfo.profileInfo.uri); @@ -356,7 +387,7 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body) auto contactInfo = owner.contactModel->getContact(participant); pimpl_->sendContactRequest(participant); if (not conversation.callId.empty()) - true; // requires more work in NewCallModel to send message from this point + owner.callModel->sendSipMessage(conversation.callId, body); else owner.contactModel->sendDhtMessage(contactInfo.profileInfo.uri, body); if (convId.empty()) { @@ -457,6 +488,25 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked, this, &ConversationModelPimpl::slotNewAccountMessage); connect(&callbacksHandler, &CallbacksHandler::incomingCallMessage, this, &ConversationModelPimpl::slotIncomingCallMessage); + + + // Call related + connect(&*linked.owner.callModel, &NewCallModel::newIncomingCall, + this, &ConversationModelPimpl::slotIncomingCall); + connect(&*linked.owner.contactModel, &ContactModel::incomingCallFromPending, + this, &ConversationModelPimpl::slotIncomingCall); + connect(&*linked.owner.callModel, + &lrc::api::NewCallModel::callStatusChanged, + this, + &ConversationModelPimpl::slotCallStatusChanged); + connect(&*linked.owner.callModel, + &lrc::api::NewCallModel::callStarted, + this, + &ConversationModelPimpl::slotCallStarted); + connect(&*linked.owner.callModel, + &lrc::api::NewCallModel::callEnded, + this, + &ConversationModelPimpl::slotCallEnded); } ConversationModelPimpl::~ConversationModelPimpl() @@ -701,6 +751,17 @@ void ConversationModelPimpl::slotCallEnded(const std::string& callId) { addCallMessage(callId, "🕽 Call ended"); + + // reset the callId stored in the conversation + auto it = std::find_if(conversations.begin(), conversations.end(), + [&](const conversation::Info& conversation) { + return conversation.callId == callId; + }); + + if (it != conversations.end()) { + it->callId = ""; + dirtyConversations = true; + } } void @@ -742,10 +803,10 @@ ConversationModelPimpl::slotNewAccountMessage(std::string& accountId, void ConversationModelPimpl::slotIncomingCallMessage(const std::string& callId, const std::string& from, const std::string& body) { - Q_UNUSED(callId); - Q_UNUSED(from); - Q_UNUSED(body); - // at this point we are not yet able to handle incoming call message + if (not linked.owner.callModel->hasCall(callId)) + return; + + addIncomingMessage(from, body); } void diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp index ee9a3388a389e98b11e641de77c589e67805dfb4..cc9da8b7f605cb6d37cd7da410f9fb351b805227 100644 --- a/src/newaccountmodel.cpp +++ b/src/newaccountmodel.cpp @@ -169,7 +169,7 @@ NewAccountModelPimpl::addToAccounts(const std::string& accountId) owner.profileInfo.alias, "", details["Account.type"].toStdString()); // Init models for this account - owner.callModel = std::make_unique<NewCallModel>(owner); + owner.callModel = std::make_unique<NewCallModel>(owner, callbacksHandler); owner.contactModel = std::make_unique<ContactModel>(owner, database, callbacksHandler); owner.conversationModel = std::make_unique<ConversationModel>(owner, database, callbacksHandler); owner.accountModel = &linked; diff --git a/src/newcallmodel.cpp b/src/newcallmodel.cpp index 4e29bcd2349f8120b512ba9e36a386a69d15c48c..5fd5cb7d2f210e7b7790154fd7d3e667b705e38b 100644 --- a/src/newcallmodel.cpp +++ b/src/newcallmodel.cpp @@ -16,117 +16,398 @@ * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ + #include "api/newcallmodel.h" -// LRC +// std +#include <chrono> + +// Lrc +#include "callbackshandler.h" +#include "api/conversationmodel.h" +#include "api/contact.h" +#include "api/contactmodel.h" #include "api/newaccountmodel.h" -#include "api/call.h" +#include "dbus/callmanager.h" +#include "private/videorenderermanager.h" +#include "video/renderer.h" + +// Ring daemon +#include <media_const.h> + +// Qt +#include <QObject> +#include <QString> namespace lrc { using namespace api; -class NewCallModelPimpl +class NewCallModelPimpl: public QObject { public: - NewCallModelPimpl(NewCallModel& linked); + NewCallModelPimpl(const NewCallModel& linked, const CallbacksHandler& callbacksHandler); ~NewCallModelPimpl(); - void sendMessage(const std::string& callId, const std::string& body) const; - - const NewCallModel& linked; NewCallModel::CallInfoMap calls; + const CallbacksHandler& callbacksHandler; + const NewCallModel& linked; + + /** + * key = peer's uri + * vector = chunks + * @note chunks are counted from 1 to number of parts. We use 0 to store the actual number of parts stored + */ + std::map<std::string, std::vector<std::string>> vcardsChunks; + +public Q_SLOTS: + /** + * Listen from CallbacksHandler when a call is incoming + * @param accountId account which receives the call + * @param callId + * @param fromId peer uri + */ + void slotIncomingCall(const std::string& accountId, const std::string& callId, const std::string& fromId); + /** + * Listen from CallbacksHandler when a call got a new state + * @param callId + * @param state the new state + * @param code unused + */ + void slotCallStateChanged(const std::string& callId, const std::string &state, int code); + /** + * Listen from VideoRendererManager when a Renderer starts + * @param callId + * @param renderer + */ + void slotRemotePreviewStarted(const std::string& callId, Video::Renderer* renderer); + /** + * Listen from CallbacksHandler when a VCard chunk is incoming + * @param callId + * @param from + * @param part + * @param numberOfParts + * @param payload + */ + void slotincomingVCardChunk(const std::string& callId, const std::string& from, int part, int numberOfParts, const std::string& payload); }; -NewCallModel::NewCallModel(const account::Info& owner) -: QObject() -, owner(owner) -, pimpl_(std::make_unique<NewCallModelPimpl>(*this)) +NewCallModel::NewCallModel(const account::Info& owner, const CallbacksHandler& callbacksHandler) +: owner(owner) +, pimpl_(std::make_unique<NewCallModelPimpl>(*this, callbacksHandler)) { } NewCallModel::~NewCallModel() { +} +const call::Info& +NewCallModel::getCallFromURI(const std::string& uri) const +{ + for (const auto& call: pimpl_->calls) { + if (call.second->peer == uri) { + return *call.second; + } + } + throw std::out_of_range("No call at URI " + uri); } const call::Info& -NewCallModel::createCall(const std::string& contactUri) +NewCallModel::getCall(const std::string& uid) const +{ + return *pimpl_->calls.at(uid); +} + +std::string +NewCallModel::createCall(const std::string& url) { + // do not use auto here (QDBusPendingReply<QString>) + QString callId = CallManager::instance().placeCall(owner.id.c_str(), url.c_str()); + + if (callId.isEmpty()) + qDebug() << "no call placed between (account :" << owner.id.c_str() << ", contact :" << url.c_str() << ")"; + + auto callInfo = std::make_shared<call::Info>(); + callInfo->id = callId.toStdString(); + callInfo->peer = url; + callInfo->status = call::Status::SEARCHING; + pimpl_->calls.emplace(callId.toStdString(), std::move(callInfo)); + return callId.toStdString(); } -const call::Info& -NewCallModel::getCallFromURI(const std::string& uri) const +void +NewCallModel::accept(const std::string& callId) const { - return {}; + CallManager::instance().accept(callId.c_str()); } void NewCallModel::hangUp(const std::string& callId) const { + CallManager::instance().hangUp(callId.c_str()); +} +void +NewCallModel::toggleAudioRecord(const std::string& callId) const +{ + CallManager::instance().toggleRecording(callId.c_str()); +} + +void +NewCallModel::playDTMF(const std::string& callId, const std::string& value) const +{ + auto call = pimpl_->calls.find(callId); + if (call == pimpl_->calls.end()) return; + if (pimpl_->calls[callId]->status != call::Status::IN_PROGRESS) return; + CallManager::instance().playDTMF(value.c_str()); } void NewCallModel::togglePause(const std::string& callId) const { + auto call = pimpl_->calls.find(callId); + if (call == pimpl_->calls.end()) return; + switch(pimpl_->calls[callId]->status) + { + case call::Status::PAUSED: + CallManager::instance().unhold(callId.c_str()); + break; + case call::Status::IN_PROGRESS: + CallManager::instance().hold(callId.c_str()); + break; + case call::Status::INVALID: + case call::Status::OUTGOING_REQUESTED: + case call::Status::INCOMING_RINGING: + case call::Status::OUTGOING_RINGING: + case call::Status::CONNECTING: + case call::Status::SEARCHING: + case call::Status::PEER_PAUSED: + case call::Status::INACTIVE: + case call::Status::ENDED: + case call::Status::TERMINATING: + case call::Status::CONNECTED: + case call::Status::AUTO_ANSWERING: + break; + } +} +void +NewCallModel::toggleMedia(const std::string& callId, const NewCallModel::Media media, bool flag) const +{ + switch(media) + { + case NewCallModel::Media::AUDIO: + CallManager::instance().muteLocalMedia(callId.c_str(), + DRing::Media::Details::MEDIA_TYPE_AUDIO, + flag); + break; + + case NewCallModel::Media::VIDEO: + CallManager::instance().muteLocalMedia(callId.c_str(), + DRing::Media::Details::MEDIA_TYPE_VIDEO, + flag); + break; + + case NewCallModel::Media::NONE: + default: + break; + } } void -NewCallModel::toggleMedia(const std::string& callId, const Media media) const +NewCallModel::setQuality(const std::string& callId, const double quality) const { + qDebug() << "setQuality, isn't yet implemented"; +} +void +NewCallModel::transfer(const std::string& callId, const std::string& to) const +{ + qDebug() << "transfer, isn't yet implemented"; } void -NewCallModel::toggleRecoringdAudio(const std::string& callId) const +NewCallModel::addParticipant(const std::string& callId, const std::string& participant) const { } void -NewCallModel::setQuality(const std::string& callId, const double quality) const +NewCallModel::removeParticipant(const std::string& callId, const std::string& participant) const { } -void -NewCallModel::transfer(const std::string& callId, const std::string& to) const +Video::Renderer* +NewCallModel::getRenderer(const std::string& callId) const { + #ifdef ENABLE_VIDEO + return VideoRendererManager::instance().getRenderer(callId); + #else + return nullptr; + #endif +} +std::string +NewCallModel::getFormattedCallDuration(const std::string& callId) const +{ + if (pimpl_->calls.find(callId) == pimpl_->calls.end()) return "00:00"; + auto& startTime = pimpl_->calls[callId]->startTime; + if (startTime.time_since_epoch().count() == 0) return "00:00"; + auto now = std::chrono::steady_clock::now(); + auto d = std::chrono::duration_cast<std::chrono::seconds>( + now.time_since_epoch() - startTime.time_since_epoch()).count(); + + std::string formattedString; + auto minutes = d / 60; + auto seconds = d % 60; + if (minutes > 0) { + formattedString += std::to_string(minutes) + ":"; + if (formattedString.length() == 2) { + formattedString = "0" + formattedString; + } + } else { + formattedString += "00:"; + } + if (seconds < 10) formattedString += "0"; + formattedString += std::to_string(seconds); + return formattedString; } -void -NewCallModel::addParticipant(const std::string& callId, const std::string& participant) + +NewCallModelPimpl::NewCallModelPimpl(const NewCallModel& linked, const CallbacksHandler& callbacksHandler) +: linked(linked) +, callbacksHandler(callbacksHandler) +{ + // TODO init call list and conferences if client crash but not the daemon in call. + connect(&callbacksHandler, &CallbacksHandler::incomingCall, this, &NewCallModelPimpl::slotIncomingCall); + connect(&callbacksHandler, &CallbacksHandler::callStateChanged, this, &NewCallModelPimpl::slotCallStateChanged); + connect(&VideoRendererManager::instance(), &VideoRendererManager::remotePreviewStarted, this, &NewCallModelPimpl::slotRemotePreviewStarted); + connect(&callbacksHandler, &CallbacksHandler::incomingVCardChunk, this, &NewCallModelPimpl::slotincomingVCardChunk); +} + +NewCallModelPimpl::~NewCallModelPimpl() { } void -NewCallModel::removeParticipant(const std::string& callId, const std::string& participant) +NewCallModel::sendSipMessage(const std::string& callId, const std::string& body) const { + QMap<QString, QString> payloads; + payloads["text/plain"] = body.c_str(); + CallManager::instance().sendTextMessage(callId.c_str(), payloads, true /* not used */); } -NewCallModelPimpl::NewCallModelPimpl(NewCallModel& linked) -: linked(linked) +void +NewCallModelPimpl::slotIncomingCall(const std::string& accountId, const std::string& callId, const std::string& fromId) { + if (linked.owner.id != accountId) return; + + qDebug() << "NewCallModelPimpl::slotIncomingCall"; + + auto callInfo = std::make_shared<call::Info>(); + callInfo->id = callId; + callInfo->peer = fromId; + callInfo->status = call::Status::INCOMING_RINGING; + calls.emplace(callId, std::move(callInfo)); + emit linked.newIncomingCall(fromId, callId); } -NewCallModelPimpl::~NewCallModelPimpl() +void +NewCallModelPimpl::slotCallStateChanged(const std::string& callId, const std::string& state, int code) { + Q_UNUSED(code) + if (calls.find(callId) != calls.end()) { + if (state == "CONNECTING") { + calls[callId]->status = call::Status::CONNECTING; + } else if (state == "RINGING") { + calls[callId]->status = call::Status::OUTGOING_RINGING; + } else if (state == "HUNGUP") { + calls[callId]->status = call::Status::TERMINATING; + } else if (state == "FAILURE" || state == "OVER") { + if (calls[callId]->startTime.time_since_epoch().count() != 0) { + emit linked.callEnded(callId); + } + calls[callId]->status = call::Status::ENDED; + } else if (state == "INACTIVE") { + calls[callId]->status = call::Status::INACTIVE; + } else if (state == "CURRENT") { + if (calls[callId]->startTime.time_since_epoch().count() == 0) { + calls[callId]->startTime = std::chrono::steady_clock::now(); + emit linked.callStarted(callId); + } + calls[callId]->status = call::Status::IN_PROGRESS; + } else if (state == "HOLD") { + calls[callId]->status = call::Status::PAUSED; + } + qDebug() << "slotCallStateChanged, call:" << callId.c_str() << " - state: " << state.c_str(); + emit linked.callStatusChanged(callId); + } +} +void +NewCallModelPimpl::slotRemotePreviewStarted(const std::string& callId, Video::Renderer* renderer) +{ + emit linked.remotePreviewStarted(callId, renderer); } void -NewCallModelPimpl::sendMessage(const std::string& callId, const std::string& body) const +NewCallModelPimpl::slotincomingVCardChunk(const std::string& callId, + const std::string& from, + int part, + int numberOfParts, + const std::string& payload) { + auto it = calls.find(callId); + + if (it != calls.end()) { + auto it_2 = vcardsChunks.find(from); + if (it_2 != vcardsChunks.end()) { + vcardsChunks[from][part-1] = payload; + + if ( not std::any_of(vcardsChunks[from].begin(), vcardsChunks[from].end(), + [](const auto& s) { return s.empty(); }) ) { + + profile::Info profileInfo; + profileInfo.uri = from; + profileInfo.type = profile::Type::RING; + std::string vcardPhoto; + + for (auto& chunk : vcardsChunks[from]) + vcardPhoto += chunk; + + for (auto& e : QString(vcardPhoto.c_str()).split( "\n" )) + if (e.contains("PHOTO")) + profileInfo.avatar = e.split( ":" )[1].toStdString(); + else if (e.contains("FN")) + profileInfo.alias = e.split( ":" )[1].toStdString(); + + contact::Info contactInfo; + contactInfo.profileInfo = profileInfo; + + linked.owner.contactModel->addContact(contactInfo); + vcardsChunks.erase(from); // Transfer is finish, we don't want to reuse this entry. + } + } else { + vcardsChunks[from] = std::vector<std::string>(numberOfParts); + vcardsChunks[from][part-1] = payload; + } + + } +} + +bool +NewCallModel::hasCall(const std::string& callId) +{ + return pimpl_->calls.find(callId) != pimpl_->calls.end(); } } // namespace lrc diff --git a/src/private/videorenderermanager.cpp b/src/private/videorenderermanager.cpp index 5cc52a62d3901b746416fd5c7a28557aa659598d..a811dd77eec0581fb688dd26071e9f7807c76eb3 100644 --- a/src/private/videorenderermanager.cpp +++ b/src/private/videorenderermanager.cpp @@ -113,6 +113,14 @@ Video::Renderer* VideoRendererManager::getRenderer(const Call* call) const return d_ptr->m_hRenderers[call->dringId().toLatin1()]; } +// helper for the new model +Video::Renderer* VideoRendererManager::getRenderer(const std::string& callId) const +{ + return (d_ptr->m_hRenderers.contains(callId.c_str())) + ? d_ptr->m_hRenderers[callId.c_str()] + : nullptr; +} + ///Get the video preview Renderer Video::Renderer* VideoRendererManager::previewRenderer() { @@ -258,8 +266,10 @@ void VideoRendererManagerPrivate::startedDecoding(const QString& id, const QStri Call* c = CallModel::instance().getCall(id); - if (c) + if (c) { + emit q_ptr->remotePreviewStarted(id.toStdString(), r); c->d_ptr->registerRenderer(r); + } else { //We don't have the conference yet QObject::connect(&CallModel::instance(), &CallModel::conferenceCreated, [=](Call* conf) { diff --git a/src/private/videorenderermanager.h b/src/private/videorenderermanager.h index b16699390bdb8a15b5e23225fa66b323437c4cd5..5605e561604428e55cc7a387a843036bcc00f7e7 100644 --- a/src/private/videorenderermanager.h +++ b/src/private/videorenderermanager.h @@ -54,6 +54,7 @@ public: //Helpers Video::Renderer* getRenderer(const Call* call) const; + Video::Renderer* getRenderer(const std::string& callId) const; void setBufferSize(uint size); void switchDevice(const Video::Device* device) const; @@ -75,6 +76,6 @@ Q_SIGNALS: void previewStateChanged(bool startStop); void previewStarted(Video::Renderer* renderer); void previewStopped(Video::Renderer* renderer); + void remotePreviewStarted(const std::string& callId, Video::Renderer* renderer); }; -