blob: 78444f8147d97eebcd74eff85216fc58fd59b069 [file] [log] [blame]
/*
* Copyright (C) 2004-2015 Savoir-faire Linux Inc.
*
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Vivien Didelot <vivien.didelot@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 "client/videomanager.h"
#include "client/ring_signal.h"
#include "config/yamlparser.h"
#include "logger.h"
#include "video_device_monitor.h"
namespace ring { namespace video {
constexpr const char * const VideoDeviceMonitor::CONFIG_LABEL;
using std::map;
using std::string;
using std::stringstream;;
using std::vector;
vector<string>
VideoDeviceMonitor::getDeviceList() const
{
vector<string> names;
for (const auto& dev : devices_)
names.push_back(dev.name);
return names;
}
DRing::VideoCapabilities
VideoDeviceMonitor::getCapabilities(const string& name) const
{
const auto iter = findDeviceByName(name);
if (iter == devices_.end())
return DRing::VideoCapabilities();
return iter->getCapabilities();
}
VideoSettings
VideoDeviceMonitor::getSettings(const string& name)
{
const auto itd = findDeviceByName(name);
if (itd == devices_.end())
return VideoSettings();
return itd->getSettings();
}
void
VideoDeviceMonitor::applySettings(const string& name, VideoSettings settings)
{
const auto iter = findDeviceByName(name);
if (iter == devices_.end())
return;
iter->applySettings(settings);
overwritePreferences(iter->getSettings());
}
string
VideoDeviceMonitor::getDefaultDevice() const
{
return defaultDevice_;
}
std::string
VideoDeviceMonitor::getMRLForDefaultDevice() const
{
const auto it = findDeviceByName(defaultDevice_);
if(it == std::end(devices_))
return {};
return "camera://" + it->getSettings().name;
}
void
VideoDeviceMonitor::setDefaultDevice(const std::string& name)
{
const auto it = findDeviceByName(name);
if (it != devices_.end())
defaultDevice_ = it->name;
}
DeviceParams
VideoDeviceMonitor::getDeviceParams(const std::string& name) const
{
const auto itd = findDeviceByName(name);
if (itd == devices_.cend())
return DeviceParams();
return itd->getDeviceParams();
}
static int
getNumber(const string &name, size_t *sharp)
{
size_t len = name.length();
// name is too short to be numbered
if (len < 3)
return -1;
for (size_t c = len; c; --c) {
if (name[c] == '#') {
unsigned i;
if (sscanf(name.substr(c).c_str(), "#%u", &i) != 1)
return -1;
*sharp = c;
return i;
}
}
return -1;
}
static void
giveUniqueName(VideoDevice &dev, const vector<VideoDevice> &devices)
{
start:
for (auto &item : devices) {
if (dev.name == item.name) {
size_t sharp;
int num = getNumber(dev.name, &sharp);
if (num < 0) // not numbered
dev.name += " #0";
else {
stringstream ss;
ss << num + 1;
dev.name.replace(sharp + 1, ss.str().length(), ss.str());
}
goto start; // we changed the name, let's look again if it is unique
}
}
}
static void
notify()
{
if (!Manager::initialized) {
RING_WARN("Manager not initialized yet");
return;
}
emitSignal<DRing::VideoSignal::DeviceEvent>();
}
void
VideoDeviceMonitor::addDevice(const string& node)
{
if (findDeviceByNode(node) != devices_.end())
return;
// instantiate a new unique device
try {
VideoDevice dev(node);
giveUniqueName(dev, devices_);
// restore its preferences if any, or store the defaults
auto it = findPreferencesByName(dev.name);
if (it != preferences_.end())
dev.applySettings(*it);
else
preferences_.push_back(dev.getSettings());
// in case there is no default device on a fresh run
if (defaultDevice_.empty())
defaultDevice_ = dev.name;
devices_.push_back(dev);
notify();
} catch (const std::runtime_error& e) {
RING_ERR("%s", e.what());
return;
}
}
void
VideoDeviceMonitor::removeDevice(const string& node)
{
const auto it = findDeviceByNode(node);
if (it == devices_.end())
return;
if (defaultDevice_ == it->name)
defaultDevice_.clear();
devices_.erase(it);
notify();
}
vector<VideoDevice>::iterator
VideoDeviceMonitor::findDeviceByName(const string& name)
{
vector<VideoDevice>::iterator it;
for (it = devices_.begin(); it != devices_.end(); ++it)
if (it->name == name)
break;
return it;
}
vector<VideoDevice>::const_iterator
VideoDeviceMonitor::findDeviceByName(const string& name) const
{
vector<VideoDevice>::const_iterator it;
for (it = devices_.cbegin(); it != devices_.cend(); ++it)
if (it->name == name)
break;
return it;
}
vector<VideoDevice>::iterator
VideoDeviceMonitor::findDeviceByNode(const string& node)
{
vector<VideoDevice>::iterator it;
for (it = devices_.begin(); it != devices_.end(); ++it)
if (it->getNode() == node)
break;
return it;
}
vector<VideoDevice>::const_iterator
VideoDeviceMonitor::findDeviceByNode(const string& node) const
{
vector<VideoDevice>::const_iterator it;
for (it = devices_.cbegin(); it != devices_.cend(); ++it)
if (it->getNode() == node)
break;
return it;
}
vector<VideoSettings>::iterator
VideoDeviceMonitor::findPreferencesByName(const string& name)
{
for (auto it = preferences_.begin(); it != preferences_.end(); ++it)
if (it->name == name) return it;
return preferences_.end();
}
void
VideoDeviceMonitor::overwritePreferences(VideoSettings settings)
{
auto it = findPreferencesByName(settings.name);
if (it != preferences_.end())
preferences_.erase(it);
preferences_.push_back(settings);
}
void
VideoDeviceMonitor::serialize(YAML::Emitter &out)
{
out << YAML::Key << CONFIG_LABEL << YAML::Value << YAML::BeginMap;
out << YAML::Key << "devices" << YAML::Value << preferences_;
out << YAML::EndMap;
}
void
VideoDeviceMonitor::unserialize(const YAML::Node &in)
{
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.name.empty())
continue; // discard malformed section
overwritePreferences(pref);
auto itd = findDeviceByName(pref.name);
if (itd != devices_.end())
itd->applySettings(pref);
}
// Restore the default device if present, or select the first one
const string pref = preferences_.empty() ? "" : preferences_[0].name;
const string first = devices_.empty() ? "" : devices_[0].name;
if (findDeviceByName(pref) != devices_.end())
defaultDevice_ = pref;
else
defaultDevice_ = first;
}
}} // namespace ring::video