Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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 315 : VideoDeviceMonitor::getDefaultDevice() const 97 : { 98 315 : std::lock_guard l(lock_); 99 315 : const auto it = findDeviceById(defaultDevice_); 100 315 : if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) 101 315 : return {}; 102 0 : return it->getDeviceId(); 103 315 : } 104 : 105 : std::string 106 761 : VideoDeviceMonitor::getMRLForDefaultDevice() const 107 : { 108 761 : std::lock_guard l(lock_); 109 761 : const auto it = findDeviceById(defaultDevice_); 110 761 : if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) 111 761 : return {}; 112 0 : static const std::string sep = libjami::Media::VideoProtocolPrefix::SEPARATOR; 113 0 : return libjami::Media::VideoProtocolPrefix::CAMERA + sep + it->getDeviceId(); 114 761 : } 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 315 : VideoDeviceMonitor::getDeviceParams(const std::string& id) const 154 : { 155 315 : std::lock_guard l(lock_); 156 315 : const auto itd = findDeviceById(id); 157 315 : if (itd == devices_.cend()) 158 0 : return DeviceParams(); 159 315 : return itd->getDeviceParams(); 160 315 : } 161 : 162 : static void 163 38 : giveUniqueName(VideoDevice& dev, const vector<VideoDevice>& devices) 164 : { 165 38 : std::string suffix; 166 38 : uint64_t number = 2; 167 38 : auto unique = true; 168 0 : for (;; unique = static_cast<bool>(++number)) { 169 38 : for (const auto& s : devices) 170 0 : unique &= static_cast<bool>(s.name.compare(dev.name + suffix)); 171 38 : if (unique) 172 76 : return (void) (dev.name += suffix); 173 0 : suffix = " (" + std::to_string(number) + ")"; 174 0 : } 175 38 : } 176 : 177 : static void 178 38 : notify() 179 : { 180 38 : if (Manager::initialized) { 181 0 : emitSignal<libjami::VideoSignal::DeviceEvent>(); 182 : } 183 38 : } 184 : 185 : bool 186 38 : VideoDeviceMonitor::addDevice(const string& id, 187 : const std::vector<std::map<std::string, std::string>>& devInfo) 188 : { 189 : try { 190 38 : std::lock_guard l(lock_); 191 38 : if (findDeviceById(id) != devices_.end()) 192 0 : return false; 193 : 194 : // instantiate a new unique device 195 38 : VideoDevice dev {id, devInfo}; 196 : 197 38 : if (dev.getChannelList().empty()) 198 0 : return false; 199 : 200 38 : giveUniqueName(dev, devices_); 201 : 202 : // restore its preferences if any, or store the defaults 203 38 : auto it = findPreferencesById(id); 204 38 : if (it != preferences_.end()) { 205 0 : dev.applySettings(*it); 206 : } else { 207 38 : dev.applySettings(dev.getDefaultSettings()); 208 38 : preferences_.emplace_back(dev.getSettings()); 209 : } 210 : 211 : // in case there is no default device on a fresh run 212 38 : if (defaultDevice_.empty() && id != DEVICE_DESKTOP) 213 0 : defaultDevice_ = dev.getDeviceId(); 214 : 215 38 : devices_.emplace_back(std::move(dev)); 216 38 : } catch (const std::exception& e) { 217 0 : JAMI_ERR("Failed to add device %s: %s", id.c_str(), e.what()); 218 0 : return false; 219 0 : } 220 38 : notify(); 221 38 : return true; 222 : } 223 : 224 : void 225 0 : VideoDeviceMonitor::removeDevice(const string& id) 226 : { 227 : { 228 0 : std::lock_guard l(lock_); 229 0 : const auto it = findDeviceById(id); 230 0 : if (it == devices_.end()) 231 0 : return; 232 : 233 0 : devices_.erase(it); 234 0 : if (defaultDevice_.find(id) != std::string::npos) { 235 0 : defaultDevice_.clear(); 236 0 : for (const auto& dev : devices_) 237 0 : if (dev.name != DEVICE_DESKTOP) { 238 0 : defaultDevice_ = dev.getDeviceId(); 239 0 : break; 240 : } 241 : } 242 0 : } 243 0 : notify(); 244 : } 245 : 246 : vector<VideoDevice>::iterator 247 98 : VideoDeviceMonitor::findDeviceById(const string& id) 248 : { 249 98 : for (auto it = devices_.begin(); it != devices_.end(); ++it) 250 60 : if (it->getDeviceId().find(id) != std::string::npos) 251 60 : return it; 252 38 : return devices_.end(); 253 : } 254 : 255 : vector<VideoDevice>::const_iterator 256 1391 : VideoDeviceMonitor::findDeviceById(const string& id) const 257 : { 258 1391 : for (auto it = devices_.cbegin(); it != devices_.cend(); ++it) 259 1391 : if (it->getDeviceId().find(id) != std::string::npos) 260 1391 : return it; 261 0 : return devices_.end(); 262 : } 263 : 264 : vector<VideoSettings>::iterator 265 68 : VideoDeviceMonitor::findPreferencesById(const string& id) 266 : { 267 68 : for (auto it = preferences_.begin(); it != preferences_.end(); ++it) 268 30 : if (it->unique_id.find(id) != std::string::npos) 269 30 : return it; 270 38 : return preferences_.end(); 271 : } 272 : 273 : void 274 30 : VideoDeviceMonitor::overwritePreferences(const VideoSettings& settings) 275 : { 276 30 : auto it = findPreferencesById(settings.unique_id); 277 30 : if (it != preferences_.end()) 278 30 : preferences_.erase(it); 279 30 : preferences_.emplace_back(settings); 280 30 : } 281 : 282 : void 283 1738 : VideoDeviceMonitor::serialize(YAML::Emitter& out) const 284 : { 285 1738 : std::lock_guard l(lock_); 286 1738 : out << YAML::Key << "devices" << YAML::Value << preferences_; 287 1738 : } 288 : 289 : void 290 30 : VideoDeviceMonitor::unserialize(const YAML::Node& in) 291 : { 292 30 : std::lock_guard l(lock_); 293 30 : const auto& node = in[CONFIG_LABEL]; 294 : 295 : /* load the device list from the "video" YAML section */ 296 30 : const auto& devices = node["devices"]; 297 60 : for (const auto& dev : devices) { 298 30 : VideoSettings pref = dev.as<VideoSettings>(); 299 30 : if (pref.unique_id.empty()) 300 0 : continue; // discard malformed section 301 30 : overwritePreferences(pref); 302 30 : auto itd = findDeviceById(pref.unique_id); 303 30 : if (itd != devices_.end()) 304 30 : itd->applySettings(pref); 305 60 : } 306 : 307 : // Restore the default device if present, or select the first one 308 30 : const string prefId = preferences_.empty() ? "" : preferences_[0].unique_id; 309 30 : const auto devIter = findDeviceById(prefId); 310 30 : if (devIter != devices_.end() && prefId != DEVICE_DESKTOP) { 311 0 : defaultDevice_ = devIter->getDeviceId(); 312 : } else { 313 30 : defaultDevice_.clear(); 314 60 : for (const auto& dev : devices_) 315 30 : if (dev.name != DEVICE_DESKTOP) { 316 0 : defaultDevice_ = dev.getDeviceId(); 317 0 : break; 318 : } 319 : } 320 30 : } 321 : 322 : } // namespace video 323 : } // namespace jami