| /* |
| * Copyright (C) 2004-2021 Savoir-faire Linux Inc. |
| * |
| * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> |
| * Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
| * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> |
| * |
| * 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, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <sstream> |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
| #include <yaml-cpp/yaml.h> |
| #pragma GCC diagnostic pop |
| |
| #include "manager.h" |
| #include "media_const.h" |
| #include "client/videomanager.h" |
| #include "client/ring_signal.h" |
| #include "config/yamlparser.h" |
| #include "logger.h" |
| #include "video_device_monitor.h" |
| |
| namespace jami { |
| namespace video { |
| |
| constexpr const char* const VideoDeviceMonitor::CONFIG_LABEL; |
| |
| using std::map; |
| using std::string; |
| using std::vector; |
| |
| vector<string> |
| VideoDeviceMonitor::getDeviceList() const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| vector<string> ids; |
| ids.reserve(devices_.size()); |
| for (const auto& dev : devices_) { |
| if (dev.name != DEVICE_DESKTOP) |
| ids.emplace_back(dev.getDeviceId()); |
| } |
| return ids; |
| } |
| |
| DRing::VideoCapabilities |
| VideoDeviceMonitor::getCapabilities(const string& id) const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto iter = findDeviceById(id); |
| if (iter == devices_.end()) |
| return DRing::VideoCapabilities(); |
| |
| return iter->getCapabilities(); |
| } |
| |
| VideoSettings |
| VideoDeviceMonitor::getSettings(const string& id) |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| |
| const auto prefIter = findPreferencesById(id); |
| if (prefIter == preferences_.end()) |
| return VideoSettings(); |
| |
| return *prefIter; |
| } |
| |
| void |
| VideoDeviceMonitor::applySettings(const string& id, const VideoSettings& settings) |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto iter = findDeviceById(id); |
| |
| if (iter == devices_.end()) |
| return; |
| |
| iter->applySettings(settings); |
| auto it = findPreferencesById(settings.unique_id); |
| if (it != preferences_.end()) |
| (*it) = settings; |
| } |
| |
| string |
| VideoDeviceMonitor::getDefaultDevice() const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto it = findDeviceById(defaultDevice_); |
| if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) |
| return {}; |
| return it->getDeviceId(); |
| } |
| |
| std::string |
| VideoDeviceMonitor::getMRLForDefaultDevice() const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto it = findDeviceById(defaultDevice_); |
| if (it == std::end(devices_) || it->getDeviceId() == DEVICE_DESKTOP) |
| return {}; |
| static const std::string sep = DRing::Media::VideoProtocolPrefix::SEPARATOR; |
| return DRing::Media::VideoProtocolPrefix::CAMERA + sep + it->getDeviceId(); |
| } |
| |
| bool |
| VideoDeviceMonitor::setDefaultDevice(const std::string& id) |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto itDev = findDeviceById(id); |
| if (itDev != devices_.end()) { |
| if (defaultDevice_ == itDev->getDeviceId()) |
| return false; |
| defaultDevice_ = itDev->getDeviceId(); |
| |
| // place it at the begining of the prefs |
| auto itPref = findPreferencesById(itDev->getDeviceId()); |
| if (itPref != preferences_.end()) { |
| auto settings = *itPref; |
| preferences_.erase(itPref); |
| preferences_.insert(preferences_.begin(), settings); |
| } else { |
| preferences_.insert(preferences_.begin(), itDev->getSettings()); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| void |
| VideoDeviceMonitor::setDeviceOrientation(const std::string& id, int angle) |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto itd = findDeviceById(id); |
| if (itd != devices_.cend()) { |
| itd->setOrientation(angle); |
| } else { |
| JAMI_WARN("Can't find device %s to set orientation %d", id.c_str(), angle); |
| } |
| } |
| |
| DeviceParams |
| VideoDeviceMonitor::getDeviceParams(const std::string& id) const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto itd = findDeviceById(id); |
| if (itd == devices_.cend()) |
| return DeviceParams(); |
| return itd->getDeviceParams(); |
| } |
| |
| static void |
| giveUniqueName(VideoDevice& dev, const vector<VideoDevice>& devices) |
| { |
| std::string suffix; |
| uint64_t number = 2; |
| auto unique = true; |
| for (;; unique = static_cast<bool>(++number)) { |
| for (const auto& s : devices) |
| unique &= static_cast<bool>(s.name.compare(dev.name + suffix)); |
| if (unique) |
| return (void) (dev.name += suffix); |
| suffix = " (" + std::to_string(number) + ")"; |
| } |
| } |
| |
| static void |
| notify() |
| { |
| if (Manager::initialized) { |
| emitSignal<DRing::VideoSignal::DeviceEvent>(); |
| } |
| } |
| |
| bool |
| VideoDeviceMonitor::addDevice(const string& id, |
| const std::vector<std::map<std::string, std::string>>& devInfo) |
| { |
| try { |
| std::lock_guard<std::mutex> l(lock_); |
| if (findDeviceById(id) != devices_.end()) |
| return false; |
| |
| // instantiate a new unique device |
| VideoDevice dev {id, devInfo}; |
| |
| if (dev.getChannelList().empty()) |
| return false; |
| |
| giveUniqueName(dev, devices_); |
| |
| // restore its preferences if any, or store the defaults |
| auto it = findPreferencesById(id); |
| if (it != preferences_.end()) { |
| dev.applySettings(*it); |
| } else { |
| dev.applySettings(dev.getDefaultSettings()); |
| preferences_.emplace_back(dev.getSettings()); |
| } |
| |
| // in case there is no default device on a fresh run |
| if (defaultDevice_.empty() && id != DEVICE_DESKTOP) |
| defaultDevice_ = dev.getDeviceId(); |
| |
| devices_.emplace_back(std::move(dev)); |
| } catch (const std::exception& e) { |
| JAMI_ERR("Failed to add device %s: %s", id.c_str(), e.what()); |
| return false; |
| } |
| notify(); |
| return true; |
| } |
| |
| void |
| VideoDeviceMonitor::removeDevice(const string& id) |
| { |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto it = findDeviceById(id); |
| if (it == devices_.end()) |
| return; |
| |
| devices_.erase(it); |
| if (defaultDevice_.find(id) != std::string::npos) { |
| defaultDevice_.clear(); |
| for (const auto& dev : devices_) |
| if (dev.name != DEVICE_DESKTOP) { |
| defaultDevice_ = dev.getDeviceId(); |
| break; |
| } |
| } |
| } |
| notify(); |
| } |
| |
| vector<VideoDevice>::iterator |
| VideoDeviceMonitor::findDeviceById(const string& id) |
| { |
| for (auto it = devices_.begin(); it != devices_.end(); ++it) |
| if (it->getDeviceId().find(id) != std::string::npos) |
| return it; |
| return devices_.end(); |
| } |
| |
| vector<VideoDevice>::const_iterator |
| VideoDeviceMonitor::findDeviceById(const string& id) const |
| { |
| for (auto it = devices_.cbegin(); it != devices_.cend(); ++it) |
| if (it->getDeviceId().find(id) != std::string::npos) |
| return it; |
| return devices_.end(); |
| } |
| |
| vector<VideoSettings>::iterator |
| VideoDeviceMonitor::findPreferencesById(const string& id) |
| { |
| for (auto it = preferences_.begin(); it != preferences_.end(); ++it) |
| if (it->unique_id.find(id) != std::string::npos) |
| return it; |
| return preferences_.end(); |
| } |
| |
| void |
| VideoDeviceMonitor::overwritePreferences(const VideoSettings& settings) |
| { |
| auto it = findPreferencesById(settings.unique_id); |
| if (it != preferences_.end()) |
| preferences_.erase(it); |
| preferences_.emplace_back(settings); |
| } |
| |
| void |
| VideoDeviceMonitor::serialize(YAML::Emitter& out) const |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| out << YAML::Key << "devices" << YAML::Value << preferences_; |
| } |
| |
| void |
| VideoDeviceMonitor::unserialize(const YAML::Node& in) |
| { |
| std::lock_guard<std::mutex> l(lock_); |
| const auto& node = in[CONFIG_LABEL]; |
| |
| /* load the device list from the "video" YAML section */ |
| const auto& devices = node["devices"]; |
| for (const auto& dev : devices) { |
| VideoSettings pref = dev.as<VideoSettings>(); |
| if (pref.unique_id.empty()) |
| continue; // discard malformed section |
| overwritePreferences(pref); |
| auto itd = findDeviceById(pref.unique_id); |
| if (itd != devices_.end()) |
| itd->applySettings(pref); |
| } |
| |
| // Restore the default device if present, or select the first one |
| const string prefId = preferences_.empty() ? "" : preferences_[0].unique_id; |
| const auto devIter = findDeviceById(prefId); |
| if (devIter != devices_.end() && prefId != DEVICE_DESKTOP) { |
| defaultDevice_ = devIter->getDeviceId(); |
| } else { |
| defaultDevice_.clear(); |
| for (const auto& dev : devices_) |
| if (dev.name != DEVICE_DESKTOP) { |
| defaultDevice_ = dev.getDeviceId(); |
| break; |
| } |
| } |
| } |
| |
| } // namespace video |
| } // namespace jami |