hyperion.ng/src/hyperiond/hyperiond.cpp

418 lines
14 KiB
C++
Raw Normal View History

#include <unistd.h>
#include <cassert>
#include <stdlib.h>
#include <QCoreApplication>
#include <QResource>
#include <QLocale>
create debian packages / multiple configs (#650) * implement make install set CMAKE_INSTALL_PREFIX e.g. to /opt to install to /opt/hyperion set ENABLE_SYSTEM_INSTALL to ON to activate installation after compiling use make install or make install/strip (for performance/size optimized binaries - compile in Release to get best performance) * cleanup cmake files use cmake -DINSTALL_PREFIX=/opt/hyperion .. to install all files to hyperion or cmake -DINSTALL_PREFIX=/usr/ to install to usr. install folders are linux standard. bin go to bin folder and additionals (effects) go to share/hyperion * add uninstall target - be patient with that, this will remove files from your system install service files to share/hyperion - if you want to use them you have to make a symlink to your location of service files * optimize build release script install service files into hyperion share folder (services not activated, this must be done by distribution package script) initial support of cmake option -DPLATFORM= option. This selects platform specific cmake flags. no need for -DENABLE_... options (unless you want some special things) automatic detect for apple build * update submodule * fix cmake error when no platform is given * initial support for deb,rpm and tgz packages - no usefull content atm! * make packeages contain usefull stuff * add license make packes more functional. package specific install missing yet * implement debian postinstall * disable rpm generation until it has a working state * add hypercon compat * add posibility for multiple config files. first one found is taken Former-commit-id: 1c2669961da98fd05a97359e75f1d6d68e126715
2016-05-24 19:56:43 +02:00
#include <QFile>
#include <QHostAddress>
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
JSON Auto correction + hyperion schema split for better readability (#452) * revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * revoke schema split * revoke schema split * Prevent compiler error if none grabber is available * revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * remove "configMigrator" * remove "configMigrator" * Change TestConfigFile to show how the autocorrection works * revoke schema split * revoke schema split * remove "ConfigMigrator" * remove "ConfigMigrator" * remove "ConfigMigratorBase" * remove "ConfigMigratorBase" * Add QJsonUtils.h * added ability "ignore-required" It has been added the ability to correct the configuration without having to pay attention to the keyword "required" in the hyperion schema * Allow Comments in Hyperion Schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * //Allow Comments in Hyperion Schema * Update jsonschema.py to version 0.8.0 to support ... references in json schema * add RefResolver from jsonschema.py to resolve references in hyperion schema * remove dupe code * split the hyperion schema in separatly files For better readability * add function "resolveReferences" to resolve references in hyperion schema. * remove CURRENT_CONFIG_VERSION * remove CURRENT_CONFIG_VERSION * split the hyperion schema in separatly files For better readability * Create schema-backgroundEffect.json * Add the rest of the Hyperion schema via upload * Remove Comments in config file * Add return variable to function writeJson(). fix function resolveReferences(). edit function load() to handle QPair result from schemaChecker. * edit function validate() to return QPair variable * fit function loadEffectDefinition() * fit function checkJson() * Expand error check by dividing "_error" variable in "_error" and "_schemaError". Replace variable "bool" in validate() in QPair * Extend function "cmd_cfg_set" to handle auto correction * Extend function "loadConfig" to handle auto correction * fix function loadConfig()
2017-07-30 13:32:10 +02:00
#include <QPair>
#include <cstdint>
#include <limits>
#include <QThread>
#include <utils/Components.h>
#include <utils/JsonUtils.h>
2013-08-14 10:54:49 +02:00
#include <hyperion/Hyperion.h>
#include <jsonserver/JsonServer.h>
#include <protoserver/ProtoServer.h>
#include <udplistener/UDPListener.h>
2018-12-27 23:11:32 +01:00
#include <webserver/WebServer.h>
#include <utils/Stats.h>
#include <HyperionConfig.h> // Required to determine the cmake options
#include "hyperiond.h"
// FlatBufferServer
#include <flatbufserver/FlatBufferServer.h>
2018-12-27 23:11:32 +01:00
// bonjour browser
#include <bonjour/bonjourbrowserwrapper.h>
// settings
#include <hyperion/SettingsManager.h>
// Init Python
#include <python/PythonInit.h>
HyperionDaemon* HyperionDaemon::daemon = nullptr;
HyperionDaemon::HyperionDaemon(QString configFile, const QString rootPath, QObject *parent, const bool& logLvlOverwrite)
: QObject(parent)
2018-12-27 23:11:32 +01:00
, _log(Logger::getInstance("DAEMON"))
, _bonjourBrowserWrapper(new BonjourBrowserWrapper())
, _pyInit(new PythonInit())
, _webserver(nullptr)
, _jsonServer(nullptr)
, _protoServer(nullptr)
, _udpListener(nullptr)
, _v4l2Grabbers()
, _dispmanx(nullptr)
, _x11Grabber(nullptr)
, _amlGrabber(nullptr)
, _fbGrabber(nullptr)
, _osxGrabber(nullptr)
, _hyperion(nullptr)
, _stats(nullptr)
2018-12-27 23:11:32 +01:00
, _currVideoMode(VIDEO_2D)
{
2018-12-27 23:11:32 +01:00
HyperionDaemon::daemon = this;
// Register metas for thread queued connection
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
qRegisterMetaType<hyperion::Components>("hyperion::Components");
qRegisterMetaType<settings::type>("settings::type");
2018-12-27 23:11:32 +01:00
// init settings
_settingsManager = new SettingsManager(0,configFile);
2016-10-14 13:52:21 +02:00
// set inital log lvl if the loglvl wasn't overwritten by arg
if(!logLvlOverwrite)
handleSettingsUpdate(settings::LOGGER, _settingsManager->getSetting(settings::LOGGER));
// spawn all Hyperion instances before network services
2018-12-27 23:11:32 +01:00
_hyperion = Hyperion::initInstance(this, 0, configFile, rootPath);
2016-10-14 13:52:21 +02:00
Info(_log, "Hyperion initialized");
2018-12-27 23:11:32 +01:00
connect(_hyperion,SIGNAL(closing()),this,SLOT(freeObjects()));
// listen for setting changes
connect(_hyperion, &Hyperion::settingsChanged, this, &HyperionDaemon::settingsChanged);
// listen for setting changes of framegrabber and v4l2
connect(this, &HyperionDaemon::settingsChanged, this, &HyperionDaemon::handleSettingsUpdate);
// forward system and v4l images to Hyperion
connect(this, &HyperionDaemon::systemImage, _hyperion, &Hyperion::systemImage);
connect(this, &HyperionDaemon::v4lImage, _hyperion, &Hyperion::v4lImage);
// forward videoModes from Hyperion to Daemon evaluation
connect(_hyperion, &Hyperion::videoMode, this, &HyperionDaemon::setVideoMode);
// forward videoMode changes from Daemon to Hyperion
connect(this, &HyperionDaemon::videoMode, _hyperion, &Hyperion::newVideoMode);
// ---- grabber -----
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_AMLOGIC)
Warning(_log, "No platform capture can be instantiated, because all grabbers have been left out from the build");
#endif
// init system capture (framegrabber)
handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
// init v4l2 capture
handleSettingsUpdate(settings::V4L2, getSetting(settings::V4L2));
// ---- network services -----
startNetworkServices();
}
HyperionDaemon::~HyperionDaemon()
{
freeObjects();
delete _hyperion;
2018-12-27 23:11:32 +01:00
delete _settingsManager;
delete _pyInit;
}
quint16 HyperionDaemon::getWebServerPort()
{
return _webserver->getPort();
}
void HyperionDaemon::setVideoMode(const VideoMode& mode)
{
if(_currVideoMode != mode)
{
_currVideoMode = mode;
emit videoMode(mode);
}
}
const QJsonDocument HyperionDaemon::getSetting(const settings::type &type)
{
return _settingsManager->getSetting(type);
}
void HyperionDaemon::freeObjects()
{
_hyperion->clearall(true);
2018-12-27 23:11:32 +01:00
// destroy network first as a client might want to access pointers
delete _webserver;
delete _jsonServer;
delete _protoServer;
_flatBufferServer->thread()->quit();
_flatBufferServer->thread()->wait(1000);
2018-12-27 23:11:32 +01:00
delete _udpListener;
delete _bonjourBrowserWrapper;
delete _amlGrabber;
delete _dispmanx;
delete _fbGrabber;
delete _osxGrabber;
for(V4L2Wrapper* grabber : _v4l2Grabbers)
{
delete grabber;
}
delete _stats;
_v4l2Grabbers.clear();
2018-12-27 23:11:32 +01:00
_bonjourBrowserWrapper = nullptr;
_amlGrabber = nullptr;
_dispmanx = nullptr;
_fbGrabber = nullptr;
_osxGrabber = nullptr;
2018-12-27 23:11:32 +01:00
_webserver = nullptr;
_jsonServer = nullptr;
_protoServer = nullptr;
_udpListener = nullptr;
_stats = nullptr;
}
2018-12-27 23:11:32 +01:00
void HyperionDaemon::startNetworkServices()
{
// Create Stats
_stats = new Stats(_settingsManager->getSettings());
2018-12-27 23:11:32 +01:00
// Create Json server
_jsonServer = new JsonServer(getSetting(settings::JSONSERVER));
connect(this, &HyperionDaemon::settingsChanged, _jsonServer, &JsonServer::handleSettingsUpdate);
2018-12-27 23:11:32 +01:00
// Create Proto server
_protoServer = new ProtoServer(getSetting(settings::PROTOSERVER));
connect(this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate);
// Create FlatBuffer server & move to Thread
_flatBufferServer = new FlatBufferServer(getSetting(settings::FLATBUFSERVER));
connect(this, &HyperionDaemon::settingsChanged, _flatBufferServer, &FlatBufferServer::handleSettingsUpdate);
QThread* fbThread = new QThread(this);
_flatBufferServer->moveToThread(fbThread);
connect( fbThread, &QThread::started, _flatBufferServer, &FlatBufferServer::initServer );
connect( fbThread, &QThread::finished, _flatBufferServer, &QObject::deleteLater );
connect( fbThread, &QThread::finished, fbThread, &QObject::deleteLater );
fbThread->start();
JSON Auto correction + hyperion schema split for better readability (#452) * revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * revoke schema split * revoke schema split * Prevent compiler error if none grabber is available * revoke schema split * add "getAutoCorrectedConfig" function * revoke schema split * remove "configMigrator" * remove "configMigrator" * Change TestConfigFile to show how the autocorrection works * revoke schema split * revoke schema split * remove "ConfigMigrator" * remove "ConfigMigrator" * remove "ConfigMigratorBase" * remove "ConfigMigratorBase" * Add QJsonUtils.h * added ability "ignore-required" It has been added the ability to correct the configuration without having to pay attention to the keyword "required" in the hyperion schema * Allow Comments in Hyperion Schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * add ability to ignore the "required" keyword in hyperion schema * //Allow Comments in Hyperion Schema * Update jsonschema.py to version 0.8.0 to support ... references in json schema * add RefResolver from jsonschema.py to resolve references in hyperion schema * remove dupe code * split the hyperion schema in separatly files For better readability * add function "resolveReferences" to resolve references in hyperion schema. * remove CURRENT_CONFIG_VERSION * remove CURRENT_CONFIG_VERSION * split the hyperion schema in separatly files For better readability * Create schema-backgroundEffect.json * Add the rest of the Hyperion schema via upload * Remove Comments in config file * Add return variable to function writeJson(). fix function resolveReferences(). edit function load() to handle QPair result from schemaChecker. * edit function validate() to return QPair variable * fit function loadEffectDefinition() * fit function checkJson() * Expand error check by dividing "_error" variable in "_error" and "_schemaError". Replace variable "bool" in validate() in QPair * Extend function "cmd_cfg_set" to handle auto correction * Extend function "loadConfig" to handle auto correction * fix function loadConfig()
2017-07-30 13:32:10 +02:00
2018-12-27 23:11:32 +01:00
// Create UDP listener
_udpListener = new UDPListener(getSetting(settings::UDPLISTENER));
connect(this, &HyperionDaemon::settingsChanged, _udpListener, &UDPListener::handleSettingsUpdate);
2018-12-27 23:11:32 +01:00
// Create Webserver
_webserver = new WebServer(getSetting(settings::WEBSERVER));
connect(this, &HyperionDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate);
}
2018-12-27 23:11:32 +01:00
void HyperionDaemon::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
{
if(type == settings::LOGGER)
{
const QJsonObject & logConfig = config.object();
std::string level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug
if (level == "silent") Logger::setLogLevel(Logger::OFF);
else if (level == "warn") Logger::setLogLevel(Logger::WARNING);
else if (level == "verbose") Logger::setLogLevel(Logger::INFO);
else if (level == "debug") Logger::setLogLevel(Logger::DEBUG);
}
2018-12-27 23:11:32 +01:00
if(type == settings::SYSTEMCAPTURE)
{
const QJsonObject & grabberConfig = config.object();
2018-12-27 23:11:32 +01:00
_grabber_width = grabberConfig["width"].toInt(96);
_grabber_height = grabberConfig["height"].toInt(96);
_grabber_frequency = grabberConfig["frequency_Hz"].toInt(10);
2018-12-27 23:11:32 +01:00
_grabber_cropLeft = grabberConfig["cropLeft"].toInt(0);
_grabber_cropRight = grabberConfig["cropRight"].toInt(0);
_grabber_cropTop = grabberConfig["cropTop"].toInt(0);
_grabber_cropBottom = grabberConfig["cropBottom"].toInt(0);
2018-12-27 23:11:32 +01:00
#ifdef ENABLE_OSX
QString type = "osx";
#else
QString type = grabberConfig["type"].toString("auto");
#endif
2018-12-27 23:11:32 +01:00
// auto eval of type
if ( type == "auto" )
{
2018-12-27 23:11:32 +01:00
// dispmanx -> on raspi
if (QFile::exists("/dev/vchiq"))
{
type = "dispmanx";
}
// amlogic -> /dev/amvideo exists
else if ( QFile::exists("/dev/amvideo") && ( QFile::exists("/dev/amvideocap0") || QFile::exists("/dev/ge2d") ) )
{
type = "amlogic";
}
// x11 -> if DISPLAY is set
else if (getenv("DISPLAY") != NULL )
{
type = "x11";
}
// framebuffer -> if nothing other applies
else
{
type = "framebuffer";
}
Info( _log, "set screen capture device to '%s'", QSTRING_CSTR(type));
}
2018-12-27 23:11:32 +01:00
if (type == "") { Info( _log, "screen capture device disabled"); }
else if (type == "framebuffer" && _fbGrabber == nullptr) createGrabberFramebuffer(grabberConfig);
else if (type == "dispmanx" && _dispmanx == nullptr) createGrabberDispmanx();
else if (type == "amlogic" && _amlGrabber == nullptr) createGrabberAmlogic();
else if (type == "osx" && _osxGrabber == nullptr) createGrabberOsx(grabberConfig);
else if (type == "x11" && _x11Grabber == nullptr) createGrabberX11(grabberConfig);
}
2018-12-27 23:11:32 +01:00
else if(type == settings::V4L2)
{
2018-12-27 23:11:32 +01:00
// stop
if(_v4l2Grabbers.size()>0)
return;
2018-12-27 23:11:32 +01:00
unsigned v4lEnableCount = 0;
2018-12-27 23:11:32 +01:00
const QJsonArray & v4lArray = config.array();
for ( signed idx=0; idx<v4lArray.size(); idx++)
{
const QJsonObject & grabberConfig = v4lArray.at(idx).toObject();
2018-12-27 23:11:32 +01:00
#ifdef ENABLE_V4L2
V4L2Wrapper* grabber = new V4L2Wrapper(
grabberConfig["device"].toString("auto"),
parseVideoStandard(grabberConfig["standard"].toString("no-change")),
parsePixelFormat(grabberConfig["pixelFormat"].toString("no-change")),
grabberConfig["sizeDecimation"].toInt(8) );
grabber->setSignalThreshold(
grabberConfig["redSignalThreshold"].toDouble(0.0)/100.0,
grabberConfig["greenSignalThreshold"].toDouble(0.0)/100.0,
grabberConfig["blueSignalThreshold"].toDouble(0.0)/100.0);
grabber->setCropping(
grabberConfig["cropLeft"].toInt(0),
grabberConfig["cropRight"].toInt(0),
grabberConfig["cropTop"].toInt(0),
grabberConfig["cropBottom"].toInt(0));
grabber->setSignalDetectionEnable(grabberConfig["signalDetection"].toBool(true));
grabber->setSignalDetectionOffset(
grabberConfig["sDHOffsetMin"].toDouble(0.25),
grabberConfig["sDVOffsetMin"].toDouble(0.25),
grabberConfig["sDHOffsetMax"].toDouble(0.75),
grabberConfig["sDVOffsetMax"].toDouble(0.75));
Debug(_log, "V4L2 grabber created");
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(grabber, &V4L2Wrapper::systemImage, this, &HyperionDaemon::v4lImage);
connect(this, &HyperionDaemon::videoMode, grabber, &V4L2Wrapper::setVideoMode);
connect(this, &HyperionDaemon::settingsChanged, grabber, &V4L2Wrapper::handleSettingsUpdate);
2018-12-27 23:11:32 +01:00
_v4l2Grabbers.push_back(grabber);
#endif
}
2018-12-27 23:11:32 +01:00
ErrorIf( (v4lEnableCount>0 && _v4l2Grabbers.size()==0), _log, "The v4l2 grabber can not be instantiated, because it has been left out from the build");
}
}
void HyperionDaemon::createGrabberDispmanx()
{
#ifdef ENABLE_DISPMANX
2018-12-27 23:11:32 +01:00
_dispmanx = new DispmanxWrapper(_grabber_width, _grabber_height, _grabber_frequency);
_dispmanx->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _dispmanx, &DispmanxWrapper::setVideoMode);
connect(_dispmanx, &DispmanxWrapper::systemImage, this, &HyperionDaemon::systemImage);
connect(this, &HyperionDaemon::settingsChanged, _dispmanx, &DispmanxWrapper::handleSettingsUpdate);
_dispmanx->start();
Info(_log, "DISPMANX frame grabber created and started");
#else
2018-12-27 23:11:32 +01:00
Error( _log, "The dispmanx framegrabber can not be instantiated, because it has been left out from the build");
#endif
}
void HyperionDaemon::createGrabberAmlogic()
{
#ifdef ENABLE_AMLOGIC
2018-12-27 23:11:32 +01:00
_amlGrabber = new AmlogicWrapper(_grabber_width, _grabber_height, _grabber_frequency);
_amlGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _amlGrabber, &AmlogicWrapper::setVideoMode);
connect(_amlGrabber, &AmlogicWrapper::systemImage, this, &HyperionDaemon::systemImage);
connect(this, &HyperionDaemon::settingsChanged, _amlGrabber, &AmlogicWrapper::handleSettingsUpdate);
_amlGrabber->start();
Info(_log, "AMLOGIC grabber created and started");
#else
Error( _log, "The AMLOGIC grabber can not be instantiated, because it has been left out from the build");
#endif
}
void HyperionDaemon::createGrabberX11(const QJsonObject & grabberConfig)
{
#ifdef ENABLE_X11
_x11Grabber = new X11Wrapper(
_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom,
2018-12-27 23:11:32 +01:00
grabberConfig["pixelDecimation"].toInt(8),
_grabber_frequency );
_x11Grabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _x11Grabber, &X11Wrapper::setVideoMode);
connect(_x11Grabber, &X11Wrapper::systemImage, this, &HyperionDaemon::systemImage);
connect(this, &HyperionDaemon::settingsChanged, _x11Grabber, &X11Wrapper::handleSettingsUpdate);
_x11Grabber->start();
Info(_log, "X11 grabber created and started");
#else
Error(_log, "The X11 grabber can not be instantiated, because it has been left out from the build");
#endif
}
void HyperionDaemon::createGrabberFramebuffer(const QJsonObject & grabberConfig)
{
#ifdef ENABLE_FB
// Construct and start the framebuffer grabber if the configuration is present
_fbGrabber = new FramebufferWrapper(
grabberConfig["device"].toString("/dev/fb0"),
2018-12-27 23:11:32 +01:00
_grabber_width, _grabber_height, _grabber_frequency);
_fbGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _fbGrabber, &FramebufferWrapper::setVideoMode);
connect(_fbGrabber, &FramebufferWrapper::systemImage, this, &HyperionDaemon::systemImage);
connect(this, &HyperionDaemon::settingsChanged, _fbGrabber, &FramebufferWrapper::handleSettingsUpdate);
_fbGrabber->start();
Info(_log, "Framebuffer grabber created and started");
#else
Error(_log, "The framebuffer grabber can not be instantiated, because it has been left out from the build");
#endif
}
void HyperionDaemon::createGrabberOsx(const QJsonObject & grabberConfig)
{
#ifdef ENABLE_OSX
// Construct and start the osx grabber if the configuration is present
_osxGrabber = new OsxWrapper(
grabberConfig["display"].toInt(0),
2018-12-27 23:11:32 +01:00
_grabber_width, _grabber_height, _grabber_frequency);
2018-12-27 23:11:32 +01:00
// connect to HyperionDaemon signal
connect(this, &HyperionDaemon::videoMode, _osxGrabber, &OsxWrapper::setVideoMode);
connect(_osxGrabber, &OsxWrapper::systemImage, this, &HyperionDaemon::systemImage);
connect(this, &HyperionDaemon::settingsChanged, _osxGrabber, &OsxWrapper::handleSettingsUpdate);
_osxGrabber->start();
Info(_log, "OSX grabber created and started");
#else
Error(_log, "The osx grabber can not be instantiated, because it has been left out from the build");
#endif
}