mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Merge remote-tracking branch 'refs/remotes/tvdzwan/master'
# Conflicts: # include/hyperion/Hyperion.h # libsrc/hyperion/CMakeLists.txt # libsrc/hyperion/Hyperion.cpp Former-commit-id: 1144520581d4531952038d2118cb11e01bebc10e
This commit is contained in:
@@ -1,11 +1,27 @@
|
||||
|
||||
#include <iostream>
|
||||
// BlackBorders includes
|
||||
#include <blackborder/BlackBorderDetector.h>
|
||||
#include <cmath>
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
BlackBorderDetector::BlackBorderDetector(uint8_t blackborderThreshold) :
|
||||
_blackborderThreshold(blackborderThreshold)
|
||||
BlackBorderDetector::BlackBorderDetector(double threshold) :
|
||||
_blackborderThreshold(calculateThreshold(threshold))
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
uint8_t BlackBorderDetector::calculateThreshold(double threshold)
|
||||
{
|
||||
int rgbThreshold = int(std::ceil(threshold * 255));
|
||||
if (rgbThreshold < 0)
|
||||
rgbThreshold = 0;
|
||||
else if (rgbThreshold > 255)
|
||||
rgbThreshold = 255;
|
||||
|
||||
uint8_t blackborderThreshold = uint8_t(rgbThreshold);
|
||||
|
||||
std::cout << "Black border threshold set to " << threshold << " (" << int(blackborderThreshold) << ")" << std::endl;
|
||||
|
||||
return blackborderThreshold;
|
||||
}
|
||||
|
@@ -1,21 +1,28 @@
|
||||
#include <iostream>
|
||||
/*
|
||||
#include <iomanip>
|
||||
using std::setw;
|
||||
//*/
|
||||
|
||||
// Blackborder includes
|
||||
#include <blackborder/BlackBorderProcessor.h>
|
||||
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
BlackBorderProcessor::BlackBorderProcessor(const unsigned unknownFrameCnt,
|
||||
const unsigned borderFrameCnt,
|
||||
const unsigned blurRemoveCnt,
|
||||
uint8_t blackborderThreshold) :
|
||||
_unknownSwitchCnt(unknownFrameCnt),
|
||||
_borderSwitchCnt(borderFrameCnt),
|
||||
_blurRemoveCnt(blurRemoveCnt),
|
||||
_detector(blackborderThreshold),
|
||||
BlackBorderProcessor::BlackBorderProcessor(const Json::Value &blackborderConfig) :
|
||||
_unknownSwitchCnt(blackborderConfig.get("unknownFrameCnt", 600).asUInt()),
|
||||
_borderSwitchCnt(blackborderConfig.get("borderFrameCnt", 50).asUInt()),
|
||||
_maxInconsistentCnt(blackborderConfig.get("maxInconsistentCnt", 10).asUInt()),
|
||||
_blurRemoveCnt(blackborderConfig.get("blurRemoveCnt", 1).asUInt()),
|
||||
_detectionMode(blackborderConfig.get("mode", "default").asString()),
|
||||
_detector(blackborderConfig.get("threshold", 0.01).asDouble()),
|
||||
_currentBorder({true, -1, -1}),
|
||||
_previousDetectedBorder({true, -1, -1}),
|
||||
_consistentCnt(0)
|
||||
_consistentCnt(0),
|
||||
_inconsistentCnt(10)
|
||||
{
|
||||
std::cout << "DETECTION MODE:" << _detectionMode << std::endl;
|
||||
// empty
|
||||
}
|
||||
|
||||
@@ -26,13 +33,35 @@ BlackBorder BlackBorderProcessor::getCurrentBorder() const
|
||||
|
||||
bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
|
||||
{
|
||||
// the new changes ignore false small borders (no reset of consistance)
|
||||
// as long as the previous stable state returns within 10 frames
|
||||
// and will only switch to a new border if it is realy detected stable >50 frames
|
||||
|
||||
// sometimes the grabber delivers "bad" frames with a smaller black border (looks like random number every few frames and even when freezing the image)
|
||||
// maybe some interferences of the power supply or bad signal causing this effect - not exactly sure what causes it but changing the power supply of the converter significantly increased that "random" effect on my system
|
||||
// (you can check with the debug output below or if you want i can provide some output logs)
|
||||
// this "random effect" caused the old algorithm to switch to that smaller border immediatly, resulting in a too small border being detected
|
||||
// makes it look like the border detectionn is not working - since the new 3 line detection algorithm is more precise this became a problem specialy in dark scenes
|
||||
// wisc
|
||||
|
||||
// std::cout << "c: " << setw(2) << _currentBorder.verticalSize << " " << setw(2) << _currentBorder.horizontalSize << " p: " << setw(2) << _previousDetectedBorder.verticalSize << " " << setw(2) << _previousDetectedBorder.horizontalSize << " n: " << setw(2) << newDetectedBorder.verticalSize << " " << setw(2) << newDetectedBorder.horizontalSize << " c:i " << setw(2) << _consistentCnt << ":" << setw(2) << _inconsistentCnt << std::endl;
|
||||
|
||||
// set the consistency counter
|
||||
if (newDetectedBorder == _previousDetectedBorder)
|
||||
{
|
||||
++_consistentCnt;
|
||||
_inconsistentCnt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++_inconsistentCnt;
|
||||
if (_inconsistentCnt <= _maxInconsistentCnt)// only few inconsistent frames
|
||||
{
|
||||
//discard the newDetectedBorder -> keep the consistent count for previousDetectedBorder
|
||||
return false;
|
||||
}
|
||||
// the inconsistency threshold is reached
|
||||
// -> give the newDetectedBorder a chance to proof that its consistent
|
||||
_previousDetectedBorder = newDetectedBorder;
|
||||
_consistentCnt = 0;
|
||||
}
|
||||
@@ -41,6 +70,7 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
|
||||
if (_currentBorder == newDetectedBorder)
|
||||
{
|
||||
// No change required
|
||||
_inconsistentCnt = 0; // we have found a consistent border -> reset _inconsistentCnt
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -62,22 +92,7 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
|
||||
_currentBorder = newDetectedBorder;
|
||||
borderChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply smaller borders immediately
|
||||
if (newDetectedBorder.verticalSize < _currentBorder.verticalSize)
|
||||
{
|
||||
_currentBorder.verticalSize = newDetectedBorder.verticalSize;
|
||||
borderChanged = true;
|
||||
}
|
||||
|
||||
if (newDetectedBorder.horizontalSize < _currentBorder.horizontalSize)
|
||||
{
|
||||
_currentBorder.horizontalSize = newDetectedBorder.horizontalSize;
|
||||
borderChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return borderChanged;
|
||||
}
|
||||
}
|
@@ -59,13 +59,16 @@ void BoblightClientConnection::readData()
|
||||
while(bytes > 0)
|
||||
{
|
||||
// create message string (strip the newline)
|
||||
#ifdef ENABLE_QT5
|
||||
QString message = QString::fromLatin1(_receiveBuffer.data(), bytes-1);
|
||||
#else
|
||||
QString message = QString::fromAscii(_receiveBuffer.data(), bytes-1);
|
||||
|
||||
#endif
|
||||
// remove message data from buffer
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
|
||||
// handle message
|
||||
handleMessage(message);
|
||||
// handle trimmed message
|
||||
handleMessage(message.trimmed());
|
||||
|
||||
// drop messages if the buffer is too full
|
||||
if (_receiveBuffer.size() > 100*1024)
|
||||
@@ -132,9 +135,15 @@ void BoblightClientConnection::handleMessage(const QString & message)
|
||||
{
|
||||
if (messageParts[3] == "rgb" && messageParts.size() == 7)
|
||||
{
|
||||
// replace decimal comma with decimal point
|
||||
messageParts[4].replace(',', '.');
|
||||
messageParts[5].replace(',', '.');
|
||||
messageParts[6].replace(',', '.');
|
||||
|
||||
bool rc1, rc2, rc3;
|
||||
uint8_t red = qMax(0, qMin(255, int(255 * messageParts[4].toFloat(&rc1))));
|
||||
|
||||
// check for correct locale should not be needed anymore - please check!
|
||||
if (!rc1)
|
||||
{
|
||||
// maybe a locale issue. switch to a locale with a comma instead of a dot as decimal seperator (or vice versa)
|
||||
|
@@ -17,7 +17,11 @@ set(BoblightServer_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/BoblightClientConnection.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_wrap_cpp(BoblightServer_HEADERS_MOC ${BoblightServer_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
qt4_wrap_cpp(BoblightServer_HEADERS_MOC ${BoblightServer_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(boblightserver
|
||||
${BoblightServer_HEADERS}
|
||||
@@ -26,6 +30,10 @@ add_library(boblightserver
|
||||
${BoblightServer_HEADERS_MOC}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(boblightserver Widgets)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(boblightserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
|
@@ -1,5 +1,7 @@
|
||||
|
||||
find_package(PythonLibs REQUIRED)
|
||||
#OpenElec uses 2.7, if you want to compile for OpenElec require 2.7
|
||||
#find_package(PythonLibs 2.7 REQUIRED)
|
||||
|
||||
# Include the python directory. Also include the parent (which is for example /usr/include)
|
||||
# which may be required when it is not includes by the (cross-) compiler by default.
|
||||
@@ -27,9 +29,13 @@ SET(EffectEngineSOURCES
|
||||
|
||||
set(EffectEngine_RESOURCES ${CURRENT_SOURCE_DIR}/EffectEngine.qrc)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS})
|
||||
qt5_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress")
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS})
|
||||
|
||||
qt4_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress")
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(effectengine
|
||||
${EffectEngineHEADERS}
|
||||
@@ -39,6 +45,10 @@ add_library(effectengine
|
||||
${EffectEngineSOURCES}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(effectengine Widgets)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(effectengine
|
||||
hyperion
|
||||
jsoncpp
|
||||
|
@@ -75,7 +75,11 @@ const std::list<EffectDefinition> &EffectEngine::getEffects() const
|
||||
|
||||
bool EffectEngine::loadEffectDefinition(const std::string &path, const std::string &effectConfigFile, EffectDefinition & effectDefinition)
|
||||
{
|
||||
#ifdef ENABLE_QT5
|
||||
std::string fileName = path + QDir::separator().toLatin1() + effectConfigFile;
|
||||
#else
|
||||
std::string fileName = path + QDir::separator().toAscii() + effectConfigFile;
|
||||
#endif
|
||||
std::ifstream file(fileName.c_str());
|
||||
|
||||
if (!file.is_open())
|
||||
@@ -110,7 +114,11 @@ bool EffectEngine::loadEffectDefinition(const std::string &path, const std::stri
|
||||
|
||||
// setup the definition
|
||||
effectDefinition.name = config["name"].asString();
|
||||
#ifdef ENABLE_QT5
|
||||
effectDefinition.script = path + QDir::separator().toLatin1() + config["script"].asString();
|
||||
#else
|
||||
effectDefinition.script = path + QDir::separator().toAscii() + config["script"].asString();
|
||||
#endif
|
||||
effectDefinition.args = config["args"];
|
||||
|
||||
// return succes
|
||||
|
@@ -13,7 +13,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
// Local includes
|
||||
#include "AmlogicGrabber.h"
|
||||
#include <grabber/AmlogicGrabber.h>
|
||||
|
||||
// Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic
|
||||
#define AMVIDEOCAP_IOC_MAGIC 'V'
|
||||
|
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// STL includes
|
||||
#include <cstdint>
|
||||
|
||||
// Utils includes
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorBgr.h>
|
||||
#include <utils/VideoMode.h>
|
||||
|
||||
///
|
||||
/// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a
|
||||
/// downsized and scaled resolution.
|
||||
///
|
||||
class AmlogicGrabber
|
||||
{
|
||||
public:
|
||||
///
|
||||
/// Construct a AmlogicGrabber that will capture snapshots with specified dimensions.
|
||||
///
|
||||
/// @param[in] width The width of the captured screenshot
|
||||
/// @param[in] height The heigth of the captured screenshot
|
||||
///
|
||||
AmlogicGrabber(const unsigned width, const unsigned height);
|
||||
~AmlogicGrabber();
|
||||
|
||||
///
|
||||
/// Set the video mode (2D/3D)
|
||||
/// @param[in] mode The new video mode
|
||||
///
|
||||
void setVideoMode(const VideoMode videoMode);
|
||||
|
||||
///
|
||||
/// Captures a single snapshot of the display and writes the data to the given image. The
|
||||
/// provided image should have the same dimensions as the configured values (_width and
|
||||
/// _height)
|
||||
///
|
||||
/// @param[out] image The snapped screenshot (should be initialized with correct width and
|
||||
/// height)
|
||||
/// @return Zero on success else negative
|
||||
///
|
||||
int grabFrame(Image<ColorBgr> & image);
|
||||
|
||||
/**
|
||||
* Returns true if video is playing over the amlogic chip
|
||||
* @return True if video is playing else false
|
||||
*/
|
||||
bool isVideoPlaying();
|
||||
private:
|
||||
|
||||
/// With of the captured snapshot [pixels]
|
||||
const unsigned _width;
|
||||
/// Height of the captured snapshot [pixels]
|
||||
const unsigned _height;
|
||||
|
||||
/** The snapshot/capture device of the amlogic video chip */
|
||||
int _amlogicCaptureDev;
|
||||
};
|
@@ -9,7 +9,7 @@
|
||||
|
||||
// Amlogic grabber includes
|
||||
#include <grabber/AmlogicWrapper.h>
|
||||
#include "AmlogicGrabber.h"
|
||||
#include <grabber/AmlogicGrabber.h>
|
||||
|
||||
|
||||
AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
|
||||
@@ -26,6 +26,7 @@ AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeig
|
||||
// Configure the timer to generate events every n milliseconds
|
||||
_timer.setInterval(_updateInterval_ms);
|
||||
_timer.setSingleShot(false);
|
||||
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
|
||||
|
||||
_processor->setSize(grabWidth, grabHeight);
|
||||
|
||||
@@ -55,8 +56,14 @@ void AmlogicWrapper::action()
|
||||
return;
|
||||
}
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
if ( _forward )
|
||||
{
|
||||
Image<ColorRgb> image_rgb;
|
||||
_image.toRgb(image_rgb);
|
||||
emit emitImage(_priority, image_rgb, _timeout_ms);
|
||||
}
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
_hyperion->setColors(_priority, _ledColors, _timeout_ms);
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/amlogic)
|
||||
SET(AmlogicQT_HEADERS ${CURRENT_HEADER_DIR}/AmlogicWrapper.h)
|
||||
|
||||
SET(AmlogicHEADERS
|
||||
${CURRENT_SOURCE_DIR}/AmlogicGrabber.h
|
||||
${CURRENT_HEADER_DIR}/AmlogicGrabber.h
|
||||
)
|
||||
|
||||
SET(AmlogicSOURCES
|
||||
@@ -15,7 +15,11 @@ SET(AmlogicSOURCES
|
||||
${CURRENT_SOURCE_DIR}/AmlogicGrabber.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(AmlogicHEADERS_MOC ${AmlogicQT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(AmlogicHEADERS_MOC ${AmlogicQT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(amlogic-grabber
|
||||
${AmlogicHEADERS}
|
||||
|
@@ -9,28 +9,33 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
|
||||
|
||||
# Group the headers that go through the MOC compiler
|
||||
SET(DispmanxGrabberQT_HEADERS
|
||||
${CURRENT_HEADER_DIR}/DispmanxWrapper.h
|
||||
${CURRENT_HEADER_DIR}/DispmanxWrapper.h
|
||||
)
|
||||
|
||||
SET(DispmanxGrabberHEADERS
|
||||
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.h
|
||||
${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h
|
||||
)
|
||||
|
||||
SET(DispmanxGrabberSOURCES
|
||||
${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp
|
||||
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp
|
||||
${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp
|
||||
${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp
|
||||
)
|
||||
|
||||
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(dispmanx-grabber
|
||||
${DispmanxGrabberHEADERS}
|
||||
${DispmanxGrabberQT_HEADERS}
|
||||
${DispmanxGrabberHEADERS_MOC}
|
||||
${DispmanxGrabberSOURCES}
|
||||
${DispmanxGrabberHEADERS}
|
||||
${DispmanxGrabberQT_HEADERS}
|
||||
${DispmanxGrabberHEADERS_MOC}
|
||||
${DispmanxGrabberSOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(dispmanx-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES}
|
||||
${BCM_LIBRARIES})
|
||||
hyperion
|
||||
${QT_LIBRARIES}
|
||||
${BCM_LIBRARIES}
|
||||
)
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <iostream>
|
||||
|
||||
// Local includes
|
||||
#include "DispmanxFrameGrabber.h"
|
||||
#include "grabber/DispmanxFrameGrabber.h"
|
||||
|
||||
DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) :
|
||||
_vc_display(0),
|
||||
|
@@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// BCM includes
|
||||
#pragma GCC system_header
|
||||
#include <bcm_host.h>
|
||||
|
||||
// STL includes
|
||||
#include <cstdint>
|
||||
|
||||
// Utils includes
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgba.h>
|
||||
#include <utils/VideoMode.h>
|
||||
|
||||
///
|
||||
/// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a
|
||||
/// downsized and scaled resolution.
|
||||
///
|
||||
class DispmanxFrameGrabber
|
||||
{
|
||||
public:
|
||||
///
|
||||
/// Construct a DispmanxFrameGrabber that will capture snapshots with specified dimensions.
|
||||
///
|
||||
/// @param[in] width The width of the captured screenshot
|
||||
/// @param[in] height The heigth of the captured screenshot
|
||||
///
|
||||
DispmanxFrameGrabber(const unsigned width, const unsigned height);
|
||||
~DispmanxFrameGrabber();
|
||||
|
||||
///
|
||||
/// Updates the frame-grab flags as used by the VC library for frame grabbing
|
||||
///
|
||||
/// @param vc_flags The snapshot grabbing mask
|
||||
///
|
||||
void setFlags(const int vc_flags);
|
||||
|
||||
///
|
||||
/// Set the video mode (2D/3D)
|
||||
/// @param[in] mode The new video mode
|
||||
///
|
||||
void setVideoMode(const VideoMode videoMode);
|
||||
|
||||
///
|
||||
/// Captures a single snapshot of the display and writes the data to the given image. The
|
||||
/// provided image should have the same dimensions as the configured values (_width and
|
||||
/// _height)
|
||||
///
|
||||
/// @param[out] image The snapped screenshot (should be initialized with correct width and
|
||||
/// height)
|
||||
///
|
||||
void grabFrame(Image<ColorRgba> & image);
|
||||
|
||||
private:
|
||||
/// Handle to the display that is being captured
|
||||
DISPMANX_DISPLAY_HANDLE_T _vc_display;
|
||||
|
||||
/// Handle to the resource for storing the captured snapshot
|
||||
DISPMANX_RESOURCE_HANDLE_T _vc_resource;
|
||||
|
||||
/// Rectangle of the captured resource that is transfered to user space
|
||||
VC_RECT_T _rectangle;
|
||||
|
||||
/// Flags (transforms) for creating snapshots
|
||||
int _vc_flags;
|
||||
|
||||
/// With of the captured snapshot [pixels]
|
||||
const unsigned _width;
|
||||
/// Height of the captured snapshot [pixels]
|
||||
const unsigned _height;
|
||||
};
|
@@ -9,7 +9,7 @@
|
||||
|
||||
// Dispmanx grabber includes
|
||||
#include <grabber/DispmanxWrapper.h>
|
||||
#include "DispmanxFrameGrabber.h"
|
||||
#include <grabber/DispmanxFrameGrabber.h>
|
||||
|
||||
|
||||
DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) :
|
||||
@@ -28,6 +28,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe
|
||||
_timer.setSingleShot(false);
|
||||
|
||||
_processor->setSize(grabWidth, grabHeight);
|
||||
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
|
||||
|
||||
// Connect the QTimer to this
|
||||
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(action()));
|
||||
@@ -51,10 +52,17 @@ void DispmanxWrapper::action()
|
||||
// Grab frame into the allocated image
|
||||
_frameGrabber->grabFrame(_image);
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
if ( _forward )
|
||||
{
|
||||
Image<ColorRgb> image_rgb;
|
||||
_image.toRgb(image_rgb);
|
||||
emit emitImage(_priority, image_rgb, _timeout_ms);
|
||||
}
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
_hyperion->setColors(_priority, _ledColors, _timeout_ms);
|
||||
}
|
||||
|
||||
void DispmanxWrapper::stop()
|
||||
{
|
||||
// Stop the timer, effectivly stopping the process
|
||||
|
@@ -21,7 +21,11 @@ SET(FramebufferGrabberSOURCES
|
||||
${CURRENT_SOURCE_DIR}/FramebufferFrameGrabber.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(FramebufferGrabberHEADERS_MOC ${FramebufferGrabberQT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(FramebufferGrabberHEADERS_MOC ${FramebufferGrabberQT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(framebuffer-grabber
|
||||
${FramebufferGrabberHEADERS}
|
||||
|
@@ -46,8 +46,9 @@ void FramebufferWrapper::action()
|
||||
// Grab frame into the allocated image
|
||||
_frameGrabber->grabFrame(_image);
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
emit emitImage(_priority, _image, _timeout_ms);
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
_hyperion->setColors(_priority, _ledColors, _timeout_ms);
|
||||
}
|
||||
void FramebufferWrapper::stop()
|
||||
|
@@ -16,7 +16,11 @@ SET(OsxGrabberSOURCES
|
||||
${CURRENT_SOURCE_DIR}/OsxFrameGrabber.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(OsxGrabberHEADERS_MOC ${OsxGrabberQT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(OsxGrabberHEADERS_MOC ${OsxGrabberQT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(osx-grabber
|
||||
${OsxGrabberHEADERS}
|
||||
|
@@ -46,8 +46,9 @@ void OsxWrapper::action()
|
||||
// Grab frame into the allocated image
|
||||
_frameGrabber->grabFrame(_image);
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
emit emitImage(_priority, _image, _timeout_ms);
|
||||
|
||||
_processor->process(_image, _ledColors);
|
||||
_hyperion->setColors(_priority, _ledColors, _timeout_ms);
|
||||
}
|
||||
void OsxWrapper::stop()
|
||||
|
@@ -16,7 +16,11 @@ SET(V4L2_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/V4L2Wrapper.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(V4L2_HEADERS_MOC ${V4L2_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(V4L2_HEADERS_MOC ${V4L2_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(v4l2-grabber
|
||||
${V4L2_HEADERS}
|
||||
@@ -25,6 +29,10 @@ add_library(v4l2-grabber
|
||||
${V4L2_HEADERS_MOC}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(v4l2-grabber Widgets)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(v4l2-grabber
|
||||
hyperion
|
||||
${QT_LIBRARIES}
|
||||
|
@@ -94,6 +94,9 @@ void V4L2Wrapper::newFrame(const Image<ColorRgb> &image)
|
||||
// process the new image
|
||||
_processor->process(image, _ledColors);
|
||||
|
||||
// forward to other hyperions
|
||||
emit emitImage(_priority, image, _timeout_ms);
|
||||
|
||||
// send colors to Hyperion
|
||||
emit emitColors(_priority, _ledColors, _timeout_ms);
|
||||
}
|
||||
|
@@ -22,7 +22,11 @@ SET(X11_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/X11Grabber.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(X11_HEADERS_MOC ${X11_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(X11_HEADERS_MOC ${X11_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(x11-grabber
|
||||
${X11_HEADERS}
|
||||
|
@@ -17,82 +17,116 @@ X11Grabber::X11Grabber(int cropLeft, int cropRight, int cropTop, int cropBottom,
|
||||
_x11Display(nullptr),
|
||||
_screenWidth(0),
|
||||
_screenHeight(0),
|
||||
_croppedWidth(0),
|
||||
_croppedHeight(0),
|
||||
_image(0,0)
|
||||
{
|
||||
_imageResampler.setHorizontalPixelDecimation(horizontalPixelDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(verticalPixelDecimation);
|
||||
_imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XGetImage
|
||||
_imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XShmGetImage
|
||||
}
|
||||
|
||||
X11Grabber::~X11Grabber()
|
||||
{
|
||||
if (_x11Display != nullptr)
|
||||
{
|
||||
freeResources();
|
||||
XCloseDisplay(_x11Display);
|
||||
}
|
||||
}
|
||||
|
||||
int X11Grabber::open()
|
||||
void X11Grabber::freeResources()
|
||||
{
|
||||
const char * display_name = nullptr;
|
||||
_x11Display = XOpenDisplay(display_name);
|
||||
// Cleanup allocated resources of the X11 grab
|
||||
XShmDetach(_x11Display, &_shminfo);
|
||||
XDestroyImage(_xImage);
|
||||
shmdt(_shminfo.shmaddr);
|
||||
shmctl(_shminfo.shmid, IPC_RMID, 0);
|
||||
}
|
||||
|
||||
void X11Grabber::setupResources()
|
||||
{
|
||||
_xImage = XShmCreateImage(_x11Display, _windowAttr.visual,
|
||||
_windowAttr.depth, ZPixmap, NULL, &_shminfo,
|
||||
_croppedWidth, _croppedHeight);
|
||||
|
||||
_shminfo.shmid = shmget(IPC_PRIVATE, _xImage->bytes_per_line * _xImage->height, IPC_CREAT|0777);
|
||||
_shminfo.shmaddr = _xImage->data = (char*)shmat(_shminfo.shmid,0,0);
|
||||
_shminfo.readOnly = False;
|
||||
|
||||
XShmAttach(_x11Display, &_shminfo);
|
||||
}
|
||||
|
||||
bool X11Grabber::Setup()
|
||||
{
|
||||
_x11Display = XOpenDisplay(NULL);
|
||||
if (_x11Display == nullptr)
|
||||
{
|
||||
std::cerr << "Failed to open the default X11-display" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cerr << "Unable to open display";
|
||||
if (getenv("DISPLAY"))
|
||||
std::cerr << " " << std::string(getenv("DISPLAY")) << std::endl;
|
||||
else
|
||||
std::cerr << ". DISPLAY environment variable not set" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
_window = DefaultRootWindow(_x11Display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Image<ColorRgb> & X11Grabber::grab()
|
||||
{
|
||||
if (_x11Display == nullptr)
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
updateScreenDimensions();
|
||||
|
||||
const unsigned croppedWidth = _screenWidth - _cropLeft - _cropRight;
|
||||
const unsigned croppedHeight = _screenHeight - _cropTop - _cropBottom;
|
||||
|
||||
// Capture the current screen
|
||||
XImage * xImage = XGetImage(_x11Display, DefaultRootWindow(_x11Display), _cropLeft, _cropTop, croppedWidth, croppedHeight, AllPlanes, ZPixmap);
|
||||
if (xImage == nullptr)
|
||||
|
||||
XShmGetImage(_x11Display, _window, _xImage, _cropLeft, _cropTop, 0x00FFFFFF);
|
||||
if (_xImage == nullptr)
|
||||
{
|
||||
std::cerr << "Grab failed" << std::endl;
|
||||
return _image;
|
||||
}
|
||||
|
||||
_imageResampler.processImage(reinterpret_cast<const uint8_t *>(xImage->data), xImage->width, xImage->height, xImage->bytes_per_line, PIXELFORMAT_BGR32, _image);
|
||||
|
||||
// Cleanup allocated resources of the X11 grab
|
||||
XDestroyImage(xImage);
|
||||
_imageResampler.processImage(reinterpret_cast<const uint8_t *>(_xImage->data), _xImage->width, _xImage->height, _xImage->bytes_per_line, PIXELFORMAT_BGR32, _image);
|
||||
|
||||
return _image;
|
||||
}
|
||||
|
||||
int X11Grabber::updateScreenDimensions()
|
||||
{
|
||||
XWindowAttributes window_attributes_return;
|
||||
const Status status = XGetWindowAttributes(_x11Display, DefaultRootWindow(_x11Display), &window_attributes_return);
|
||||
const Status status = XGetWindowAttributes(_x11Display, _window, &_windowAttr);
|
||||
if (status == 0)
|
||||
{
|
||||
std::cerr << "Failed to obtain window attributes" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_screenWidth == unsigned(window_attributes_return.width) && _screenHeight == unsigned(window_attributes_return.height))
|
||||
if (_screenWidth == unsigned(_windowAttr.width) && _screenHeight == unsigned(_windowAttr.height))
|
||||
{
|
||||
// No update required
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Update of screen resolution: [" << _screenWidth << "x" << _screenHeight <<"] => ";
|
||||
_screenWidth = window_attributes_return.width;
|
||||
_screenHeight = window_attributes_return.height;
|
||||
|
||||
if (_screenWidth || _screenHeight)
|
||||
freeResources();
|
||||
|
||||
_screenWidth = _windowAttr.width;
|
||||
_screenHeight = _windowAttr.height;
|
||||
|
||||
std::cout << "[" << _screenWidth << "x" << _screenHeight <<"]" << std::endl;
|
||||
|
||||
if (_screenWidth > unsigned(_cropLeft + _cropRight))
|
||||
_croppedWidth = _screenWidth - _cropLeft - _cropRight;
|
||||
else
|
||||
_croppedWidth = _screenWidth;
|
||||
|
||||
if (_screenHeight > unsigned(_cropTop + _cropBottom))
|
||||
_croppedHeight = _screenHeight - _cropTop - _cropBottom;
|
||||
else
|
||||
_croppedHeight = _screenHeight;
|
||||
|
||||
setupResources();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -8,10 +8,11 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold) :
|
||||
//ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector, uint8_t blackborderThreshold) :
|
||||
ImageProcessor::ImageProcessor(const LedString& ledString, const Json::Value & blackborderConfig) :
|
||||
_ledString(ledString),
|
||||
_enableBlackBorderRemoval(enableBlackBorderDetector),
|
||||
_borderProcessor(new BlackBorderProcessor(600, 50, 1, blackborderThreshold)),
|
||||
_enableBlackBorderRemoval(blackborderConfig.get("enable", true).asBool()),
|
||||
_borderProcessor(new BlackBorderProcessor(blackborderConfig) ),
|
||||
_imageToLeds(nullptr)
|
||||
{
|
||||
// empty
|
||||
|
@@ -1,7 +1,3 @@
|
||||
|
||||
// STL includes
|
||||
#include <cmath>
|
||||
|
||||
// Hyperion includes
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
@@ -13,25 +9,13 @@ ImageProcessorFactory& ImageProcessorFactory::getInstance()
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ImageProcessorFactory::init(const LedString& ledString, bool enableBlackBorderDetector, double blackborderThreshold)
|
||||
void ImageProcessorFactory::init(const LedString& ledString, const Json::Value & blackborderConfig)
|
||||
{
|
||||
_ledString = ledString;
|
||||
_enableBlackBorderDetector = enableBlackBorderDetector;
|
||||
|
||||
int threshold = int(std::ceil(blackborderThreshold * 255));
|
||||
if (threshold < 0)
|
||||
threshold = 0;
|
||||
else if (threshold > 255)
|
||||
threshold = 255;
|
||||
_blackborderThreshold = uint8_t(threshold);
|
||||
|
||||
if (_enableBlackBorderDetector)
|
||||
{
|
||||
std::cout << "Black border threshold set to " << blackborderThreshold << " (" << int(_blackborderThreshold) << ")" << std::endl;
|
||||
}
|
||||
_blackborderConfig = blackborderConfig;
|
||||
}
|
||||
|
||||
ImageProcessor* ImageProcessorFactory::newImageProcessor() const
|
||||
{
|
||||
return new ImageProcessor(_ledString, _enableBlackBorderDetector, _blackborderThreshold);
|
||||
return new ImageProcessor(_ledString, _blackborderConfig);
|
||||
}
|
||||
|
51
libsrc/hyperion/MessageForwarder.cpp
Normal file
51
libsrc/hyperion/MessageForwarder.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// STL includes
|
||||
#include <stdexcept>
|
||||
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
|
||||
|
||||
MessageForwarder::MessageForwarder()
|
||||
{
|
||||
}
|
||||
|
||||
MessageForwarder::~MessageForwarder()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MessageForwarder::addJsonSlave(std::string slave)
|
||||
{
|
||||
QStringList parts = QString(slave.c_str()).split(":");
|
||||
if (parts.size() != 2)
|
||||
throw std::runtime_error(QString("Wrong address: unable to parse address (%1)").arg(slave.c_str()).toStdString());
|
||||
|
||||
bool ok;
|
||||
quint16 port = parts[1].toUShort(&ok);
|
||||
if (!ok)
|
||||
throw std::runtime_error(QString("Wrong address: Unable to parse the port number (%1)").arg(parts[1]).toStdString());
|
||||
|
||||
JsonSlaveAddress c;
|
||||
c.addr = QHostAddress(parts[0]);
|
||||
c.port = port;
|
||||
_jsonSlaves << c;
|
||||
}
|
||||
|
||||
void MessageForwarder::addProtoSlave(std::string slave)
|
||||
{
|
||||
_protoSlaves << QString(slave.c_str());
|
||||
}
|
||||
|
||||
QStringList MessageForwarder::getProtoSlaves()
|
||||
{
|
||||
return _protoSlaves;
|
||||
}
|
||||
|
||||
QList<MessageForwarder::JsonSlaveAddress> MessageForwarder::getJsonSlaves()
|
||||
{
|
||||
return _jsonSlaves;
|
||||
}
|
||||
|
||||
bool MessageForwarder::protoForwardingEnabled()
|
||||
{
|
||||
return ! _protoSlaves.empty();
|
||||
}
|
@@ -20,10 +20,13 @@ set(JsonServer_SOURCES
|
||||
set(JsonServer_RESOURCES
|
||||
${CURRENT_SOURCE_DIR}/JsonSchemas.qrc
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_wrap_cpp(JsonServer_HEADERS_MOC ${JsonServer_QT_HEADERS})
|
||||
qt5_add_resources(JsonServer_RESOURCES_RCC ${JsonServer_RESOURCES} OPTIONS "-no-compress")
|
||||
else(ENABLE_QT5)
|
||||
qt4_wrap_cpp(JsonServer_HEADERS_MOC ${JsonServer_QT_HEADERS})
|
||||
|
||||
qt4_add_resources(JsonServer_RESOURCES_RCC ${JsonServer_RESOURCES} OPTIONS "-no-compress")
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(jsonserver
|
||||
${JsonServer_HEADERS}
|
||||
@@ -34,6 +37,10 @@ add_library(jsonserver
|
||||
${JsonServer_RESOURCES_RCC}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(jsonserver Widgets Network)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(jsonserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
|
@@ -16,6 +16,7 @@
|
||||
// hyperion util includes
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
#include <hyperion/ColorTransform.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
|
||||
@@ -250,8 +251,27 @@ void JsonClientConnection::handleMessage(const std::string &messageString)
|
||||
handleNotImplemented();
|
||||
}
|
||||
|
||||
|
||||
void JsonClientConnection::forwardJsonMessage(const Json::Value & message)
|
||||
{
|
||||
QTcpSocket client;
|
||||
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
|
||||
|
||||
for ( int i=0; i<list.size(); i++ )
|
||||
{
|
||||
client.connectToHost(list.at(i).addr, list.at(i).port);
|
||||
if ( client.waitForConnected(500) )
|
||||
{
|
||||
sendMessage(message,&client);
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JsonClientConnection::handleColorCommand(const Json::Value &message)
|
||||
{
|
||||
forwardJsonMessage(message);
|
||||
|
||||
// extract parameters
|
||||
int priority = message["priority"].asInt();
|
||||
int duration = message.get("duration", -1).asInt();
|
||||
@@ -289,6 +309,8 @@ void JsonClientConnection::handleColorCommand(const Json::Value &message)
|
||||
|
||||
void JsonClientConnection::handleImageCommand(const Json::Value &message)
|
||||
{
|
||||
forwardJsonMessage(message);
|
||||
|
||||
// extract parameters
|
||||
int priority = message["priority"].asInt();
|
||||
int duration = message.get("duration", -1).asInt();
|
||||
@@ -320,6 +342,8 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message)
|
||||
|
||||
void JsonClientConnection::handleEffectCommand(const Json::Value &message)
|
||||
{
|
||||
forwardJsonMessage(message);
|
||||
|
||||
// extract parameters
|
||||
int priority = message["priority"].asInt();
|
||||
int duration = message.get("duration", -1).asInt();
|
||||
@@ -418,6 +442,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
|
||||
|
||||
void JsonClientConnection::handleClearCommand(const Json::Value &message)
|
||||
{
|
||||
forwardJsonMessage(message);
|
||||
|
||||
// extract parameters
|
||||
int priority = message["priority"].asInt();
|
||||
|
||||
@@ -428,8 +454,10 @@ void JsonClientConnection::handleClearCommand(const Json::Value &message)
|
||||
sendSuccessReply();
|
||||
}
|
||||
|
||||
void JsonClientConnection::handleClearallCommand(const Json::Value &)
|
||||
void JsonClientConnection::handleClearallCommand(const Json::Value & message)
|
||||
{
|
||||
forwardJsonMessage(message);
|
||||
|
||||
// clear priority
|
||||
_hyperion->clearall();
|
||||
|
||||
@@ -530,10 +558,51 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
|
||||
|
||||
response.append(serializedReply.c_str(), serializedReply.length());
|
||||
|
||||
_socket->write(response.data(), response.length());
|
||||
_socket->write(response.data(), response.length());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JsonClientConnection::sendMessage(const Json::Value & message, QTcpSocket * socket)
|
||||
{
|
||||
// serialize message (FastWriter already appends a newline)
|
||||
std::string serializedMessage = Json::FastWriter().write(message);
|
||||
|
||||
// write message
|
||||
socket->write(serializedMessage.c_str());
|
||||
if (!socket->waitForBytesWritten())
|
||||
{
|
||||
//std::cout << "Error while writing data to host" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// read reply data
|
||||
QByteArray serializedReply;
|
||||
while (!serializedReply.contains('\n'))
|
||||
{
|
||||
// receive reply
|
||||
if (!socket->waitForReadyRead())
|
||||
{
|
||||
//std::cout << "Error while reading data from host" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
serializedReply += socket->readAll();
|
||||
}
|
||||
int bytes = serializedReply.indexOf('\n') + 1; // Find the end of message
|
||||
|
||||
// parse reply data
|
||||
Json::Reader jsonReader;
|
||||
Json::Value reply;
|
||||
if (!jsonReader.parse(serializedReply.constData(), serializedReply.constData() + bytes, reply))
|
||||
{
|
||||
//std::cout << "Error while parsing reply: invalid json" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void JsonClientConnection::sendSuccessReply()
|
||||
{
|
||||
// create reply
|
||||
|
@@ -124,6 +124,7 @@ private:
|
||||
/// @param message The JSON message to send
|
||||
///
|
||||
void sendMessage(const Json::Value & message);
|
||||
void sendMessage(const Json::Value & message, QTcpSocket * socket);
|
||||
|
||||
///
|
||||
/// Send a standard reply indicating success
|
||||
@@ -147,6 +148,11 @@ private:
|
||||
///
|
||||
void handleWebSocketFrame();
|
||||
|
||||
///
|
||||
/// forward json message
|
||||
///
|
||||
void forwardJsonMessage(const Json::Value & message);
|
||||
|
||||
private:
|
||||
///
|
||||
/// Check if a JSON messag is valid according to a given JSON schema
|
||||
|
@@ -16,6 +16,14 @@ JsonServer::JsonServer(Hyperion *hyperion, uint16_t port) :
|
||||
throw std::runtime_error("Json server could not bind to port");
|
||||
}
|
||||
|
||||
QList<MessageForwarder::JsonSlaveAddress> list = _hyperion->getForwarder()->getJsonSlaves();
|
||||
for ( int i=0; i<list.size(); i++ )
|
||||
{
|
||||
if ( list.at(i).addr == QHostAddress::LocalHost && list.at(i).port == port ) {
|
||||
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
|
||||
}
|
||||
}
|
||||
|
||||
// Set trigger for incoming connections
|
||||
connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
|
||||
|
@@ -20,6 +20,8 @@ SET(Leddevice_QT_HEADERS
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h
|
||||
${CURRENT_SOURCE_DIR}/LedHIDDevice.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceRawHID.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h
|
||||
)
|
||||
|
||||
SET(Leddevice_HEADERS
|
||||
@@ -31,7 +33,9 @@ SET(Leddevice_HEADERS
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceUdp.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceAtmo.h
|
||||
@@ -52,7 +56,9 @@ SET(Leddevice_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceUdp.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp
|
||||
@@ -102,8 +108,12 @@ if(ENABLE_TINKERFORGE)
|
||||
)
|
||||
endif(ENABLE_TINKERFORGE)
|
||||
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
|
||||
add_library(leddevice
|
||||
${Leddevice_HEADERS}
|
||||
@@ -112,9 +122,13 @@ add_library(leddevice
|
||||
${Leddevice_SOURCES}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(leddevice Widgets Network)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(leddevice
|
||||
hyperion-utils
|
||||
serialport
|
||||
serialport
|
||||
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${QT_LIBRARIES}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
// Linux includes
|
||||
#include <fcntl.h>
|
||||
@@ -13,30 +12,38 @@
|
||||
#include "LedDeviceAdalightApa102.h"
|
||||
|
||||
LedDeviceAdalightApa102::LedDeviceAdalightApa102(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) :
|
||||
LedDeviceAdalight(outputDevice, baudrate, delayAfterConnect_ms),
|
||||
LedRs232Device(outputDevice, baudrate, delayAfterConnect_ms),
|
||||
_ledBuffer(0),
|
||||
_timer()
|
||||
{
|
||||
// setup the timer
|
||||
_timer.setSingleShot(false);
|
||||
_timer.setInterval(5000);
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(rewriteLeds()));
|
||||
|
||||
// start the timer
|
||||
_timer.start();
|
||||
}
|
||||
//comparing to ws2801 adalight, the following changes were needed:
|
||||
// 1- differnt data frame (4 bytes instead of 3)
|
||||
// 2 - in order to accomodate point 1 above, number of leds sent to adalight is increased by 1/3rd
|
||||
int LedDeviceAdalightApa102::write(const std::vector<ColorRgb> & ledValues)
|
||||
{
|
||||
ledCount = ledValues.size();
|
||||
const unsigned int startFrameSize = 4;
|
||||
const unsigned int endFrameSize = std::max<unsigned int>(((ledValues.size() + 15) / 16), 4);
|
||||
const unsigned int mLedCount = (ledValues.size() * 4) + startFrameSize + endFrameSize;
|
||||
if(_ledBuffer.size() != mLedCount){
|
||||
_ledBuffer.resize(mLedCount, 0xFF);
|
||||
const unsigned int endFrameSize = std::max<unsigned int>(((ledCount + 15) / 16), 4);
|
||||
const unsigned int mLedCount = (ledCount * 4) + startFrameSize + endFrameSize;
|
||||
if(_ledBuffer.size() != mLedCount+6){
|
||||
_ledBuffer.resize(mLedCount+6, 0x00);
|
||||
_ledBuffer[0] = 'A';
|
||||
_ledBuffer[1] = 'd';
|
||||
_ledBuffer[2] = 'a';
|
||||
_ledBuffer[3] = (((unsigned int)(ledValues.size() * 1.33) - 1) >> 8) & 0xFF; // LED count high byte
|
||||
_ledBuffer[4] = ((unsigned int)(ledValues.size() * 1.33) - 1) & 0xFF; // LED count low byte
|
||||
_ledBuffer[3] = (((unsigned int)(ledValues.size())) >> 8) & 0xFF; // LED count high byte
|
||||
_ledBuffer[4] = ((unsigned int)(ledValues.size())) & 0xFF; // LED count low byte
|
||||
_ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55; // Checksum
|
||||
}
|
||||
|
||||
for (unsigned iLed=1; iLed<=ledValues.size(); iLed++) {
|
||||
|
||||
for (unsigned iLed=1; iLed<=ledCount; iLed++) {
|
||||
const ColorRgb& rgb = ledValues[iLed-1];
|
||||
_ledBuffer[iLed*4+6] = 0xFF;
|
||||
_ledBuffer[iLed*4+1+6] = rgb.red;
|
||||
@@ -51,4 +58,25 @@ int LedDeviceAdalightApa102::write(const std::vector<ColorRgb> & ledValues)
|
||||
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
}
|
||||
|
||||
int LedDeviceAdalightApa102::switchOff()
|
||||
{
|
||||
for (unsigned iLed=1; iLed<=ledCount; iLed++) {
|
||||
_ledBuffer[iLed*4+6] = 0xFF;
|
||||
_ledBuffer[iLed*4+1+6] = 0x00;
|
||||
_ledBuffer[iLed*4+2+6] = 0x00;
|
||||
_ledBuffer[iLed*4+3+6] = 0x00;
|
||||
}
|
||||
|
||||
// restart the timer
|
||||
_timer.start();
|
||||
|
||||
// write data
|
||||
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
|
||||
}
|
||||
|
||||
void LedDeviceAdalightApa102::rewriteLeds()
|
||||
{
|
||||
writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
}
|
||||
|
||||
|
@@ -7,12 +7,12 @@
|
||||
#include <QTimer>
|
||||
|
||||
// hyperion incluse
|
||||
#include "LedDeviceAdalight.h"
|
||||
#include "LedRs232Device.h"
|
||||
|
||||
///
|
||||
/// Implementation of the LedDevice interface for writing to an Adalight led device for APA102.
|
||||
///
|
||||
class LedDeviceAdalightApa102 : public LedDeviceAdalight
|
||||
class LedDeviceAdalightApa102 : public LedRs232Device
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -32,13 +32,17 @@ public:
|
||||
/// @return Zero on succes else negative
|
||||
///
|
||||
virtual int write(const std::vector<ColorRgb> & ledValues);
|
||||
virtual int switchOff();
|
||||
|
||||
|
||||
|
||||
private slots:
|
||||
/// Write the last data to the leds again
|
||||
void rewriteLeds();
|
||||
|
||||
private:
|
||||
/// The buffer containing the packed RGB values
|
||||
std::vector<uint8_t> _ledBuffer;
|
||||
|
||||
unsigned int ledCount;
|
||||
/// Timer object which makes sure that led data is written at a minimum rate
|
||||
/// The Adalight device will switch off when it does not receive data at least
|
||||
/// every 15 seconds
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "LedDevicePiBlaster.h"
|
||||
#include "LedDeviceSedu.h"
|
||||
#include "LedDeviceTest.h"
|
||||
#include "LedDeviceFadeCandy.h"
|
||||
#include "LedDeviceUdp.h"
|
||||
#include "LedDeviceHyperionUsbasp.h"
|
||||
#include "LedDevicePhilipsHue.h"
|
||||
#include "LedDeviceTpm2.h"
|
||||
@@ -127,8 +129,9 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||
{
|
||||
const std::string output = deviceConfig["output"].asString();
|
||||
const unsigned rate = deviceConfig["rate"].asInt();
|
||||
const unsigned latchtime = deviceConfig.get("latchtime",500000).asInt();
|
||||
|
||||
LedDeviceWs2801* deviceWs2801 = new LedDeviceWs2801(output, rate);
|
||||
LedDeviceWs2801* deviceWs2801 = new LedDeviceWs2801(output, rate, latchtime);
|
||||
deviceWs2801->open();
|
||||
|
||||
device = deviceWs2801;
|
||||
@@ -243,6 +246,21 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||
const std::string output = deviceConfig["output"].asString();
|
||||
device = new LedDeviceTest(output);
|
||||
}
|
||||
else if (type == "fadecandy")
|
||||
{
|
||||
const std::string host = deviceConfig.get("output", "127.0.0.1").asString();
|
||||
const uint16_t port = deviceConfig.get("port", 7890).asInt();
|
||||
const uint16_t channel = deviceConfig.get("channel", 0).asInt();
|
||||
device = new LedDeviceFadeCandy(host, port, channel);
|
||||
}
|
||||
else if (type == "udp")
|
||||
{
|
||||
const std::string output = deviceConfig["output"].asString();
|
||||
const unsigned rate = deviceConfig["rate"].asInt();
|
||||
const unsigned protocol = deviceConfig["protocol"].asInt();
|
||||
const unsigned maxPacket = deviceConfig["maxpacket"].asInt();
|
||||
device = new LedDeviceUdp(output, rate, protocol, maxPacket);
|
||||
}
|
||||
else if (type == "tpm2")
|
||||
{
|
||||
const std::string output = deviceConfig["output"].asString();
|
||||
|
90
libsrc/leddevice/LedDeviceFadeCandy.cpp
Normal file
90
libsrc/leddevice/LedDeviceFadeCandy.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "LedDeviceFadeCandy.h"
|
||||
|
||||
static const signed MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds
|
||||
static const unsigned OPC_SET_PIXELS = 0; // OPC command codes
|
||||
static const unsigned OPC_HEADER_SIZE = 4; // OPC header size
|
||||
|
||||
|
||||
LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) :
|
||||
_host(host), _port(port), _channel(channel)
|
||||
{
|
||||
_opc_data.resize( OPC_HEADER_SIZE );
|
||||
_opc_data[0] = channel;
|
||||
_opc_data[1] = OPC_SET_PIXELS;
|
||||
_opc_data[2] = 0;
|
||||
_opc_data[3] = 0;
|
||||
}
|
||||
|
||||
|
||||
LedDeviceFadeCandy::~LedDeviceFadeCandy()
|
||||
{
|
||||
_client.close();
|
||||
}
|
||||
|
||||
|
||||
bool LedDeviceFadeCandy::isConnected()
|
||||
{
|
||||
return _client.state() == QAbstractSocket::ConnectedState;
|
||||
}
|
||||
|
||||
|
||||
bool LedDeviceFadeCandy::tryConnect()
|
||||
{
|
||||
if ( _client.state() == QAbstractSocket::UnconnectedState ) {
|
||||
_client.connectToHost( _host.c_str(), _port);
|
||||
if ( _client.waitForConnected(1000) )
|
||||
qDebug("fadecandy/opc: connected to %s:%i on channel %i", _host.c_str(), _port, _channel);
|
||||
}
|
||||
|
||||
return isConnected();
|
||||
}
|
||||
|
||||
|
||||
int LedDeviceFadeCandy::write( const std::vector<ColorRgb> & ledValues )
|
||||
{
|
||||
ssize_t nrLedValues = ledValues.size();
|
||||
ssize_t led_data_size = nrLedValues * 3; // 3 color bytes
|
||||
ssize_t opc_data_size = led_data_size + OPC_HEADER_SIZE;
|
||||
|
||||
if (nrLedValues > MAX_NUM_LEDS)
|
||||
{
|
||||
std::cerr << "fadecandy/opc: Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( opc_data_size != _opc_data.size() )
|
||||
_opc_data.resize( opc_data_size );
|
||||
|
||||
_opc_data[2] = led_data_size >> 8;
|
||||
_opc_data[3] = led_data_size & 0xff;
|
||||
|
||||
uint idx = OPC_HEADER_SIZE;
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
_opc_data[idx ] = unsigned( color.red );
|
||||
_opc_data[idx+1] = unsigned( color.green );
|
||||
_opc_data[idx+2] = unsigned( color.blue );
|
||||
idx += 3;
|
||||
}
|
||||
|
||||
return ( transferData()<0 ? -1 : 0 );
|
||||
}
|
||||
|
||||
|
||||
int LedDeviceFadeCandy::transferData()
|
||||
{
|
||||
if ( isConnected() || tryConnect() )
|
||||
return _client.write( _opc_data, _opc_data.size() );
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
int LedDeviceFadeCandy::switchOff()
|
||||
{
|
||||
for ( int idx=OPC_HEADER_SIZE; idx < _opc_data.size(); idx++ )
|
||||
_opc_data[idx] = 0;
|
||||
|
||||
return ( transferData()<0 ? -1 : 0 );
|
||||
}
|
||||
|
71
libsrc/leddevice/LedDeviceFadeCandy.h
Normal file
71
libsrc/leddevice/LedDeviceFadeCandy.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
// STL/Qt includes
|
||||
#include <fstream>
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
|
||||
// Leddevice includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
|
||||
///
|
||||
/// Implementation of the LedDevice interface for sending to
|
||||
/// fadecandy/opc-server via network by using the 'open pixel control' protocol.
|
||||
///
|
||||
class LedDeviceFadeCandy : public QObject, public LedDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
///
|
||||
/// Constructs the LedDevice for fadecandy/opc server
|
||||
///
|
||||
/// @param host The ip address/host name of fadecandy/opc server
|
||||
/// @param port The port to use (fadecandy default is 7890)
|
||||
///
|
||||
LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel);
|
||||
|
||||
///
|
||||
/// Destructor of the LedDevice; closes the tcp client
|
||||
///
|
||||
virtual ~LedDeviceFadeCandy();
|
||||
|
||||
///
|
||||
/// Writes the led color values to the led-device
|
||||
///
|
||||
/// @param ledValues The color-value per led
|
||||
/// @return Zero on succes else negative
|
||||
///
|
||||
virtual int write(const std::vector<ColorRgb> & ledValues);
|
||||
|
||||
/// Switch the leds off
|
||||
virtual int switchOff();
|
||||
|
||||
|
||||
private:
|
||||
QTcpSocket _client;
|
||||
const std::string _host;
|
||||
const uint16_t _port;
|
||||
const unsigned _channel;
|
||||
QByteArray _opc_data;
|
||||
|
||||
/// try to establish connection to opc server, if not connected yet
|
||||
///
|
||||
/// @return true if connection is established
|
||||
///
|
||||
bool tryConnect();
|
||||
|
||||
/// return the conenction state
|
||||
///
|
||||
/// @return True if connection established
|
||||
///
|
||||
bool isConnected();
|
||||
|
||||
|
||||
/// transfer current opc_data buffer to opc server
|
||||
///
|
||||
/// @return amount of transfered bytes. -1 error while transfering, -2 error while connecting
|
||||
///
|
||||
int transferData();
|
||||
|
||||
};
|
@@ -5,10 +5,9 @@
|
||||
#include <json/json.h>
|
||||
|
||||
// qt includes
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QUrl>
|
||||
#include <QHttpRequestHeader>
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -144,14 +143,14 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, const std::s
|
||||
int transitiontime, std::vector<unsigned int> lightIds) :
|
||||
host(output.c_str()), username(username.c_str()), switchOffOnBlack(switchOffOnBlack), transitiontime(
|
||||
transitiontime), lightIds(lightIds) {
|
||||
http = new QHttp(host);
|
||||
manager = new QNetworkAccessManager();
|
||||
timer.setInterval(3000);
|
||||
timer.setSingleShot(true);
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()));
|
||||
}
|
||||
|
||||
LedDevicePhilipsHue::~LedDevicePhilipsHue() {
|
||||
delete http;
|
||||
delete manager;
|
||||
}
|
||||
|
||||
int LedDevicePhilipsHue::write(const std::vector<ColorRgb> & ledValues) {
|
||||
@@ -215,34 +214,30 @@ int LedDevicePhilipsHue::switchOff() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LedDevicePhilipsHue::put(QString route, QString content) {
|
||||
QString url = QString("/api/%1/%2").arg(username).arg(route);
|
||||
QHttpRequestHeader header("PUT", url);
|
||||
header.setValue("Host", host);
|
||||
header.setValue("Accept-Encoding", "identity");
|
||||
header.setValue("Connection", "keep-alive");
|
||||
header.setValue("Content-Length", QString("%1").arg(content.size()));
|
||||
QEventLoop loop;
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit()));
|
||||
void LedDevicePhilipsHue::put(QString route, QString content) {
|
||||
QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route);
|
||||
// Perfrom request
|
||||
http->request(header, content.toAscii());
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = manager->put(request, content.toLatin1());
|
||||
// Connect finished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
QByteArray LedDevicePhilipsHue::get(QString route) {
|
||||
QString url = QString("/api/%1/%2").arg(username).arg(route);
|
||||
// Event loop to block until request finished.
|
||||
QEventLoop loop;
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit()));
|
||||
QByteArray LedDevicePhilipsHue::get(QString route) {
|
||||
QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route);
|
||||
// Perfrom request
|
||||
http->get(url);
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = manager->get(request);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
// Read all data of the response.
|
||||
return http->readAll();
|
||||
// Read all data of the response.
|
||||
return reply->readAll();
|
||||
}
|
||||
|
||||
QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) {
|
||||
|
@@ -6,9 +6,8 @@
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QHttp>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTimer>
|
||||
|
||||
// Leddevice includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
|
||||
@@ -164,8 +163,8 @@ private:
|
||||
QString host;
|
||||
/// User name for the API ("newdeveloper")
|
||||
QString username;
|
||||
/// Qhttp object for sending requests.
|
||||
QHttp* http;
|
||||
/// QNetworkAccessManager object for sending requests.
|
||||
QNetworkAccessManager* manager;
|
||||
/// Use timer to reset lights when we got into "GRABBINGMODE_OFF".
|
||||
QTimer timer;
|
||||
///
|
||||
|
165
libsrc/leddevice/LedDeviceUdp.cpp
Normal file
165
libsrc/leddevice/LedDeviceUdp.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceUdp.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
//char udpbuffer[1024];
|
||||
int sockfd;
|
||||
int ledprotocol;
|
||||
int leds_per_pkt;
|
||||
int update_number;
|
||||
int fragment_number;
|
||||
|
||||
LedDeviceUdp::LedDeviceUdp(const std::string& output, const unsigned baudrate, const unsigned protocol, const unsigned maxPacket)
|
||||
//LedDeviceUdp::LedDeviceUdp(const std::string& output, const unsigned baudrate) :
|
||||
// _ofs(output.empty()?"/home/pi/LedDevice.out":output.c_str())
|
||||
{
|
||||
|
||||
std::string hostname;
|
||||
std::string port;
|
||||
ledprotocol = protocol;
|
||||
leds_per_pkt = ((maxPacket-4)/3);
|
||||
if (leds_per_pkt <= 0) {
|
||||
leds_per_pkt = 200;
|
||||
}
|
||||
|
||||
//printf ("leds_per_pkt is %d\n", leds_per_pkt);
|
||||
int got_colon=0;
|
||||
for (unsigned int i=0; i<output.length(); i++) {
|
||||
if (output[i] == ':') {
|
||||
got_colon++;
|
||||
} else if (got_colon == 0) {
|
||||
hostname+=output[i];
|
||||
} else {
|
||||
port+=output[i];
|
||||
}
|
||||
}
|
||||
//std::cout << "output " << output << " hostname " << hostname << " port " << port <<std::endl;
|
||||
assert(got_colon==1);
|
||||
|
||||
int rv;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
if ((rv = getaddrinfo(hostname.c_str() , port.c_str(), &hints, &servinfo)) != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||
assert(rv==0);
|
||||
}
|
||||
|
||||
// loop through all the results and make a socket
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1) {
|
||||
perror("talker: socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "talker: failed to create socket\n");
|
||||
assert(p!=NULL);
|
||||
}
|
||||
}
|
||||
|
||||
LedDeviceUdp::~LedDeviceUdp()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
int LedDeviceUdp::write(const std::vector<ColorRgb> & ledValues)
|
||||
{
|
||||
|
||||
char udpbuffer[4096];
|
||||
int udpPtr=0;
|
||||
|
||||
update_number++;
|
||||
update_number &= 0xf;
|
||||
|
||||
if (ledprotocol == 0) {
|
||||
int i=0;
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
if (i<4090) {
|
||||
udpbuffer[i++] = color.red;
|
||||
udpbuffer[i++] = color.green;
|
||||
udpbuffer[i++] = color.blue;
|
||||
}
|
||||
//printf ("c.red %d sz c.red %d\n", color.red, sizeof(color.red));
|
||||
}
|
||||
sendto(sockfd, udpbuffer, i, 0, p->ai_addr, p->ai_addrlen);
|
||||
}
|
||||
if (ledprotocol == 1) {
|
||||
#define MAXLEDperFRAG 450
|
||||
int mLedCount = ledValues.size();
|
||||
|
||||
for (int frag=0; frag<4; frag++) {
|
||||
udpPtr=0;
|
||||
udpbuffer[udpPtr++] = 0;
|
||||
udpbuffer[udpPtr++] = 0;
|
||||
udpbuffer[udpPtr++] = (frag*MAXLEDperFRAG)/256; // high byte
|
||||
udpbuffer[udpPtr++] = (frag*MAXLEDperFRAG)%256; // low byte
|
||||
int ct=0;
|
||||
for (int this_led = frag*300; ((this_led<mLedCount) && (ct++<MAXLEDperFRAG)); this_led++) {
|
||||
const ColorRgb& color = ledValues[this_led];
|
||||
if (udpPtr<4090) {
|
||||
udpbuffer[udpPtr++] = color.red;
|
||||
udpbuffer[udpPtr++] = color.green;
|
||||
udpbuffer[udpPtr++] = color.blue;
|
||||
}
|
||||
}
|
||||
if (udpPtr > 7)
|
||||
sendto(sockfd, udpbuffer, udpPtr, 0, p->ai_addr, p->ai_addrlen);
|
||||
}
|
||||
}
|
||||
if (ledprotocol == 2) {
|
||||
udpPtr = 0;
|
||||
unsigned int ledCtr = 0;
|
||||
fragment_number = 0;
|
||||
udpbuffer[udpPtr++] = update_number & 0xf;
|
||||
udpbuffer[udpPtr++] = fragment_number++;
|
||||
udpbuffer[udpPtr++] = ledCtr/256; // high byte
|
||||
udpbuffer[udpPtr++] = ledCtr%256; // low byte
|
||||
|
||||
for (const ColorRgb& color : ledValues)
|
||||
{
|
||||
if (udpPtr<4090) {
|
||||
udpbuffer[udpPtr++] = color.red;
|
||||
udpbuffer[udpPtr++] = color.green;
|
||||
udpbuffer[udpPtr++] = color.blue;
|
||||
}
|
||||
ledCtr++;
|
||||
if ( (ledCtr % leds_per_pkt == 0) || (ledCtr == ledValues.size()) ) {
|
||||
sendto(sockfd, udpbuffer, udpPtr, 0, p->ai_addr, p->ai_addrlen);
|
||||
memset(udpbuffer, 0, sizeof udpbuffer);
|
||||
udpPtr = 0;
|
||||
udpbuffer[udpPtr++] = update_number & 0xf;
|
||||
udpbuffer[udpPtr++] = fragment_number++;
|
||||
udpbuffer[udpPtr++] = ledCtr/256; // high byte
|
||||
udpbuffer[udpPtr++] = ledCtr%256; // low byte
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LedDeviceUdp::switchOff()
|
||||
{
|
||||
// return write(std::vector<ColorRgb>(mLedCount, ColorRgb{0,0,0}));
|
||||
return 0;
|
||||
}
|
44
libsrc/leddevice/LedDeviceUdp.h
Normal file
44
libsrc/leddevice/LedDeviceUdp.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
// STL includes0
|
||||
#include <fstream>
|
||||
|
||||
// Leddevice includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
|
||||
///
|
||||
/// Implementation of the LedDevice that write the led-colors to an
|
||||
/// ASCII-textfile('/home/pi/LedDevice.out')
|
||||
///
|
||||
class LedDeviceUdp : public LedDevice
|
||||
{
|
||||
public:
|
||||
///
|
||||
/// Constructs the test-device, which opens an output stream to the file
|
||||
///
|
||||
LedDeviceUdp(const std::string& output, const unsigned baudrate, const unsigned protocol, const unsigned maxPacket);
|
||||
|
||||
///
|
||||
/// Destructor of this test-device
|
||||
///
|
||||
virtual ~LedDeviceUdp();
|
||||
|
||||
///
|
||||
/// Writes the given led-color values to the output stream
|
||||
///
|
||||
/// @param ledValues The color-value per led
|
||||
///
|
||||
/// @return Zero on success else negative
|
||||
///
|
||||
virtual int write(const std::vector<ColorRgb> & ledValues);
|
||||
|
||||
/// Switch the leds off
|
||||
virtual int switchOff();
|
||||
|
||||
private:
|
||||
/// The outputstream
|
||||
// std::ofstream _ofs;
|
||||
|
||||
/// the number of leds (needed when switching off)
|
||||
size_t mLedCount;
|
||||
};
|
@@ -18,6 +18,13 @@ LedDeviceWs2801::LedDeviceWs2801(const std::string& outputDevice, const unsigned
|
||||
// empty
|
||||
}
|
||||
|
||||
LedDeviceWs2801::LedDeviceWs2801(const std::string& outputDevice, const unsigned baudrate, const unsigned latchTime) :
|
||||
LedSpiDevice(outputDevice, baudrate, latchTime),
|
||||
mLedCount(0)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
int LedDeviceWs2801::write(const std::vector<ColorRgb> &ledValues)
|
||||
{
|
||||
mLedCount = ledValues.size();
|
||||
|
@@ -21,6 +21,10 @@ public:
|
||||
LedDeviceWs2801(const std::string& outputDevice,
|
||||
const unsigned baudrate);
|
||||
|
||||
LedDeviceWs2801(const std::string& outputDevice,
|
||||
const unsigned baudrate,
|
||||
const unsigned latchTime);
|
||||
|
||||
///
|
||||
/// Writes the led color values to the led-device
|
||||
///
|
||||
|
@@ -34,7 +34,11 @@ protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS
|
||||
${ProtoServer_PROTOS}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_wrap_cpp(ProtoServer_HEADERS_MOC ${ProtoServer_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
qt4_wrap_cpp(ProtoServer_HEADERS_MOC ${ProtoServer_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(protoserver
|
||||
${ProtoServer_HEADERS}
|
||||
@@ -45,6 +49,9 @@ add_library(protoserver
|
||||
${ProtoServer_PROTO_SRCS}
|
||||
${ProtoServer_PROTO_HDRS}
|
||||
)
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(protoserver Widgets)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(protoserver
|
||||
hyperion
|
||||
|
@@ -81,6 +81,9 @@ void ProtoClientConnection::socketClosed()
|
||||
|
||||
void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message)
|
||||
{
|
||||
// forward messages
|
||||
emit newMessage(&message);
|
||||
|
||||
switch (message.command())
|
||||
{
|
||||
case proto::HyperionRequest::COLOR:
|
||||
|
@@ -6,12 +6,14 @@
|
||||
// Qt includes
|
||||
#include <QByteArray>
|
||||
#include <QTcpSocket>
|
||||
#include <QStringList>
|
||||
|
||||
// Hyperion includes
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
// proto includes
|
||||
#include "message.pb.h"
|
||||
#include "protoserver/ProtoConnection.h"
|
||||
|
||||
class ImageProcessor;
|
||||
|
||||
@@ -41,6 +43,7 @@ signals:
|
||||
/// @param connection This connection object
|
||||
///
|
||||
void connectionClosed(ProtoClientConnection * connection);
|
||||
void newMessage(const proto::HyperionRequest * message);
|
||||
|
||||
private slots:
|
||||
///
|
||||
|
@@ -9,7 +9,8 @@
|
||||
|
||||
ProtoConnection::ProtoConnection(const std::string & a) :
|
||||
_socket(),
|
||||
_skipReply(false)
|
||||
_skipReply(false),
|
||||
_prevSocketState(QAbstractSocket::UnconnectedState)
|
||||
{
|
||||
QString address(a.c_str());
|
||||
QStringList parts = address.split(":");
|
||||
@@ -29,10 +30,18 @@ ProtoConnection::ProtoConnection(const std::string & a) :
|
||||
// try to connect to host
|
||||
std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
|
||||
connectToHost();
|
||||
|
||||
// start the connection timer
|
||||
_timer.setInterval(5000);
|
||||
_timer.setSingleShot(false);
|
||||
|
||||
connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost()) );
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
ProtoConnection::~ProtoConnection()
|
||||
{
|
||||
_timer.stop();
|
||||
_socket.close();
|
||||
}
|
||||
|
||||
@@ -91,20 +100,37 @@ void ProtoConnection::clearAll()
|
||||
|
||||
void ProtoConnection::connectToHost()
|
||||
{
|
||||
_socket.connectToHost(_host, _port);
|
||||
if (_socket.waitForConnected()) {
|
||||
std::cout << "Connected to Hyperion host" << std::endl;
|
||||
// try connection only when
|
||||
if (_socket.state() == QAbstractSocket::UnconnectedState)
|
||||
{
|
||||
_socket.connectToHost(_host, _port);
|
||||
//_socket.waitForConnected(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConnection::sendMessage(const proto::HyperionRequest &message)
|
||||
{
|
||||
if (_socket.state() == QAbstractSocket::UnconnectedState)
|
||||
// print out connection message only when state is changed
|
||||
if (_socket.state() != _prevSocketState )
|
||||
{
|
||||
std::cout << "Currently disconnected: trying to connect to host" << std::endl;
|
||||
connectToHost();
|
||||
switch (_socket.state() )
|
||||
{
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
std::cout << "No connection to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
|
||||
break;
|
||||
|
||||
case QAbstractSocket::ConnectedState:
|
||||
std::cout << "Connected to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
//std::cout << "Connecting to Hyperion: " << _host.toStdString() << ":" << _port << std::endl;
|
||||
break;
|
||||
}
|
||||
_prevSocketState = _socket.state();
|
||||
}
|
||||
|
||||
|
||||
if (_socket.state() != QAbstractSocket::ConnectedState)
|
||||
{
|
||||
return;
|
||||
|
@@ -2,7 +2,9 @@
|
||||
#include <stdexcept>
|
||||
|
||||
// project includes
|
||||
#include <hyperion/MessageForwarder.h>
|
||||
#include <protoserver/ProtoServer.h>
|
||||
#include "protoserver/ProtoConnection.h"
|
||||
#include "ProtoClientConnection.h"
|
||||
|
||||
ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
|
||||
@@ -11,6 +13,20 @@ ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) :
|
||||
_server(),
|
||||
_openConnections()
|
||||
{
|
||||
|
||||
MessageForwarder * forwarder = hyperion->getForwarder();
|
||||
QStringList slaves = forwarder->getProtoSlaves();
|
||||
|
||||
for (int i = 0; i < slaves.size(); ++i) {
|
||||
if ( QString("127.0.0.1:%1").arg(port) == slaves.at(i) ) {
|
||||
throw std::runtime_error("Loop between proto server and forwarder detected. Fix your config!");
|
||||
}
|
||||
|
||||
ProtoConnection* p = new ProtoConnection(slaves.at(i).toLocal8Bit().constData());
|
||||
p->setSkipReply(true);
|
||||
_proxy_connections << p;
|
||||
}
|
||||
|
||||
if (!_server.listen(QHostAddress::Any, port))
|
||||
{
|
||||
throw std::runtime_error("Proto server could not bind to port");
|
||||
@@ -25,6 +41,9 @@ ProtoServer::~ProtoServer()
|
||||
foreach (ProtoClientConnection * connection, _openConnections) {
|
||||
delete connection;
|
||||
}
|
||||
|
||||
while (!_proxy_connections.isEmpty())
|
||||
delete _proxy_connections.takeFirst();
|
||||
}
|
||||
|
||||
uint16_t ProtoServer::getPort() const
|
||||
@@ -44,9 +63,23 @@ void ProtoServer::newConnection()
|
||||
|
||||
// register slot for cleaning up after the connection closed
|
||||
connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*)));
|
||||
connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoServer::newMessage(const proto::HyperionRequest * message)
|
||||
{
|
||||
for (int i = 0; i < _proxy_connections.size(); ++i)
|
||||
_proxy_connections.at(i)->sendMessage(*message);
|
||||
}
|
||||
|
||||
void ProtoServer::sendImageToProtoSlaves(int priority, const Image<ColorRgb> & image, int duration_ms)
|
||||
{
|
||||
for (int i = 0; i < _proxy_connections.size(); ++i)
|
||||
_proxy_connections.at(i)->setImage(image, priority, duration_ms);
|
||||
}
|
||||
|
||||
void ProtoServer::closedConnection(ProtoClientConnection *connection)
|
||||
{
|
||||
std::cout << "Proto connection closed" << std::endl;
|
||||
|
@@ -15,7 +15,11 @@ SET(XBMCVideoChecker_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/XBMCVideoChecker.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
QT5_WRAP_CPP(XBMCVideoChecker_HEADERS_MOC ${XBMCVideoChecker_QT_HEADERS})
|
||||
else(ENABLE_QT5)
|
||||
QT4_WRAP_CPP(XBMCVideoChecker_HEADERS_MOC ${XBMCVideoChecker_QT_HEADERS})
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
add_library(xbmcvideochecker
|
||||
${XBMCVideoChecker_HEADERS}
|
||||
@@ -24,6 +28,10 @@ add_library(xbmcvideochecker
|
||||
${XBMCVideoChecker_SOURCES}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(xbmcvideochecker Widgets)
|
||||
endif(ENABLE_QT5)
|
||||
|
||||
target_link_libraries(xbmcvideochecker
|
||||
hyperion
|
||||
${QT_LIBRARIES})
|
||||
|
Reference in New Issue
Block a user