mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
1435 lines
50 KiB
C++
1435 lines
50 KiB
C++
/*
|
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
|
*
|
|
* This file is part of linphone-desktop
|
|
* (see https://www.linphone.org).
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* 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 "CallModel.hpp"
|
|
|
|
#include <QDateTime>
|
|
#include <QQuickWindow>
|
|
#include <QRegularExpression>
|
|
#include <QTimer>
|
|
|
|
#include "app/App.hpp"
|
|
#include "CallListener.hpp"
|
|
#include "components/calls/CallsListModel.hpp"
|
|
#include "components/chat-room/ChatRoomInitializer.hpp"
|
|
#include "components/chat-room/ChatRoomListener.hpp"
|
|
#include "components/chat-room/ChatRoomModel.hpp"
|
|
#include "components/conference/ConferenceModel.hpp"
|
|
#include "components/conferenceInfo/ConferenceInfoModel.hpp"
|
|
#include "components/contact/ContactModel.hpp"
|
|
#include "components/contacts/ContactsListModel.hpp"
|
|
#include "components/core/CoreHandlers.hpp"
|
|
#include "components/core/CoreManager.hpp"
|
|
#include "components/notifier/Notifier.hpp"
|
|
#include "components/settings/AccountSettingsModel.hpp"
|
|
#include "components/settings/SettingsModel.hpp"
|
|
#include "components/timeline/TimelineListModel.hpp"
|
|
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
|
|
#include "utils/MediastreamerUtils.hpp"
|
|
#include "utils/Utils.hpp"
|
|
|
|
#include "linphone/api/c-search-result.h"
|
|
|
|
// =============================================================================
|
|
|
|
using namespace std;
|
|
|
|
namespace {
|
|
constexpr char AutoAnswerObjectName[] = "auto-answer-timer";
|
|
}
|
|
void CallModel::connectTo(CallListener * listener){
|
|
connect(listener, &CallListener::remoteRecording, this, &CallModel::onRemoteRecording);
|
|
}
|
|
|
|
CallModel::CallModel (shared_ptr<linphone::Call> call){
|
|
CoreManager *coreManager = CoreManager::getInstance();
|
|
SettingsModel *settings = coreManager->getSettingsModel();
|
|
|
|
connect(this, &CallModel::callIdChanged, this, &CallModel::chatRoomModelChanged);// When the call Id change, the chat room change.
|
|
connect(this, &CallModel::encryptionChanged, this, &CallModel::securityUpdated);
|
|
connect(this, &CallModel::isPQZrtpChanged, this, &CallModel::securityUpdated);
|
|
connect(this, &CallModel::securityUpdated, this, &CallModel::onSecurityUpdated);
|
|
|
|
|
|
mCall = call;
|
|
if(mCall)
|
|
mCall->setData("call-model", *this);
|
|
updateIsInConference();
|
|
if(mCall && mCall->getState() != linphone::Call::State::End) {
|
|
mCallListener = std::make_shared<CallListener>();
|
|
connectTo(mCallListener.get());
|
|
mCall->addListener(mCallListener);
|
|
auto callParams = mCall->getParams();
|
|
auto currentParams = mCall->getCurrentParams();
|
|
if(currentParams)
|
|
mEncryption = static_cast<CallEncryption>(currentParams->getMediaEncryption());
|
|
mConferenceVideoLayout = LinphoneEnums::fromLinphone(callParams->getConferenceVideoLayout());
|
|
if(mConferenceVideoLayout == LinphoneEnums::ConferenceLayoutGrid && !callParams->videoEnabled())
|
|
mConferenceVideoLayout = LinphoneEnums::ConferenceLayoutAudioOnly;
|
|
if(mCall->getConference()){
|
|
if( mConferenceVideoLayout == LinphoneEnums::ConferenceLayoutGrid)
|
|
settings->setCameraMode(settings->getGridCameraMode());
|
|
else
|
|
settings->setCameraMode(settings->getActiveSpeakerCameraMode());
|
|
}else
|
|
settings->setCameraMode(settings->getCallCameraMode());
|
|
|
|
}
|
|
|
|
// Deal with auto-answer.
|
|
if (!isOutgoing()) {
|
|
|
|
|
|
if (settings->getAutoAnswerStatus()) {
|
|
QTimer *timer = new QTimer(this);
|
|
timer->setInterval(settings->getAutoAnswerDelay());
|
|
timer->setSingleShot(true);
|
|
timer->setObjectName(AutoAnswerObjectName);
|
|
|
|
QObject::connect(timer, &QTimer::timeout, this, &CallModel::acceptWithAutoAnswerDelay);
|
|
timer->start();
|
|
}
|
|
}
|
|
|
|
CoreHandlers *coreHandlers = coreManager->getHandlers().get();
|
|
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &CallModel::handleCallStateChanged );
|
|
QObject::connect(coreHandlers, &CoreHandlers::callEncryptionChanged, this, &CallModel::handleCallEncryptionChanged );
|
|
|
|
// Update fields and make a search to know to who the call belong
|
|
mMagicSearch = CoreManager::getInstance()->getCore()->createMagicSearch();
|
|
mSearch = std::make_shared<SearchListener>();
|
|
QObject::connect(mSearch.get(), SIGNAL(searchReceived(std::list<std::shared_ptr<linphone::SearchResult>> )), this, SLOT(searchReceived(std::list<std::shared_ptr<linphone::SearchResult>>)));
|
|
mMagicSearch->addListener(mSearch);
|
|
|
|
if(mCall) {
|
|
mRemoteAddress = mCall->getRemoteAddress()->clone();
|
|
if(mCall->getConference()) {
|
|
mConferenceModel = ConferenceModel::create(mCall->getConference());
|
|
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
|
|
connect(mConferenceModel.get(), &ConferenceModel::localScreenSharingChanged, this, &CallModel::onLocalScreenSharingChanged);
|
|
}
|
|
auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress());
|
|
if( conferenceInfo ){
|
|
mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo);
|
|
}
|
|
mMagicSearch->getContactsListAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain(), (int)linphone::MagicSearch::Source::LdapServers | (int)linphone::MagicSearch::Source::Friends, linphone::MagicSearch::Aggregation::Friend);
|
|
|
|
}
|
|
}
|
|
|
|
CallModel::~CallModel () {
|
|
mMagicSearch->removeListener(mSearch);
|
|
removeCall();
|
|
}
|
|
|
|
void CallModel::removeCall(){
|
|
if(mCall){
|
|
mCall->removeListener(mCallListener);
|
|
mConferenceModel = nullptr;// Ordering deletion.
|
|
mConferenceInfoModel = nullptr;
|
|
mCall->unsetData("call-model");
|
|
mCall = nullptr;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::getPeerAddress () const {
|
|
return Utils::coreStringToAppString(mRemoteAddress->asStringUriOnly());
|
|
}
|
|
|
|
QString CallModel::getLocalAddress () const {
|
|
return mCall ? Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asStringUriOnly()) : "";
|
|
}
|
|
|
|
QString CallModel::getFullPeerAddress () const {
|
|
return Utils::coreStringToAppString(mRemoteAddress->asString());
|
|
}
|
|
|
|
QString CallModel::getFullLocalAddress () const {
|
|
return mCall ? Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asString()) : "";
|
|
}
|
|
|
|
std::shared_ptr<linphone::Address> CallModel::getConferenceAddress () const{
|
|
std::shared_ptr<linphone::Address> conferenceAddress;
|
|
if(mCall){
|
|
auto remoteContact = mCall->getRemoteContact();
|
|
|
|
if (mCall->getDir() == linphone::Call::Dir::Incoming){
|
|
if( remoteContact != "" )
|
|
conferenceAddress = Utils::interpretUrl(Utils::coreStringToAppString(remoteContact));
|
|
}else
|
|
conferenceAddress = mCall->getRemoteAddress()->clone();
|
|
}
|
|
return conferenceAddress;
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
|
|
ContactModel *CallModel::getContactModel() const{
|
|
QString cleanedAddress = mCall ? Utils::cleanSipAddress(Utils::coreStringToAppString(mCall->getRemoteAddress()->asString())) : "";
|
|
return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(cleanedAddress).get();
|
|
}
|
|
|
|
ChatRoomModel * CallModel::getChatRoomModel(){
|
|
if(mCall && mCall->getCallLog()->getCallId() != "" && !isConference()){// No chat rooms for conference (TODO)
|
|
auto currentParams = mCall->getCurrentParams();
|
|
bool isEncrypted = currentParams->getMediaEncryption() != linphone::MediaEncryption::None;
|
|
SettingsModel * settingsModel = CoreManager::getInstance()->getSettingsModel();
|
|
if(mChatRoom){// We already created a chat room.
|
|
if( mChatRoom->getState() == linphone::ChatRoom::State::Created)
|
|
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get();
|
|
else// Chat room is not yet created.
|
|
return nullptr;
|
|
}
|
|
if( (settingsModel->getSecureChatEnabled() &&
|
|
(!settingsModel->getStandardChatEnabled() || (settingsModel->getStandardChatEnabled() && isEncrypted))
|
|
)){// Make a secure chat
|
|
std::shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
|
std::shared_ptr<linphone::ChatRoomParams> params = core->createDefaultChatRoomParams();
|
|
auto callLog = mCall->getCallLog();
|
|
auto callLocalAddress = callLog->getLocalAddress();
|
|
std::list<std::shared_ptr<linphone::Address>> participants;
|
|
// Copy parameters
|
|
params->enableEncryption(true);
|
|
auto conference = mCall->getConference();
|
|
if(conference){// This is a group
|
|
params->enableGroup(true);
|
|
params->setSubject(conference->getSubject());
|
|
auto conferenceParaticipants = conference->getParticipantList();
|
|
for(auto p : conferenceParaticipants){
|
|
participants.push_back(p->getAddress()->clone());
|
|
}
|
|
}else{
|
|
params->enableGroup(false);
|
|
participants.push_back(mCall->getRemoteAddress()->clone());
|
|
}
|
|
if( params->getSubject() == "") // A linphone::ChatRoom::Backend::FlexisipChat need a subject.
|
|
params->setSubject("Dummy Subject");
|
|
|
|
mChatRoom = core->searchChatRoom(params, callLocalAddress
|
|
, nullptr
|
|
, participants);
|
|
if(mChatRoom)
|
|
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get();
|
|
else{// Wait for creation. Secure chat rooms cannot be used before being created.
|
|
mChatRoom = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants);
|
|
auto initializer = ChatRoomInitializer::create(mChatRoom);
|
|
connect(initializer.get(), &ChatRoomInitializer::finished, this, &CallModel::onChatRoomInitialized, Qt::DirectConnection);
|
|
ChatRoomInitializer::start(initializer);
|
|
return nullptr;
|
|
}
|
|
}else
|
|
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mCall->getChatRoom(), true).get();
|
|
}else
|
|
return nullptr;
|
|
}
|
|
|
|
ConferenceModel * CallModel::getConferenceModel(){
|
|
return mConferenceModel.get();
|
|
}
|
|
|
|
ConferenceInfoModel * CallModel::getConferenceInfoModel(){
|
|
return mConferenceInfoModel.get();
|
|
}
|
|
|
|
QSharedPointer<ConferenceModel> CallModel::getConferenceSharedModel(){
|
|
if(mCall->getState() != linphone::Call::State::End && mCall->getConference() && !mConferenceModel){
|
|
mConferenceModel = ConferenceModel::create(mCall->getConference());
|
|
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
|
|
connect(mConferenceModel.get(), &ConferenceModel::localScreenSharingChanged, this, &CallModel::onLocalScreenSharingChanged);
|
|
emit conferenceModelChanged();
|
|
}
|
|
return mConferenceModel;
|
|
}
|
|
|
|
bool CallModel::isInConference () const{
|
|
return mIsInConference;
|
|
}
|
|
|
|
bool CallModel::isConference () const{
|
|
// Check status to avoid crash when requesting a conference on an ended call.
|
|
bool isConf = false;
|
|
if(mCall){
|
|
// Do not call getConference on Ended status.
|
|
isConf = (getStatus() != CallStatusEnded && mCall->getConference() != nullptr) || mConferenceInfoModel != nullptr;
|
|
|
|
if(!isConf){// Check special cases for Linphone. Having conf-id for a conference URI is not standard.
|
|
auto remoteAddress = mCall->getRemoteAddress();
|
|
if( remoteAddress->getDomain() == Constants::LinphoneDomain){
|
|
isConf = remoteAddress->hasUriParam("conf-id");
|
|
}
|
|
}
|
|
}
|
|
|
|
return isConf;
|
|
}
|
|
|
|
bool CallModel::isOneToOne() const{
|
|
return !isConference();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
void CallModel::setRecordFile (const shared_ptr<linphone::CallParams> &callParams) {
|
|
callParams->setRecordFile(Utils::appStringToCoreString(
|
|
CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder()
|
|
.append(generateSavedFilename())
|
|
.append(".mkv")
|
|
));
|
|
}
|
|
|
|
void CallModel::setRecordFile (const shared_ptr<linphone::CallParams> &callParams, const QString &to) {
|
|
const QString from(
|
|
Utils::coreStringToAppString(
|
|
CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->getUsername()
|
|
)
|
|
);
|
|
|
|
callParams->setRecordFile(Utils::appStringToCoreString(
|
|
CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder()
|
|
.append(generateSavedFilename(from, to))
|
|
.append(".mkv")
|
|
));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::updateStats (const shared_ptr<const linphone::CallStats> &callStats) {
|
|
switch (callStats->getType()) {
|
|
case linphone::StreamType::Text:
|
|
case linphone::StreamType::Unknown:
|
|
break;
|
|
|
|
case linphone::StreamType::Audio:
|
|
if( callStats)
|
|
isPQZrtp(callStats->isZrtpKeyAgreementAlgoPostQuantum() ? CallPQStateOn : CallPQStateOff);
|
|
updateStats(callStats, mAudioStats);
|
|
updateEncrypionStats(callStats, mEncryptionStats);
|
|
break;
|
|
case linphone::StreamType::Video:
|
|
updateStats(callStats, mVideoStats);
|
|
break;
|
|
}
|
|
|
|
emit statsUpdated();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
float CallModel::getSpeakerVolumeGain () const {
|
|
float gain = mCall ? mCall->getSpeakerVolumeGain() : 0;
|
|
if( gain < 0)
|
|
gain = CoreManager::getInstance()->getSettingsModel()->getPlaybackGain();
|
|
return gain;
|
|
}
|
|
|
|
void CallModel::setSpeakerVolumeGain (float volume) {
|
|
Q_ASSERT(volume >= 0.0f && volume <= 1.0f);
|
|
float oldGain = getSpeakerVolumeGain();
|
|
if( mCall && mCall->getSpeakerVolumeGain() >= 0)
|
|
mCall->setSpeakerVolumeGain(volume);
|
|
else
|
|
CoreManager::getInstance()->getSettingsModel()->setPlaybackGain(volume);
|
|
if( (int)(oldGain * 1000) != (int)(volume*1000))
|
|
emit speakerVolumeGainChanged(getSpeakerVolumeGain());
|
|
}
|
|
|
|
float CallModel::getMicroVolumeGain () const {
|
|
float gain = mCall ? mCall->getMicrophoneVolumeGain() : 0.0;
|
|
if( gain < 0)
|
|
gain = CoreManager::getInstance()->getSettingsModel()->getCaptureGain();
|
|
return gain;
|
|
}
|
|
|
|
void CallModel::setMicroVolumeGain (float volume) {
|
|
Q_ASSERT(volume >= 0.0f && volume <= 1.0f);
|
|
float oldGain = getMicroVolumeGain();
|
|
if(mCall && mCall->getMicrophoneVolumeGain() >= 0)
|
|
mCall->setMicrophoneVolumeGain(volume);
|
|
else
|
|
CoreManager::getInstance()->getSettingsModel()->setCaptureGain(volume);
|
|
if( (int)(oldGain * 1000) != (int)(volume*1000))
|
|
emit microVolumeGainChanged(getMicroVolumeGain());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::notifyCameraFirstFrameReceived (unsigned int width, unsigned int height) {
|
|
if (mNotifyCameraFirstFrameReceived) {
|
|
mNotifyCameraFirstFrameReceived = false;
|
|
emit cameraFirstFrameReceived(width, height);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::accept () {
|
|
accept(false);
|
|
}
|
|
|
|
void CallModel::acceptWithVideo () {
|
|
accept(true);
|
|
}
|
|
|
|
void CallModel::terminate () {
|
|
mEndByUser = true;
|
|
CoreManager *core = CoreManager::getInstance();
|
|
core->lockVideoRender();
|
|
if(mCall)
|
|
mCall->terminate();
|
|
core->unlockVideoRender();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::askForTransfer () {
|
|
CoreManager::getInstance()->getCallsListModel()->askForTransfer(this);
|
|
}
|
|
|
|
void CallModel::askForAttendedTransfer () {
|
|
CoreManager::getInstance()->getCallsListModel()->askForAttendedTransfer(this);
|
|
}
|
|
|
|
bool CallModel::transferTo (const QString &sipAddress) {
|
|
bool failure = mCall ? !!mCall->transferTo(Utils::interpretUrl(sipAddress)) : false;
|
|
if (failure)
|
|
qWarning() << QStringLiteral("Unable to transfer: `%1`.").arg(sipAddress);
|
|
return !failure;
|
|
}
|
|
|
|
bool CallModel::transferToAnother (const QString &peerAddress) {
|
|
qInfo() << QStringLiteral("Transferring to another: `%1`.").arg(peerAddress);
|
|
CallModel *transferCallModel = CoreManager::getInstance()->getCallsListModel()->findCallModelFromPeerAddress(peerAddress);
|
|
if (transferCallModel == nullptr) {
|
|
qWarning() << QStringLiteral("Unable to transfer to another: `%1` (peer not found)").arg(peerAddress);
|
|
return false;
|
|
}
|
|
bool failure = mCall ? !!transferCallModel->mCall->transferToAnother(mCall) : false;
|
|
if (failure)
|
|
qWarning() << QStringLiteral("Unable to transfer to another: `%1` (transfer failed)").arg(peerAddress);
|
|
return !failure;
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::acceptVideoRequest () {
|
|
if(mCall) {
|
|
shared_ptr<linphone::CallParams> params = CoreManager::getInstance()->getCore()->createCallParams(mCall);
|
|
params->enableVideo(CoreManager::getInstance()->getSettingsModel()->getVideoEnabled());
|
|
params->setVideoDirection(linphone::MediaDirection::SendRecv);// Force for symmetric case
|
|
mCall->acceptUpdate(params);
|
|
}
|
|
}
|
|
|
|
void CallModel::rejectVideoRequest () {
|
|
if(mCall) {
|
|
shared_ptr<linphone::CallParams> params = CoreManager::getInstance()->getCore()->createCallParams(mCall);
|
|
params->enableVideo(false);
|
|
mCall->acceptUpdate(params);
|
|
}
|
|
}
|
|
|
|
void CallModel::takeSnapshot () {
|
|
static QString oldName;
|
|
QString newName(generateSavedFilename().append(".jpg"));
|
|
|
|
if (newName == oldName) {
|
|
qWarning() << QStringLiteral("Unable to take snapshot. Wait one second.");
|
|
return;
|
|
}
|
|
oldName = newName;
|
|
|
|
qInfo() << QStringLiteral("Take snapshot of call:") << this;
|
|
|
|
const QString filePath(CoreManager::getInstance()->getSettingsModel()->getSavedScreenshotsFolder().append(newName));
|
|
if(mCall)
|
|
mCall->takeVideoSnapshot(Utils::appStringToCoreString(filePath));
|
|
App::getInstance()->getNotifier()->notifySnapshotWasTaken(filePath);
|
|
}
|
|
|
|
void CallModel::startRecording () {
|
|
if (mRecording)
|
|
return;
|
|
|
|
qInfo() << QStringLiteral("Start recording call:") << this;
|
|
if(mCall)
|
|
mCall->startRecording();
|
|
mRecording = true;
|
|
|
|
emit recordingChanged(true);
|
|
}
|
|
|
|
void CallModel::stopRecording () {
|
|
if (!mRecording)
|
|
return;
|
|
|
|
qInfo() << QStringLiteral("Stop recording call:") << this;
|
|
|
|
mRecording = false;
|
|
if(mCall) {
|
|
mCall->stopRecording();
|
|
|
|
App::getInstance()->getNotifier()->notifyRecordingCompleted(
|
|
Utils::coreStringToAppString(mCall->getParams()->getRecordFile())
|
|
);
|
|
}
|
|
emit recordingChanged(false);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::handleCallEncryptionChanged (const shared_ptr<linphone::Call> &call) {
|
|
if (call == mCall && mCall->getState() != linphone::Call::State::End){
|
|
if(!setEncryption(static_cast<CallEncryption>(mCall->getCurrentParams()->getMediaEncryption())))
|
|
emit securityUpdated();
|
|
}
|
|
}
|
|
|
|
void CallModel::handleCallStateChanged (const shared_ptr<linphone::Call> &call, linphone::Call::State state) {
|
|
if (call != mCall)
|
|
return;
|
|
|
|
updateIsInConference();
|
|
if(!mConferenceInfoModel){// Check if conferenceInfo has been set.
|
|
auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress());
|
|
if( conferenceInfo ){
|
|
mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo);
|
|
getConferenceSharedModel(); // emit new conferenceModel if it was not created.
|
|
emit conferenceInfoModelChanged();
|
|
}
|
|
}
|
|
switch (state) {
|
|
case linphone::Call::State::Error:
|
|
case linphone::Call::State::End:
|
|
setCallErrorFromReason(call->getReason());
|
|
stopAutoAnswerTimer();
|
|
stopRecording();
|
|
setPausedByRemote(false);
|
|
break;
|
|
|
|
case linphone::Call::State::StreamsRunning: {
|
|
getConferenceSharedModel();
|
|
if (!mWasConnected && CoreManager::getInstance()->getSettingsModel()->getAutomaticallyRecordCalls()) {
|
|
startRecording();
|
|
mWasConnected = true;
|
|
}
|
|
setPausedByRemote(false);
|
|
updateConferenceVideoLayout();
|
|
setCallId(QString::fromStdString(mCall->getCallLog()->getCallId()));
|
|
updateEncryption();
|
|
break;
|
|
}
|
|
case linphone::Call::State::Connected: getConferenceSharedModel();
|
|
case linphone::Call::State::Referred:
|
|
case linphone::Call::State::Released:
|
|
setPausedByRemote(false);
|
|
break;
|
|
|
|
case linphone::Call::State::PausedByRemote:
|
|
mNotifyCameraFirstFrameReceived = true;
|
|
setPausedByRemote(true);
|
|
break;
|
|
|
|
case linphone::Call::State::Pausing:
|
|
mNotifyCameraFirstFrameReceived = true;
|
|
mPausedByUser = true;
|
|
break;
|
|
|
|
case linphone::Call::State::Resuming:
|
|
mPausedByUser = false;
|
|
break;
|
|
|
|
case linphone::Call::State::UpdatedByRemote:
|
|
qDebug() << "UpdatedByRemote : " << (mCall ? QString( "Video enabled ? CurrentParams:") + mCall->getCurrentParams()->videoEnabled() + QString(", RemoteParams:")+mCall->getRemoteParams()->videoEnabled() : " call NULL");
|
|
if (mCall && !mCall->getCurrentParams()->videoEnabled() && mCall->getRemoteParams()->videoEnabled()) {
|
|
mCall->deferUpdate();
|
|
emit videoRequested();
|
|
}
|
|
break;
|
|
|
|
case linphone::Call::State::Idle:
|
|
case linphone::Call::State::IncomingReceived:
|
|
case linphone::Call::State::OutgoingInit:
|
|
case linphone::Call::State::OutgoingProgress:
|
|
case linphone::Call::State::OutgoingRinging:
|
|
case linphone::Call::State::OutgoingEarlyMedia:
|
|
case linphone::Call::State::Paused:
|
|
case linphone::Call::State::IncomingEarlyMedia:
|
|
case linphone::Call::State::Updating:
|
|
case linphone::Call::State::EarlyUpdatedByRemote:
|
|
case linphone::Call::State::EarlyUpdating:
|
|
case linphone::Call::State::PushIncomingReceived:
|
|
break;
|
|
}
|
|
emit statusChanged(getStatus());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::accept (bool withVideo) {
|
|
stopAutoAnswerTimer();
|
|
CoreManager *coreManager = CoreManager::getInstance();
|
|
|
|
QQuickWindow *callsWindow = App::getInstance()->getCallsWindow();
|
|
if (callsWindow) {
|
|
if (coreManager->getSettingsModel()->getKeepCallsWindowInBackground()) {
|
|
if (!callsWindow->isVisible())
|
|
callsWindow->showMinimized();
|
|
} else
|
|
App::smartShowWindow(callsWindow);
|
|
}
|
|
shared_ptr<linphone::Core> core = coreManager->getCore();
|
|
if(mCall) {
|
|
shared_ptr<linphone::CallParams> params = core->createCallParams(mCall);
|
|
params->enableVideo(withVideo && CoreManager::getInstance()->getSettingsModel()->getVideoEnabled());
|
|
params->setVideoDirection(withVideo ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly);
|
|
setRecordFile(params);
|
|
auto localAddress = mCall->getCallLog()->getLocalAddress();
|
|
for(auto account : coreManager->getAccountList()){
|
|
if( account->getParams()->getIdentityAddress()->weakEqual(localAddress)){
|
|
params->setAccount(account);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mCall->acceptWithParams(params);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::updateIsInConference () {
|
|
if (mIsInConference != (mCall && mCall->getState() != linphone::Call::State::End && mCall->getCurrentParams()->getLocalConferenceMode() )) {
|
|
mIsInConference = !mIsInConference;
|
|
}
|
|
emit isInConferenceChanged(mIsInConference);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::stopAutoAnswerTimer () const {
|
|
QTimer *timer = findChild<QTimer *>(AutoAnswerObjectName, Qt::FindDirectChildrenOnly);
|
|
if (timer) {
|
|
timer->stop();
|
|
timer->deleteLater();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CallModel::CallStatus CallModel::getStatus () const {
|
|
CallModel::CallStatus status;
|
|
if(mCall){
|
|
switch (mCall->getState()) {
|
|
case linphone::Call::State::Connected:
|
|
case linphone::Call::State::StreamsRunning:
|
|
status = CallStatusConnected;
|
|
break;
|
|
|
|
case linphone::Call::State::End:
|
|
case linphone::Call::State::Error:
|
|
case linphone::Call::State::Referred:
|
|
case linphone::Call::State::Released:
|
|
status = CallStatusEnded;
|
|
break;
|
|
|
|
case linphone::Call::State::Paused:
|
|
case linphone::Call::State::PausedByRemote:
|
|
case linphone::Call::State::Pausing:
|
|
case linphone::Call::State::Resuming:
|
|
status = CallStatusPaused;
|
|
break;
|
|
|
|
case linphone::Call::State::Updating:
|
|
case linphone::Call::State::UpdatedByRemote:
|
|
status = mPausedByRemote ? CallStatusPaused : CallStatusConnected;
|
|
break;
|
|
|
|
case linphone::Call::State::EarlyUpdatedByRemote:
|
|
case linphone::Call::State::EarlyUpdating:
|
|
case linphone::Call::State::Idle:
|
|
case linphone::Call::State::IncomingEarlyMedia:
|
|
case linphone::Call::State::IncomingReceived:
|
|
case linphone::Call::State::OutgoingEarlyMedia:
|
|
case linphone::Call::State::OutgoingInit:
|
|
case linphone::Call::State::OutgoingProgress:
|
|
case linphone::Call::State::OutgoingRinging:
|
|
default:{
|
|
status = mCall->getDir() == linphone::Call::Dir::Incoming ? CallStatusIncoming : CallStatusOutgoing;
|
|
}
|
|
}
|
|
}else
|
|
status = CallStatusIdle;
|
|
return status;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::acceptWithAutoAnswerDelay () {
|
|
CoreManager *coreManager = CoreManager::getInstance();
|
|
SettingsModel *settingsModel = coreManager->getSettingsModel();
|
|
|
|
// Use auto-answer if activated and it's the only call.
|
|
if (settingsModel->getAutoAnswerStatus() && coreManager->getCore()->getCallsNb() == 1) {
|
|
if (mCall && mCall->getRemoteParams()->videoEnabled() && settingsModel->getAutoAnswerVideoStatus() && settingsModel->getVideoEnabled())
|
|
acceptWithVideo();
|
|
else
|
|
accept();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::getCallError () const {
|
|
return mCallError;
|
|
}
|
|
|
|
void CallModel::setCallErrorFromReason (linphone::Reason reason) {
|
|
switch (reason) {
|
|
case linphone::Reason::Declined:
|
|
mCallError = tr("callErrorDeclined");
|
|
break;
|
|
case linphone::Reason::NotFound:
|
|
mCallError = tr("callErrorNotFound");
|
|
break;
|
|
case linphone::Reason::Busy:
|
|
mCallError = tr("callErrorBusy");
|
|
break;
|
|
case linphone::Reason::NotAcceptable:
|
|
mCallError = tr("callErrorNotAcceptable");
|
|
break;
|
|
case linphone::Reason::None:
|
|
if(!mEndByUser)
|
|
mCallError = tr("callErrorHangUp");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!mCallError.isEmpty())
|
|
qInfo() << QStringLiteral("Call terminated with error (%1):").arg(mCallError) << this;
|
|
|
|
emit callErrorChanged(mCallError);
|
|
}
|
|
|
|
void CallModel::setCallId(const QString& callId){
|
|
if(callId != mCallId){
|
|
mCallId = callId;
|
|
emit callIdChanged();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
int CallModel::getDuration () const {
|
|
return mCall ? mCall->getDuration() : 0;
|
|
}
|
|
|
|
float CallModel::getQuality () const {
|
|
return mCall ? mCall->getCurrentQuality() : 0.0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
float CallModel::getSpeakerVu () const {
|
|
if (mCall && mCall->getState() == linphone::Call::State::StreamsRunning)
|
|
return MediastreamerUtils::computeVu(mCall->getPlayVolume());
|
|
return 0.0;
|
|
}
|
|
|
|
float CallModel::getMicroVu () const {
|
|
if (mCall && mCall->getState() == linphone::Call::State::StreamsRunning)
|
|
return MediastreamerUtils::computeVu(mCall->getRecordVolume());
|
|
return 0.0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool CallModel::getSpeakerMuted () const {
|
|
return mCall && mCall->getSpeakerMuted();
|
|
}
|
|
|
|
void CallModel::setSpeakerMuted (bool status) {
|
|
if (status == getSpeakerMuted())
|
|
return;
|
|
if(mCall)
|
|
mCall->setSpeakerMuted(status);
|
|
emit speakerMutedChanged(getSpeakerMuted());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool CallModel::getMicroMuted () const {
|
|
if (isConference())
|
|
return mConferenceModel && mConferenceModel->getMicroMuted();
|
|
else
|
|
return mCall && mCall->getMicrophoneMuted();
|
|
}
|
|
|
|
void CallModel::setMicroMuted (bool status) {
|
|
if (status == getMicroMuted())
|
|
return;
|
|
if (isConference()) mConferenceModel->setMicroMuted(status);
|
|
else if(mCall) mCall->setMicrophoneMuted(status);
|
|
emit microMutedChanged(getMicroMuted());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool CallModel::getCameraEnabled () const{
|
|
return mCall && mCall->getState() != linphone::Call::State::End && (((int)mCall->getCurrentParams()->getVideoDirection() & (int)linphone::MediaDirection::SendOnly) == (int)linphone::MediaDirection::SendOnly);
|
|
}
|
|
|
|
void CallModel::setCameraEnabled (bool status){
|
|
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
|
if(mCall) {
|
|
switch (mCall->getState()) {
|
|
case linphone::Call::State::Connected:
|
|
case linphone::Call::State::StreamsRunning:
|
|
break;
|
|
default: {
|
|
qWarning() << "Cannot set Camera mode because of call status : " << (int)mCall->getState() << " is not in {" <<(int)linphone::Call::State::Connected << ", " <<(int)linphone::Call::State::StreamsRunning << "}";
|
|
return;
|
|
}
|
|
}
|
|
if (status == getCameraEnabled())
|
|
return;
|
|
shared_ptr<linphone::CallParams> params = core->createCallParams(mCall);
|
|
params->enableVideo(CoreManager::getInstance()->getSettingsModel()->getVideoEnabled());
|
|
params->setVideoDirection(status ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly);
|
|
mCall->update(params);
|
|
}
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool CallModel::getPausedByUser () const {
|
|
return mPausedByUser;
|
|
}
|
|
|
|
void CallModel::setPausedByUser (bool status) {
|
|
if(mCall){
|
|
switch (mCall->getState()) {
|
|
case linphone::Call::State::Connected:
|
|
case linphone::Call::State::StreamsRunning:
|
|
case linphone::Call::State::Paused:
|
|
case linphone::Call::State::PausedByRemote:
|
|
break;
|
|
default: return;
|
|
}
|
|
|
|
if (status) {
|
|
if (!mPausedByUser)
|
|
mCall->pause();
|
|
return;
|
|
}
|
|
|
|
if (mPausedByUser)
|
|
mCall->resume();
|
|
}
|
|
}
|
|
|
|
bool CallModel::getPausedByRemote () const {
|
|
return mPausedByRemote;
|
|
}
|
|
|
|
void CallModel::setPausedByRemote (bool status) {
|
|
if(mPausedByRemote != status){
|
|
mPausedByRemote = status;
|
|
emit pausedByRemoteChanged();
|
|
}
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
bool CallModel::getRemoteVideoEnabled () const {
|
|
shared_ptr<const linphone::CallParams> params = mCall->getRemoteParams();
|
|
return params && params->videoEnabled();
|
|
}
|
|
|
|
bool CallModel::getLocalVideoEnabled () const {
|
|
if(mCall){
|
|
shared_ptr<const linphone::CallParams> params = mCall->getParams();
|
|
return params && params->videoEnabled();
|
|
}else
|
|
return true;
|
|
}
|
|
|
|
bool CallModel::getVideoEnabled () const {
|
|
if(mCall){
|
|
if(mCall->getState() == linphone::Call::State::End )
|
|
return false;
|
|
shared_ptr<const linphone::CallParams> params = mCall->getCurrentParams();
|
|
return params && params->videoEnabled();
|
|
}else
|
|
return CoreManager::getInstance()->getSettingsModel()->getVideoEnabled();
|
|
}
|
|
|
|
void CallModel::setVideoEnabled (bool status) {
|
|
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
|
if(mCall) {
|
|
switch (mCall->getState()) {
|
|
case linphone::Call::State::Connected:
|
|
case linphone::Call::State::StreamsRunning:
|
|
break;
|
|
default: {
|
|
qWarning() << "Cannot set Video mode because of call status : " << (int)mCall->getState() << " is not in {" <<(int)linphone::Call::State::Connected << ", " <<(int)linphone::Call::State::StreamsRunning << "}";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (status == getVideoEnabled())
|
|
return;
|
|
|
|
shared_ptr<linphone::CallParams> params = core->createCallParams(mCall);
|
|
params->enableVideo(status && CoreManager::getInstance()->getSettingsModel()->getVideoEnabled());
|
|
params->setVideoDirection(status ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly);
|
|
mCall->update(params);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool CallModel::getUpdating () const {
|
|
if(mCall) {
|
|
switch (mCall->getState()) {
|
|
case linphone::Call::State::Connected:
|
|
case linphone::Call::State::StreamsRunning:
|
|
case linphone::Call::State::Paused:
|
|
case linphone::Call::State::PausedByRemote:
|
|
return false;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CallModel::getRecording () const {
|
|
return mRecording;
|
|
}
|
|
|
|
bool CallModel::getSnapshotEnabled() const{
|
|
return getVideoEnabled() && getConferenceVideoLayout() != LinphoneEnums::ConferenceLayout::ConferenceLayoutGrid;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::sendDtmf (const QString &dtmf) {
|
|
const char key = dtmf.constData()[0].toLatin1();
|
|
qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(key);
|
|
if(mCall)
|
|
mCall->sendDtmf(key);
|
|
CoreManager::getInstance()->getCore()->playDtmf(key, DtmfSoundDelay);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::verifyAuthenticationToken (bool verify) {
|
|
if(mCall && getStatus() != CallStatusEnded)
|
|
mCall->setAuthenticationTokenVerified(verify);
|
|
emit securityUpdated();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CallModel::updateStreams () {
|
|
if(mCall)
|
|
mCall->update(nullptr);
|
|
}
|
|
void CallModel::toggleSpeakerMute(){
|
|
setSpeakerMuted(!getSpeakerMuted());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Set remote display name when a search has been done
|
|
// Local Friend > LDAP friend > Address > others
|
|
void CallModel::searchReceived(std::list<std::shared_ptr<linphone::SearchResult>> results){
|
|
bool found = false;
|
|
for(auto it = results.begin() ; it != results.end() && !found ; ++it){
|
|
if((*it)->getFriend()){// Local Friend
|
|
if((*it)->getFriend()->getAddress()->weakEqual(mRemoteAddress)){
|
|
setRemoteDisplayName((*it)->getFriend()->getName());
|
|
found = true;
|
|
}
|
|
}else{
|
|
if((*it)->getAddress()->weakEqual(mRemoteAddress)){
|
|
std::string newDisplayName = (*it)->getAddress()->getDisplayName();
|
|
if(!newDisplayName.empty()){
|
|
// LDAP friend
|
|
if( ((*it)->getSourceFlags() & (int) linphone::MagicSearch::Source::LdapServers) == (int) linphone::MagicSearch::Source::LdapServers){
|
|
setRemoteDisplayName(newDisplayName);
|
|
found = true;
|
|
}else if( Utils::coreStringToAppString(mRemoteAddress->getDisplayName()).isEmpty()){
|
|
setRemoteDisplayName(newDisplayName);
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CallModel::endCall(){
|
|
if(mCall){
|
|
ChatRoomModel * model = getChatRoomModel();
|
|
|
|
if(model){
|
|
model->onCallEnded(mCall);
|
|
}else{// No chat rooms have been associated for this call. Search one in current chat room list
|
|
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
|
std::shared_ptr<linphone::ChatRoomParams> params = core->createDefaultChatRoomParams();
|
|
std::list<std::shared_ptr<linphone::Address>> participants;
|
|
|
|
auto chatRoom = core->searchChatRoom(params, mCall->getCallLog()->getLocalAddress()
|
|
, mCall->getRemoteAddress()
|
|
, participants);
|
|
QSharedPointer<ChatRoomModel> chatRoomModel= CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, false);
|
|
if(chatRoomModel)
|
|
chatRoomModel->onCallEnded(mCall);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CallModel::getRemoteRecording() const{
|
|
return mCall && mCall->getRemoteParams() && mCall->getRemoteParams()->isRecording();
|
|
}
|
|
|
|
void CallModel::onRemoteRecording(const std::shared_ptr<linphone::Call> & call, bool recording){
|
|
emit remoteRecordingChanged(recording);
|
|
}
|
|
|
|
void CallModel::onChatRoomInitialized(int state){
|
|
qInfo() << "[CallModel] Chat room initialized with state : " << state;
|
|
emit chatRoomModelChanged();
|
|
}
|
|
|
|
void CallModel::onParticipantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant){
|
|
if(mConferenceModel && participant == mConferenceModel->getConference()->getMe()) {
|
|
emit meAdminChanged();
|
|
}
|
|
}
|
|
|
|
void CallModel::onSecurityUpdated(){
|
|
ChatRoomModel * model = getChatRoomModel();
|
|
if(model)
|
|
model->updateSecurityLevel();
|
|
}
|
|
|
|
void CallModel::onLocalScreenSharingChanged(bool enabled) {
|
|
qDebug() << "onLocalScreenSharingChanged" << enabled;
|
|
if(!enabled)
|
|
setVideoSource(nullptr);
|
|
}
|
|
|
|
void CallModel::setRemoteDisplayName(const std::string& name){
|
|
mRemoteAddress->setDisplayName(name);
|
|
if(mCall) {
|
|
auto callLog = mCall->getCallLog();
|
|
if(name!= "") {
|
|
auto core = CoreManager::getInstance()->getCore();
|
|
auto address = Utils::interpretUrl(getFullPeerAddress());
|
|
callLog->setRemoteAddress(address);
|
|
}
|
|
}
|
|
emit fullPeerAddressChanged();
|
|
ChatRoomModel * model = getChatRoomModel();
|
|
if(model)
|
|
model->emitFullPeerAddressChanged();
|
|
}
|
|
|
|
QString CallModel::getTransferAddress () const {
|
|
return mTransferAddress;
|
|
}
|
|
|
|
void CallModel::setTransferAddress (const QString &transferAddress) {
|
|
mTransferAddress = transferAddress;
|
|
emit transferAddressChanged(mTransferAddress);
|
|
}
|
|
|
|
void CallModel::prepareTransfert(shared_ptr<linphone::Call> call, const QString& transfertAddress){
|
|
if( call && transfertAddress != ""){
|
|
CallModel * model = &call->getData<CallModel>("call-model");
|
|
model->setTransferAddress(transfertAddress);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<linphone::Address> CallModel::getRemoteAddress()const{
|
|
return mRemoteAddress;
|
|
}
|
|
|
|
LinphoneEnums::ConferenceLayout CallModel::getConferenceVideoLayout() const{
|
|
return mConferenceVideoLayout;
|
|
// return mCall ? LinphoneEnums::fromLinphone(mCall->getParams()->getConferenceVideoLayout()) : LinphoneEnums::ConferenceLayoutGrid;
|
|
}
|
|
|
|
void CallModel::changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout){
|
|
auto coreManager = CoreManager::getInstance();
|
|
if( layout == LinphoneEnums::ConferenceLayoutGrid)
|
|
coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getGridCameraMode());
|
|
else
|
|
coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getActiveSpeakerCameraMode());
|
|
shared_ptr<linphone::CallParams> params = coreManager->getCore()->createCallParams(mCall);
|
|
params->setConferenceVideoLayout(LinphoneEnums::toLinphone(layout));
|
|
params->enableVideo(layout != LinphoneEnums::ConferenceLayoutAudioOnly);
|
|
if (!params->videoEnabled() && params->screenSharingEnabled()) {
|
|
params->enableScreenSharing(false); // Deactivate screensharing if going to audio only.
|
|
}
|
|
mCall->update(params);
|
|
}
|
|
|
|
void CallModel::updateConferenceVideoLayout(){
|
|
auto callParams = mCall->getParams();
|
|
auto settings = CoreManager::getInstance()->getSettingsModel();
|
|
auto newLayout = LinphoneEnums::fromLinphone(callParams->getConferenceVideoLayout());
|
|
if( !callParams->videoEnabled())
|
|
newLayout = LinphoneEnums::ConferenceLayoutAudioOnly;
|
|
if( mConferenceVideoLayout != newLayout && !getPausedByUser()){// Only update if not in pause.
|
|
if(mCall->getConference()){
|
|
if( callParams->getConferenceVideoLayout() == linphone::Conference::Layout::Grid)
|
|
settings->setCameraMode(settings->getGridCameraMode());
|
|
else
|
|
settings->setCameraMode(settings->getActiveSpeakerCameraMode());
|
|
}else
|
|
settings->setCameraMode(settings->getCallCameraMode());
|
|
qDebug() << "Changing layout from " << mConferenceVideoLayout << " into " << newLayout;
|
|
mConferenceVideoLayout = newLayout;
|
|
emit conferenceVideoLayoutChanged();
|
|
emit snapshotEnabledChanged();
|
|
}
|
|
}
|
|
|
|
void CallModel::setVideoSource(std::shared_ptr<linphone::VideoSourceDescriptor> videoDesc){
|
|
//auto videoSource = mCall->getVideoSource();
|
|
//if(!videoDesc) {
|
|
// auto oldDesc = linphone::Factory::get()->createVideoSourceDescriptor();
|
|
// oldDesc->setCameraId(CoreManager::getInstance()->getCore()->getVideoDevice());
|
|
// mCall->setVideoSource(oldDesc);
|
|
// mCall->setVideoSource(videoDesc);
|
|
//}else
|
|
mCall->setVideoSource(videoDesc);
|
|
|
|
emit videoDescriptorChanged();
|
|
}
|
|
|
|
LinphoneEnums::VideoSourceScreenSharingType CallModel::getVideoSourceType() const{
|
|
auto videoSource = mCall->getVideoSource();
|
|
return LinphoneEnums::fromLinphone(videoSource ? videoSource->getScreenSharingType() : linphone::VideoSourceScreenSharingType::Display);
|
|
}
|
|
int CallModel::getScreenSharingIndex() const{
|
|
auto videoSource = mCall->getVideoSource();
|
|
if(videoSource && videoSource->getScreenSharingType() == linphone::VideoSourceScreenSharingType::Display) {
|
|
void * t = videoSource->getScreenSharing();
|
|
return *(int*)(&t);
|
|
}else
|
|
return -1;
|
|
}
|
|
|
|
VideoSourceDescriptorModel *CallModel::getVideoSourceDescriptorModel() const {
|
|
auto videoSource = mCall->getVideoSource();
|
|
return new VideoSourceDescriptorModel(videoSource ? videoSource->clone() : nullptr);
|
|
}
|
|
|
|
void CallModel::setVideoSourceDescriptorModel(VideoSourceDescriptorModel *model) {
|
|
if(model)
|
|
setVideoSource(model->mDesc);
|
|
else {
|
|
setVideoSource(nullptr);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CallModel::CallEncryption CallModel::getEncryption () const {
|
|
return mEncryption;
|
|
}
|
|
|
|
bool CallModel::setEncryption(const CallModel::CallEncryption& encryption){
|
|
if( encryption != mEncryption){
|
|
mEncryption = encryption;
|
|
emit encryptionChanged();
|
|
return true;
|
|
}else
|
|
return false;
|
|
}
|
|
|
|
void CallModel::updateEncryption(){
|
|
if(mCall && mCall->getState() != linphone::Call::State::End){
|
|
auto currentParams = mCall->getCurrentParams();
|
|
if( currentParams){
|
|
setEncryption(static_cast<CallEncryption>(currentParams->getMediaEncryption()));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CallModel::isSecured () const {
|
|
if(mCall){
|
|
auto encryption = getEncryption();
|
|
return (encryption == CallEncryptionZrtp && mCall->getAuthenticationTokenVerified())
|
|
|| encryption == CallEncryptionSrtp
|
|
|| encryption == CallEncryptionDtls;
|
|
}else
|
|
return false;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::getLocalSas () const {
|
|
if(mCall){
|
|
QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken());
|
|
if(token.isEmpty())
|
|
return "";
|
|
else
|
|
return mCall->getDir() == linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper();
|
|
}else
|
|
return "";
|
|
}
|
|
|
|
QString CallModel::getRemoteSas () const {
|
|
if(mCall){
|
|
QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken());
|
|
if(token.isEmpty())
|
|
return "";
|
|
else
|
|
return mCall->getDir() != linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper();
|
|
}else
|
|
return "";
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::getSecuredString (const shared_ptr<const linphone::CallStats> &callStats) const {
|
|
if(mCall){
|
|
switch (getEncryption()) {
|
|
case CallEncryptionSrtp:
|
|
return QStringLiteral("SRTP");
|
|
case CallEncryptionZrtp:
|
|
return (callStats && callStats->isZrtpKeyAgreementAlgoPostQuantum() || (!callStats && mIsPQZrtp == CallPQStateOn) )
|
|
? QStringLiteral("Post Quantum ZRTP")
|
|
: QStringLiteral("ZRTP");
|
|
case CallEncryptionDtls:
|
|
return QStringLiteral("DTLS");
|
|
case CallEncryptionNone:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return QString("");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QVariantList CallModel::getAudioStats () const {
|
|
return mAudioStats;
|
|
}
|
|
|
|
QVariantList CallModel::getVideoStats () const {
|
|
return mVideoStats;
|
|
}
|
|
|
|
QVariantList CallModel::getEncryptionStats () const {
|
|
return mEncryptionStats;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static inline QVariantMap createStat (const QString &key, const QString &value) {
|
|
QVariantMap m;
|
|
m["key"] = key;
|
|
m["value"] = value;
|
|
return m;
|
|
}
|
|
|
|
void CallModel::updateStats (const shared_ptr<const linphone::CallStats> &callStats, QVariantList &statsList) {
|
|
if(mCall && mCall->getState() != linphone::Call::State::End){
|
|
shared_ptr<const linphone::CallParams> params = mCall->getCurrentParams();
|
|
shared_ptr<const linphone::PayloadType> payloadType;
|
|
|
|
switch (callStats->getType()) {
|
|
case linphone::StreamType::Audio:
|
|
payloadType = params->getUsedAudioPayloadType();
|
|
break;
|
|
case linphone::StreamType::Video:
|
|
payloadType = params->getUsedVideoPayloadType();
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
QString family;
|
|
switch (callStats->getIpFamilyOfRemote()) {
|
|
case linphone::Address::Family::Inet:
|
|
family = QStringLiteral("IPv4");
|
|
break;
|
|
case linphone::Address::Family::Inet6:
|
|
family = QStringLiteral("IPv6");
|
|
break;
|
|
default:
|
|
family = QStringLiteral("Unknown");
|
|
break;
|
|
}
|
|
|
|
statsList.clear();
|
|
|
|
statsList << createStat(tr("callStatsCodec"), payloadType
|
|
? QStringLiteral("%1 / %2kHz").arg(Utils::coreStringToAppString(payloadType->getMimeType())).arg(payloadType->getClockRate() / 1000)
|
|
: QString(""));
|
|
statsList << createStat(tr("callStatsUploadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getUploadBandwidth())));
|
|
statsList << createStat(tr("callStatsDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getDownloadBandwidth())));
|
|
statsList << createStat(tr("callStatsIceState"), iceStateToString(callStats->getIceState()));
|
|
statsList << createStat(tr("callStatsIpFamily"), family);
|
|
statsList << createStat(tr("callStatsSenderLossRate"), QStringLiteral("%1 %").arg(static_cast<double>(callStats->getSenderLossRate())));
|
|
statsList << createStat(tr("callStatsReceiverLossRate"), QStringLiteral("%1 %").arg(static_cast<double>(callStats->getReceiverLossRate())));
|
|
|
|
switch (callStats->getType()) {
|
|
case linphone::StreamType::Audio:
|
|
statsList << createStat(tr("callStatsJitterBuffer"), QStringLiteral("%1 ms").arg(callStats->getJitterBufferSizeMs()));
|
|
break;
|
|
case linphone::StreamType::Video: {
|
|
statsList << createStat(tr("callStatsEstimatedDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getEstimatedDownloadBandwidth())));
|
|
const QString sentVideoDefinitionName = Utils::coreStringToAppString(params->getSentVideoDefinition()->getName());
|
|
const QString sentVideoDefinition = QStringLiteral("%1x%2")
|
|
.arg(params->getSentVideoDefinition()->getWidth())
|
|
.arg(params->getSentVideoDefinition()->getHeight());
|
|
|
|
statsList << createStat(tr("callStatsSentVideoDefinition"), sentVideoDefinition == sentVideoDefinitionName
|
|
? sentVideoDefinition
|
|
: QStringLiteral("%1 (%2)").arg(sentVideoDefinition).arg(sentVideoDefinitionName));
|
|
|
|
const QString receivedVideoDefinitionName = Utils::coreStringToAppString(params->getReceivedVideoDefinition()->getName());
|
|
const QString receivedVideoDefinition = QString("%1x%2")
|
|
.arg(params->getReceivedVideoDefinition()->getWidth())
|
|
.arg(params->getReceivedVideoDefinition()->getHeight());
|
|
|
|
statsList << createStat(tr("callStatsReceivedVideoDefinition"), receivedVideoDefinition == receivedVideoDefinitionName
|
|
? receivedVideoDefinition
|
|
: QString("%1 (%2)").arg(receivedVideoDefinition).arg(receivedVideoDefinitionName));
|
|
|
|
statsList << createStat(tr("callStatsReceivedFramerate"), QStringLiteral("%1 FPS").arg(static_cast<double>(params->getReceivedFramerate())));
|
|
statsList << createStat(tr("callStatsSentFramerate"), QStringLiteral("%1 FPS").arg(static_cast<double>(params->getSentFramerate())));
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void CallModel::updateEncrypionStats (const shared_ptr<const linphone::CallStats> &callStats, QVariantList &statsList) {
|
|
if( callStats->getType() == linphone::StreamType::Audio && mCall->getState() != linphone::Call::State::End) {// just in case
|
|
statsList.clear();
|
|
if(isSecured()) {
|
|
//: 'Media encryption' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsMediaEncryption"), getSecuredString(callStats));
|
|
if(mCall->getCurrentParams()->getMediaEncryption() == linphone::MediaEncryption::ZRTP){
|
|
//: 'Cipher algorithm' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsCipherAlgo"), Utils::coreStringToAppString(callStats->getZrtpCipherAlgo()));
|
|
//: 'Key agreement algorithm' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsKeyAgreementAlgo"), Utils::coreStringToAppString(callStats->getZrtpKeyAgreementAlgo()));
|
|
//: 'Hash algorithm' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsHashAlgo"), Utils::coreStringToAppString(callStats->getZrtpHashAlgo()));
|
|
//: 'Authentication algorithm' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsAuthAlgo"), Utils::coreStringToAppString(callStats->getZrtpAuthTagAlgo()));
|
|
//: 'SAS algorithm' : label in encryption section of call statistics
|
|
statsList << createStat(tr("callStatsSasAlgo"), Utils::coreStringToAppString(callStats->getZrtpSasAlgo()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CallModel::isPQZrtp(const CallPQState& isPQ){
|
|
if(mIsPQZrtp != isPQ){
|
|
mIsPQZrtp = isPQ;
|
|
emit isPQZrtpChanged();
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::iceStateToString (linphone::IceState state) const {
|
|
switch (state) {
|
|
case linphone::IceState::NotActivated:
|
|
return tr("iceStateNotActivated");
|
|
case linphone::IceState::Failed:
|
|
return tr("iceStateFailed");
|
|
case linphone::IceState::InProgress:
|
|
return tr("iceStateInProgress");
|
|
case linphone::IceState::ReflexiveConnection:
|
|
return tr("iceStateReflexiveConnection");
|
|
case linphone::IceState::HostConnection:
|
|
return tr("iceStateHostConnection");
|
|
case linphone::IceState::RelayConnection:
|
|
return tr("iceStateRelayConnection");
|
|
}
|
|
|
|
return tr("iceStateInvalid");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
QString CallModel::generateSavedFilename () const {
|
|
const shared_ptr<linphone::CallLog> callLog(mCall->getCallLog());
|
|
return generateSavedFilename(
|
|
Utils::coreStringToAppString(callLog->getFromAddress()->getUsername()),
|
|
Utils::coreStringToAppString(callLog->getToAddress()->getUsername())
|
|
);
|
|
}
|
|
|
|
QString CallModel::generateSavedFilename (const QString &from, const QString &to) {
|
|
auto escape = [](const QString &str) {
|
|
constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+";
|
|
static QRegularExpression regexp(ReservedCharacters);
|
|
return QString(str).replace(regexp, "");
|
|
};
|
|
return QStringLiteral("%1_%2_%3")
|
|
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss"))
|
|
.arg(escape(from))
|
|
.arg(escape(to));
|
|
}
|
|
|
|
QStringList CallModel::splitSavedFilename(const QString& filename){
|
|
QStringList fields = filename.split('_');
|
|
if(fields.size() == 4 && fields[0].split('-').size() == 3 && fields[1].split('-').size() == 3){
|
|
return fields;
|
|
}else
|
|
return QStringList(filename);
|
|
}
|
|
|
|
QDateTime CallModel::getDateTimeSavedFilename(const QString& filename){
|
|
auto fields = splitSavedFilename(filename);
|
|
if(fields.size() > 1)
|
|
return QDateTime::fromString(fields[0] + "_" +fields[1], "yyyy-MM-dd_hh-mm-ss");
|
|
else
|
|
return QDateTime();
|
|
}
|
|
|
|
QString CallModel::getFromSavedFilename(const QString& filename){
|
|
auto fields = splitSavedFilename(filename);
|
|
if(fields.size() > 1)
|
|
return fields[2];
|
|
else
|
|
return "";
|
|
}
|
|
|
|
QString CallModel::getToSavedFilename(const QString& filename){
|
|
auto fields = splitSavedFilename(filename);
|
|
if(fields.size() > 1)
|
|
return fields[3];
|
|
else
|
|
return "";
|
|
}
|