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