/* * Copyright (c) 2010-2024 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 . */ #include "CallModel.hpp" #include #include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" #include "tool/Utils.hpp" DEFINE_ABSTRACT_OBJECT(CallModel) CallModel::CallModel(const std::shared_ptr &call, QObject *parent) : ::Listener(call, parent) { lDebug() << "[CallModel] new" << this; mustBeInLinphoneThread(getClassName()); mDurationTimer.setInterval(1000); mDurationTimer.setSingleShot(false); connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->durationChanged(mMonitor->getDuration()); }); mDurationTimer.start(); mMicroVolumeTimer.setInterval(50); mMicroVolumeTimer.setSingleShot(false); connect(&mMicroVolumeTimer, &QTimer::timeout, this, [this]() { this->microphoneVolumeChanged(Utils::computeVu(mMonitor->getRecordVolume())); }); mMicroVolumeTimer.start(); connect(this, &CallModel::stateChanged, this, [this] { auto state = mMonitor->getState(); if (state == linphone::Call::State::Paused) setPaused(true); }); } CallModel::~CallModel() { mustBeInLinphoneThread("~" + getClassName()); } void CallModel::accept(bool withVideo) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); auto params = core->createCallParams(mMonitor); params->setRecordFile( Paths::getCapturesDirPath() .append(Utils::generateSavedFilename(QString::fromStdString(mMonitor->getToAddress()->getUsername()), "")) .append(".mkv") .toStdString()); // Answer with local call address. auto localAddress = mMonitor->getCallLog()->getLocalAddress(); for (auto account : core->getAccountList()) { if (account->getParams()->getIdentityAddress()->weakEqual(localAddress)) { params->setAccount(account); break; } } activateLocalVideo(params, mMonitor->getCurrentParams(), withVideo); mMonitor->acceptWithParams(params); emit localVideoEnabledChanged(withVideo); } void CallModel::decline() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto errorInfo = linphone::Factory::get()->createErrorInfo(); errorInfo->set("SIP", linphone::Reason::Declined, 603, "Decline", ""); mMonitor->terminateWithErrorInfo(errorInfo); } void CallModel::terminate() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->terminate(); } void CallModel::setPaused(bool paused) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (paused) { auto status = mMonitor->pause(); if (status != -1) emit pausedChanged(paused); } else { auto status = mMonitor->resume(); if (status != -1) emit pausedChanged(paused); } } void CallModel::transferTo(const std::shared_ptr &address) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mMonitor->transferTo(address) == -1) qWarning() << log() .arg(QStringLiteral("Unable to transfer: `%1`.")) .arg(Utils::coreStringToAppString(address->asStringUriOnly())); } void CallModel::terminateAllCalls() { auto status = mMonitor->getCore()->terminateAllCalls(); } void CallModel::setMicrophoneMuted(bool isMuted) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setMicrophoneMuted(isMuted); emit microphoneMutedChanged(isMuted); } void CallModel::setSpeakerMuted(bool isMuted) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setSpeakerMuted(isMuted); emit speakerMutedChanged(isMuted); } void CallModel::activateLocalVideo(std::shared_ptr ¶ms, const std::shared_ptr ¤tParams, bool enable) { params->enableVideo(true); auto direction = currentParams ? currentParams->getVideoDirection() : params->getVideoDirection(); auto videoDirection = linphone::MediaDirection::RecvOnly; if (enable) { // +Send switch (direction) { case linphone::MediaDirection::RecvOnly: videoDirection = linphone::MediaDirection::SendRecv; break; case linphone::MediaDirection::SendOnly: videoDirection = linphone::MediaDirection::SendOnly; break; case linphone::MediaDirection::SendRecv: videoDirection = linphone::MediaDirection::SendRecv; break; default: videoDirection = linphone::MediaDirection::SendOnly; } } else { // -Send switch (direction) { case linphone::MediaDirection::RecvOnly: videoDirection = linphone::MediaDirection::RecvOnly; break; case linphone::MediaDirection::SendOnly: videoDirection = linphone::MediaDirection::Inactive; break; case linphone::MediaDirection::SendRecv: videoDirection = linphone::MediaDirection::RecvOnly; break; default: videoDirection = linphone::MediaDirection::Inactive; } } /* auto videoDirection = !enabled ? linphone::MediaDirection::RecvOnly : direction == linphone::MediaDirection::RecvOnly // ? linphone::MediaDirection::SendRecv : direction == linphone::MediaDirection::SendRecv || direction == linphone::MediaDirection::SendOnly ? linphone::MediaDirection::RecvOnly : linphone::MediaDirection::SendOnly; */ params->setVideoDirection(videoDirection); } void CallModel::setLocalVideoEnabled(bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto params = CoreModel::getInstance()->getCore()->createCallParams(mMonitor); activateLocalVideo(params, mMonitor->getCurrentParams(), enabled); mMonitor->update(params); } void CallModel::startRecording() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->startRecording(); emit recordingChanged(mMonitor->getParams()->isRecording()); } void CallModel::stopRecording() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->stopRecording(); emit recordingChanged(mMonitor->getParams()->isRecording()); } void CallModel::setRecordFile(const std::string &path) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); auto params = core->createCallParams(mMonitor); params->setRecordFile(path); mMonitor->update(params); } void CallModel::setSpeakerVolumeGain(float gain) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setSpeakerVolumeGain(gain); emit speakerVolumeGainChanged(gain); } float CallModel::getSpeakerVolumeGain() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto gain = mMonitor->getSpeakerVolumeGain(); return gain; } void CallModel::setMicrophoneVolumeGain(float gain) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setMicrophoneVolumeGain(gain); emit microphoneVolumeGainChanged(gain); } float CallModel::getMicrophoneVolumeGain() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto gain = mMonitor->getMicrophoneVolumeGain(); return gain; } float CallModel::getMicrophoneVolume() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto volume = mMonitor->getRecordVolume(); return volume; } void CallModel::setInputAudioDevice(const std::shared_ptr &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setInputAudioDevice(device); std::string deviceName; if (device) deviceName = device->getDeviceName(); emit inputAudioDeviceChanged(deviceName); } std::shared_ptr CallModel::getInputAudioDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getInputAudioDevice(); } void CallModel::setOutputAudioDevice(const std::shared_ptr &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setOutputAudioDevice(device); std::string deviceName; if (device) deviceName = device->getDeviceName(); emit outputAudioDeviceChanged(deviceName); } std::shared_ptr CallModel::getOutputAudioDevice() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getOutputAudioDevice(); } std::string CallModel::getRecordFile() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getParams()->getRecordFile(); } std::shared_ptr CallModel::getRemoteAddress() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getRemoteAddress(); } bool CallModel::getAuthenticationTokenVerified() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mMonitor->getAuthenticationTokenVerified(); } void CallModel::setAuthenticationTokenVerified(bool verified) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setAuthenticationTokenVerified(verified); emit authenticationTokenVerifiedChanged(verified); } std::string CallModel::getAuthenticationToken() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto token = mMonitor->getAuthenticationToken(); return token; } void CallModel::setConference(const std::shared_ptr &conference) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mConference != conference) { mConference = conference; emit conferenceChanged(); } } LinphoneEnums::ConferenceLayout CallModel::getConferenceVideoLayout() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return LinphoneEnums::fromLinphone(mMonitor->getParams()->getConferenceVideoLayout()); } void CallModel::changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto coreManager = CoreModel::getInstance(); // TODO : change layout for grid/active speaker in settings // if (layout == LinphoneEnums::ConferenceLayout::Grid) // coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getGridCameraMode()); // else // coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getActiveSpeakerCameraMode()); auto params = coreManager->getCore()->createCallParams(mMonitor); params->setConferenceVideoLayout(LinphoneEnums::toLinphone(layout)); params->enableVideo(layout != LinphoneEnums::ConferenceLayout::AudioOnly); if (!params->videoEnabled() && params->screenSharingEnabled()) { params->enableScreenSharing(false); // Deactivate screensharing if going to audio only. } mMonitor->update(params); } void CallModel::updateConferenceVideoLayout() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto callParams = mMonitor->getParams(); // auto settings = CoreManager::getInstance()->getSettingsModel(); auto newLayout = LinphoneEnums::fromLinphone(callParams->getConferenceVideoLayout()); if (!callParams->videoEnabled()) newLayout = LinphoneEnums::ConferenceLayout::AudioOnly; if (!mConference) newLayout = LinphoneEnums::ConferenceLayout::ActiveSpeaker; if (mConferenceVideoLayout != newLayout) { // && !getPausedByUser()) { // Only update if not in pause. // if (mMonitor->getConference()) { // if (callParams->getConferenceVideoLayout() == linphone::Conference::Layout::Grid) // settings->setCameraMode(settings->getGridCameraMode()); // else settings->setCameraMode(settings->getActiveSpeakerCameraMode()); // } else settings->setCameraMode(settings->getCallCameraMode()); // TODO : change layout for grid/active speaker in settings lDebug() << "Changing layout from " << mConferenceVideoLayout << " into " << newLayout; mConferenceVideoLayout = newLayout; emit conferenceVideoLayoutChanged(mConferenceVideoLayout); } } void CallModel::onDtmfReceived(const std::shared_ptr &call, int dtmf) { emit dtmfReceived(call, dtmf); } void CallModel::onGoclearAckSent(const std::shared_ptr &call) { emit goclearAckSent(call); } void CallModel::onEncryptionChanged(const std::shared_ptr &call, bool on, const std::string &authenticationToken) { emit encryptionChanged(call, on, authenticationToken); } void CallModel::onSendMasterKeyChanged(const std::shared_ptr &call, const std::string &sendMasterKey) { emit sendMasterKeyChanged(call, sendMasterKey); } void CallModel::onReceiveMasterKeyChanged(const std::shared_ptr &call, const std::string &receiveMasterKey) { emit receiveMasterKeyChanged(call, receiveMasterKey); } void CallModel::onInfoMessageReceived(const std::shared_ptr &call, const std::shared_ptr &message) { emit infoMessageReceived(call, message); } void CallModel::onStateChanged(const std::shared_ptr &call, linphone::Call::State state, const std::string &message) { lDebug() << "CallModel::onStateChanged" << (int)state; if (state == linphone::Call::State::StreamsRunning) { // After UpdatedByRemote, video direction could be changed. auto params = call->getRemoteParams(); auto videoDirection = params ? params->getVideoDirection() : linphone::MediaDirection::Inactive; emit remoteVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv); videoDirection = call->getCurrentParams()->getVideoDirection(); emit localVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv); setConference(call->getConference()); updateConferenceVideoLayout(); } emit stateChanged(state, message); } void CallModel::onStatusChanged(const std::shared_ptr &call, linphone::Call::Status status) { lDebug() << "CallModel::onStatusChanged" << (int)status; emit statusChanged(status); } void CallModel::onDirChanged(const std::shared_ptr &call, linphone::Call::Dir dir) { emit dirChanged(dir); } void CallModel::onStatsUpdated(const std::shared_ptr &call, const std::shared_ptr &stats) { emit statsUpdated(call, stats); } void CallModel::onTransferStateChanged(const std::shared_ptr &call, linphone::Call::State state) { emit transferStateChanged(call, state); } void CallModel::onAckProcessing(const std::shared_ptr &call, const std::shared_ptr &ack, bool isReceived) { emit ackProcessing(call, ack, isReceived); } void CallModel::onTmmbrReceived(const std::shared_ptr &call, int streamIndex, int tmmbr) { emit tmmbrReceived(call, streamIndex, tmmbr); } void CallModel::onSnapshotTaken(const std::shared_ptr &call, const std::string &filePath) { emit snapshotTaken(call, filePath); } void CallModel::onNextVideoFrameDecoded(const std::shared_ptr &call) { emit nextVideoFrameDecoded(call); } void CallModel::onCameraNotWorking(const std::shared_ptr &call, const std::string &cameraName) { emit cameraNotWorking(call, cameraName); } void CallModel::onVideoDisplayErrorOccurred(const std::shared_ptr &call, int errorCode) { emit videoDisplayErrorOccurred(call, errorCode); } void CallModel::onAudioDeviceChanged(const std::shared_ptr &call, const std::shared_ptr &audioDevice) { emit audioDeviceChanged(call, audioDevice); } void CallModel::onRemoteRecording(const std::shared_ptr &call, bool recording) { emit remoteRecording(call, recording); }