Line data Source code
1 : /* 2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include <algorithm> 19 : #include <cassert> 20 : #include <sstream> 21 : 22 : #pragma GCC diagnostic push 23 : #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 24 : #include <yaml-cpp/yaml.h> 25 : #pragma GCC diagnostic pop 26 : 27 : #include "manager.h" 28 : #include "media_const.h" 29 : #include "client/videomanager.h" 30 : #include "client/ring_signal.h" 31 : #include "config/yamlparser.h" 32 : #include "logger.h" 33 : #include "video_device_monitor.h" 34 : 35 : namespace jami { 36 : namespace video { 37 : 38 : constexpr const char* const VideoDeviceMonitor::CONFIG_LABEL; 39 : 40 : using std::map; 41 : using std::string; 42 : using std::vector; 43 : 44 : vector<string> 45 1 : VideoDeviceMonitor::getDeviceList() const 46 : { 47 1 : std::lock_guard l(lock_); 48 1 : vector<string> ids; 49 1 : ids.reserve(devices_.size()); 50 2 : for (const auto& dev : devices_) { 51 1 : if (dev.name != DEVICE_DESKTOP) 52 0 : ids.emplace_back(dev.getDeviceId()); 53 : } 54 2 : return ids; 55 1 : } 56 : 57 : libjami::VideoCapabilities 58 0 : VideoDeviceMonitor::getCapabilities(const string& id) const 59 : { 60 0 : std::lock_guard l(lock_); 61 0 : const auto iter = findDeviceById(id); 62 0 : if (iter == devices_.end()) 63 0 : return libjami::VideoCapabilities(); 64 : 65 0 : return iter->getCapabilities(); 66 0 : } 67 : 68 : VideoSettings 69 0 : VideoDeviceMonitor::getSettings(const string& id) 70 : { 71 0 : std::lock_guard l(lock_); 72 : 73 0 : const auto prefIter = findPreferencesById(id); 74 0 : if (prefIter == preferences_.end()) 75 0 : return VideoSettings(); 76 : 77 0 : return *prefIter; 78 0 : } 79 : 80 : void 81 0 : VideoDeviceMonitor::applySettings(const string& id, const VideoSettings& settings) 82 : { 83 0 : std::lock_guard l(lock_); 84 0 : const auto iter = findDeviceById(id); 85 : 86 0 : if (iter == devices_.end()) 87 0 : return; 88 : 89 0 : iter->applySettings(settings); 90 0 : auto it = findPreferencesById(settings.unique_id); 91 0 : if (it != preferences_.end()) 92 0 : (*it) = settings; 93 0 : } 94 : 95 : string 96 301 : VideoDeviceMonitor::getDefaultDevice() const 97 : { 98 301 : std::lock_guard l(lock_); 99 301 : const auto it = findDeviceById(defaultDevice_); 100 301 : if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) 101 301 : return {}; 102 0 : return it->getDeviceId(); 103 301 : } 104 : 105 : std::string 106 360 : VideoDeviceMonitor::getMRLForDefaultDevice() const 107 : { 108 360 : std::lock_guard l(lock_); 109 360 : const auto it = findDeviceById(defaultDevice_); 110 360 : if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) 111 360 : return {}; 112 0 : static const std::string sep = libjami::Media::VideoProtocolPrefix::SEPARATOR; 113 0 : return libjami::Media::VideoProtocolPrefix::CAMERA + sep + it->getDeviceId(); 114 360 : } 115 : 116 : bool 117 0 : VideoDeviceMonitor::setDefaultDevice(const std::string& id) 118 : { 119 0 : std::lock_guard l(lock_); 120 0 : const auto itDev = findDeviceById(id); 121 0 : if (itDev != devices_.end()) { 122 0 : if (defaultDevice_ == itDev->getDeviceId()) 123 0 : return false; 124 0 : defaultDevice_ = itDev->getDeviceId(); 125 : 126 : // place it at the begining of the prefs 127 0 : auto itPref = findPreferencesById(itDev->getDeviceId()); 128 0 : if (itPref != preferences_.end()) { 129 0 : auto settings = *itPref; 130 0 : preferences_.erase(itPref); 131 0 : preferences_.insert(preferences_.begin(), settings); 132 0 : } else { 133 0 : preferences_.insert(preferences_.begin(), itDev->getSettings()); 134 : } 135 0 : return true; 136 : } 137 0 : return false; 138 0 : } 139 : 140 : void 141 0 : VideoDeviceMonitor::setDeviceOrientation(const std::string& id, int angle) 142 : { 143 0 : std::lock_guard l(lock_); 144 0 : const auto itd = findDeviceById(id); 145 0 : if (itd != devices_.cend()) { 146 0 : itd->setOrientation(angle); 147 : } else { 148 0 : JAMI_WARN("Unable to find device %s to set orientation %d", id.c_str(), angle); 149 : } 150 0 : } 151 : 152 : DeviceParams 153 301 : VideoDeviceMonitor::getDeviceParams(const std::string& id) const 154 : { 155 301 : std::lock_guard l(lock_); 156 301 : const auto itd = findDeviceById(id); 157 301 : if (itd == devices_.cend()) 158 0 : return DeviceParams(); 159 301 : return itd->getDeviceParams(); 160 301 : } 161 : 162 : static void 163 32 : giveUniqueName(VideoDevice& dev, const vector<VideoDevice>& devices) 164 : { 165 32 : std::string suffix; 166 32 : uint64_t number = 2; 167 32 : auto unique = true; 168 0 : for (;; unique = static_cast<bool>(++number)) { 169 32 : for (const auto& s : devices) 170 0 : unique &= static_cast<bool>(s.name.compare(dev.name + suffix)); 171 32 : if (unique) 172 64 : return (void) (dev.name += suffix); 173 0 : suffix = " (" + std::to_string(number) + ")"; 174 0 : } 175 32 : } 176 : 177 : static void 178 32 : notify() 179 : { 180 32 : if (Manager::initialized) { 181 32 : emitSignal<libjami::VideoSignal::DeviceEvent>(); 182 : } 183 32 : } 184 : 185 : bool 186 32 : VideoDeviceMonitor::addDevice(const string& id, const std::vector<std::map<std::string, std::string>>& devInfo) 187 : { 188 : try { 189 32 : std::lock_guard l(lock_); 190 32 : if (findDeviceById(id) != devices_.end()) 191 0 : return false; 192 : 193 : // instantiate a new unique device 194 32 : VideoDevice dev {id, devInfo}; 195 : 196 32 : if (dev.getChannelList().empty()) 197 0 : return false; 198 : 199 32 : giveUniqueName(dev, devices_); 200 : 201 : // restore its preferences if any, or store the defaults 202 32 : auto it = findPreferencesById(id); 203 32 : if (it != preferences_.end()) { 204 0 : dev.applySettings(*it); 205 : } else { 206 32 : dev.applySettings(dev.getDefaultSettings()); 207 32 : preferences_.emplace_back(dev.getSettings()); 208 : } 209 : 210 : // in case there is no default device on a fresh run 211 32 : if (defaultDevice_.empty() && id != DEVICE_DESKTOP) 212 0 : defaultDevice_ = dev.getDeviceId(); 213 : 214 32 : devices_.emplace_back(std::move(dev)); 215 32 : } catch (const std::exception& e) { 216 0 : JAMI_ERR("Failed to add device %s: %s", id.c_str(), e.what()); 217 0 : return false; 218 0 : } 219 32 : notify(); 220 32 : return true; 221 : } 222 : 223 : void 224 0 : VideoDeviceMonitor::removeDevice(const string& id) 225 : { 226 : { 227 0 : std::lock_guard l(lock_); 228 0 : const auto it = findDeviceById(id); 229 0 : if (it == devices_.end()) 230 0 : return; 231 : 232 0 : devices_.erase(it); 233 0 : if (defaultDevice_.find(id) != std::string::npos) { 234 0 : defaultDevice_.clear(); 235 0 : for (const auto& dev : devices_) 236 0 : if (dev.name != DEVICE_DESKTOP) { 237 0 : defaultDevice_ = dev.getDeviceId(); 238 0 : break; 239 : } 240 : } 241 0 : } 242 0 : notify(); 243 : } 244 : 245 : vector<VideoDevice>::iterator 246 32 : VideoDeviceMonitor::findDeviceById(const string& id) 247 : { 248 32 : for (auto it = devices_.begin(); it != devices_.end(); ++it) 249 0 : if (it->getDeviceId().find(id) != std::string::npos) 250 0 : return it; 251 32 : return devices_.end(); 252 : } 253 : 254 : vector<VideoDevice>::const_iterator 255 962 : VideoDeviceMonitor::findDeviceById(const string& id) const 256 : { 257 962 : for (auto it = devices_.cbegin(); it != devices_.cend(); ++it) 258 962 : if (it->getDeviceId().find(id) != std::string::npos) 259 962 : return it; 260 0 : return devices_.end(); 261 : } 262 : 263 : vector<VideoSettings>::iterator 264 32 : VideoDeviceMonitor::findPreferencesById(const string& id) 265 : { 266 32 : for (auto it = preferences_.begin(); it != preferences_.end(); ++it) 267 0 : if (it->unique_id.find(id) != std::string::npos) 268 0 : return it; 269 32 : return preferences_.end(); 270 : } 271 : 272 : void 273 0 : VideoDeviceMonitor::overwritePreferences(const VideoSettings& settings) 274 : { 275 0 : auto it = findPreferencesById(settings.unique_id); 276 0 : if (it != preferences_.end()) 277 0 : preferences_.erase(it); 278 0 : preferences_.emplace_back(settings); 279 0 : } 280 : 281 : void 282 1700 : VideoDeviceMonitor::serialize(YAML::Emitter& out) const 283 : { 284 1700 : std::lock_guard l(lock_); 285 1700 : out << YAML::Key << "devices" << YAML::Value << preferences_; 286 1700 : } 287 : 288 : void 289 0 : VideoDeviceMonitor::unserialize(const YAML::Node& in) 290 : { 291 0 : std::lock_guard l(lock_); 292 0 : const auto& node = in[CONFIG_LABEL]; 293 : 294 : /* load the device list from the "video" YAML section */ 295 0 : const auto& devices = node["devices"]; 296 0 : for (const auto& dev : devices) { 297 0 : VideoSettings pref = dev.as<VideoSettings>(); 298 0 : if (pref.unique_id.empty()) 299 0 : continue; // discard malformed section 300 0 : overwritePreferences(pref); 301 0 : auto itd = findDeviceById(pref.unique_id); 302 0 : if (itd != devices_.end()) 303 0 : itd->applySettings(pref); 304 0 : } 305 : 306 : // Restore the default device if present, or select the first one 307 0 : const string prefId = preferences_.empty() ? "" : preferences_[0].unique_id; 308 0 : const auto devIter = findDeviceById(prefId); 309 0 : if (devIter != devices_.end() && prefId != DEVICE_DESKTOP) { 310 0 : defaultDevice_ = devIter->getDeviceId(); 311 : } else { 312 0 : defaultDevice_.clear(); 313 0 : for (const auto& dev : devices_) 314 0 : if (dev.name != DEVICE_DESKTOP) { 315 0 : defaultDevice_ = dev.getDeviceId(); 316 0 : break; 317 : } 318 : } 319 0 : } 320 : 321 : } // namespace video 322 : } // namespace jami