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