/* * 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 "SettingsModel.hpp" #include "model/core/CoreModel.hpp" #include "tool/Utils.hpp" #include "model/tool/ToolModel.hpp" #include "core/path/Paths.hpp" // ============================================================================= DEFINE_ABSTRACT_OBJECT(SettingsModel) using namespace std; const std::string SettingsModel::UiSection("ui"); SettingsModel::SettingsModel(QObject *parent) : QObject(parent) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); mConfig = core->getConfig(); CoreModel::getInstance()->getLogger()->applyConfig(mConfig); } SettingsModel::~SettingsModel() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); } bool SettingsModel::isReadOnly(const std::string §ion, const std::string &name) const { return mConfig->hasEntry(section, name + "/readonly"); } std::string SettingsModel::getEntryFullName(const std::string §ion, const std::string &name) const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return isReadOnly(section, name) ? name + "/readonly" : name; } QStringList SettingsModel::getVideoDevices() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); QStringList result; for (auto &device : core->getVideoDevicesList()) { result.append(Utils::coreStringToAppString(device)); } return result; } QString SettingsModel::getVideoDevice () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return Utils::coreStringToAppString( CoreModel::getInstance()->getCore()->getVideoDevice() ); } void SettingsModel::setVideoDevice (const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->setVideoDevice( Utils::appStringToCoreString(device) ); emit videoDeviceChanged(device); } // ============================================================================= // Audio. // ============================================================================= bool SettingsModel::getIsInCall() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->getCallsNb() != 0; } void SettingsModel::resetCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); deleteCaptureGraph(); createCaptureGraph(); } void SettingsModel::createCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mSimpleCaptureGraph = new MediastreamerUtils::SimpleCaptureGraph(Utils::appStringToCoreString(getCaptureDevice()), Utils::appStringToCoreString(getPlaybackDevice())); mSimpleCaptureGraph->start(); emit captureGraphRunningChanged(getCaptureGraphRunning()); } void SettingsModel::startCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (!getIsInCall()) { if (!mSimpleCaptureGraph) { qDebug() << "Starting capture graph [" << mCaptureGraphListenerCount << "]"; createCaptureGraph(); } ++mCaptureGraphListenerCount; } } void SettingsModel::stopCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mCaptureGraphListenerCount > 0) { if (--mCaptureGraphListenerCount == 0) { qDebug() << "Stopping capture graph [" << mCaptureGraphListenerCount << "]"; deleteCaptureGraph(); } } } void SettingsModel::stopCaptureGraphs() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mCaptureGraphListenerCount > 0) { mCaptureGraphListenerCount = 0; deleteCaptureGraph(); } } void SettingsModel::deleteCaptureGraph() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mSimpleCaptureGraph) { if (mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->stop(); } delete mSimpleCaptureGraph; mSimpleCaptureGraph = nullptr; } } //Force a call on the 'detect' method of all audio filters, updating new or removed devices void SettingsModel::accessCallSettings() { // Audio mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->reloadSoundDevices(); emit captureDevicesChanged(getCaptureDevices()); emit playbackDevicesChanged(getPlaybackDevices()); emit playbackDeviceChanged(getPlaybackDevice()); emit captureDeviceChanged(getCaptureDevice()); emit ringerDeviceChanged(getRingerDevice()); emit playbackGainChanged(getPlaybackGain()); emit captureGainChanged(getCaptureGain()); // Media cards must not be used twice (capture card + call) else we will get latencies issues and bad echo calibrations in call. if (!getIsInCall()) { qDebug() << "Starting capture graph from accessing audio panel"; startCaptureGraph(); } // Video CoreModel::getInstance()->getCore()->reloadVideoDevices(); emit videoDevicesChanged(getVideoDevices()); } void SettingsModel::closeCallSettings() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); stopCaptureGraph(); emit captureGraphRunningChanged(getCaptureGraphRunning()); } bool SettingsModel::getCaptureGraphRunning() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning() && !getIsInCall(); } float SettingsModel::getMicVolume() { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); float v = 0.0; if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { v = mSimpleCaptureGraph->getCaptureVolume(); } emit micVolumeChanged(v); return v; } float SettingsModel::getPlaybackGain() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); float dbGain = CoreModel::getInstance()->getCore()->getPlaybackGainDb(); return MediastreamerUtils::dbToLinear(dbGain); } void SettingsModel::setPlaybackGain(float gain) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); float oldGain = getPlaybackGain(); CoreModel::getInstance()->getCore()->setPlaybackGainDb(MediastreamerUtils::linearToDb(gain)); if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setPlaybackGain(gain); } if((int)(oldGain*1000) != (int)(gain*1000)) emit playbackGainChanged(gain); } float SettingsModel::getCaptureGain() const { mustBeInLinphoneThread(getClassName()); float dbGain = CoreModel::getInstance()->getCore()->getMicGainDb(); return MediastreamerUtils::dbToLinear(dbGain); } void SettingsModel::setCaptureGain(float gain) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); float oldGain = getCaptureGain(); CoreModel::getInstance()->getCore()->setMicGainDb(MediastreamerUtils::linearToDb(gain)); if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setCaptureGain(gain); } if((int)(oldGain *1000) != (int)(gain *1000)) emit captureGainChanged(gain); } QStringList SettingsModel::getCaptureDevices () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); shared_ptr core = CoreModel::getInstance()->getCore(); QStringList list; for (const auto &device : core->getExtendedAudioDevices()) { if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityRecord)) list << Utils::coreStringToAppString(device->getId()); } return list; } QStringList SettingsModel::getPlaybackDevices () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); shared_ptr core = CoreModel::getInstance()->getCore(); QStringList list; for (const auto &device : core->getExtendedAudioDevices()) { if (device->hasCapability(linphone::AudioDevice::Capabilities::CapabilityPlay)) list << Utils::coreStringToAppString(device->getId()); } return list; } // ----------------------------------------------------------------------------- QString SettingsModel::getCaptureDevice () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto audioDevice = CoreModel::getInstance()->getCore()->getInputAudioDevice(); return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getCaptureDevice()); } void SettingsModel::setCaptureDevice (const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); std::string devId = Utils::appStringToCoreString(device); auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { return audioItem->getId() == devId; }); if(audioDevice != list.cend()){ CoreModel::getInstance()->getCore()->setCaptureDevice(devId); CoreModel::getInstance()->getCore()->setInputAudioDevice(*audioDevice); emit captureDeviceChanged(device); resetCaptureGraph(); }else qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- QString SettingsModel::getPlaybackDevice () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto audioDevice = CoreModel::getInstance()->getCore()->getOutputAudioDevice(); return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreModel::getInstance()->getCore()->getPlaybackDevice()); } void SettingsModel::setPlaybackDevice (const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); std::string devId = Utils::appStringToCoreString(device); auto list = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { return audioItem->getId() == devId; }); if(audioDevice != list.cend()){ CoreModel::getInstance()->getCore()->setPlaybackDevice(devId); CoreModel::getInstance()->getCore()->setOutputAudioDevice(*audioDevice); emit playbackDeviceChanged(device); resetCaptureGraph(); }else qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- QString SettingsModel::getRingerDevice () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return Utils::coreStringToAppString( CoreModel::getInstance()->getCore()->getRingerDevice() ); } void SettingsModel::setRingerDevice (const QString &device) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->setRingerDevice( Utils::appStringToCoreString(device) ); emit ringerDeviceChanged(device); } // ----------------------------------------------------------------------------- bool SettingsModel::getVideoEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->videoEnabled(); } void SettingsModel::setVideoEnabled(const bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); core->enableVideoCapture(enabled); core->enableVideoDisplay(enabled); emit videoEnabledChanged(enabled); } // ----------------------------------------------------------------------------- bool SettingsModel::getEchoCancellationEnabled () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->echoCancellationEnabled(); } void SettingsModel::setEchoCancellationEnabled (bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->enableEchoCancellation(status); emit echoCancellationEnabledChanged(status); } void SettingsModel::startEchoCancellerCalibration(){ mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->startEchoCancellerCalibration(); } int SettingsModel::getEchoCancellationCalibration()const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->getEchoCancellationCalibration(); } bool SettingsModel::getAutomaticallyRecordCallsEnabled () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return !!mConfig->getInt(UiSection, "automatically_record_calls", 0); } void SettingsModel::setAutomaticallyRecordCallsEnabled (bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "automatically_record_calls", enabled); emit automaticallyRecordCallsEnabledChanged(enabled); } // ============================================================================= // VFS. // ============================================================================= bool SettingsModel::getVfsEnabled () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return !!mConfig->getInt(UiSection, "vfs_enabled", 0); } void SettingsModel::setVfsEnabled (bool enabled) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "vfs_enabled", enabled); emit vfsEnabledChanged(enabled); } // ============================================================================= // Logs. // ============================================================================= bool SettingsModel::getLogsEnabled () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsEnabled(mConfig); } void SettingsModel::setLogsEnabled (bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "logs_enabled", status); CoreModel::getInstance()->getLogger()->enable(status); emit logsEnabledChanged(status); } bool SettingsModel::getFullLogsEnabled () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getFullLogsEnabled(mConfig); } void SettingsModel::setFullLogsEnabled (bool status) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mConfig->setInt(UiSection, "full_logs_enabled", status); CoreModel::getInstance()->getLogger()->enableFullLogs(status); emit fullLogsEnabledChanged(status); } bool SettingsModel::getLogsEnabled (const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "logs_enabled", false) : true; } bool SettingsModel::getFullLogsEnabled (const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? config->getInt(UiSection, "full_logs_enabled", false) : false; } QString SettingsModel::getLogsFolder () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return getLogsFolder(mConfig); } QString SettingsModel::getLogsFolder (const shared_ptr &config) { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); return config ? Utils::coreStringToAppString(config->getString(UiSection, "logs_folder", Utils::appStringToCoreString(Paths::getLogsDirPath()))) : Paths::getLogsDirPath(); } void SettingsModel::cleanLogs () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); CoreModel::getInstance()->getCore()->resetLogCollection(); } void SettingsModel::sendLogs () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); qInfo() << QStringLiteral("Send logs to: `%1` from `%2`.") .arg(Utils::coreStringToAppString(core->getLogCollectionUploadServerUrl())) .arg(Utils::coreStringToAppString(core->getLogCollectionPath())); core->uploadLogCollection(); } QString SettingsModel::getLogsEmail () const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return Utils::coreStringToAppString( mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail) ); }