From 24495bbc65bfbe72179a3e641437884570f98e7b Mon Sep 17 00:00:00 2001 From: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com> Date: Sat, 24 Aug 2019 22:53:30 +0200 Subject: [PATCH] Fix #604, #605 ... (#607) * Fix #604 and #605 Signed-off-by: Paulchen-Panther * clear current prio on color command Signed-off-by: Paulchen-Panther * Fix QTimer threading issues * Call QTimer start() stop() from QEvent Signed-off-by: Paulchen-Panther * send initial color/image to WebUI hide error message when opening webbrowser Signed-off-by: Paulchen-Panther * added streaming timer to update WebUI Preview Signed-off-by: Paulchen-Panther * remove QMetaObject::invokeMethod() Signed-off-by: Paulchen-Panther * added parent to streaming timers Signed-off-by: Paulchen-Panther * header cleanup --- include/api/JsonAPI.h | 43 +++--- include/utils/Image.h | 11 ++ .../api/JSONRPC_schema/schema-ledcolors.json | 4 +- libsrc/api/JsonAPI.cpp | 127 +++++++++++------- libsrc/hyperion/Hyperion.cpp | 3 + libsrc/hyperion/LinearColorSmoothing.cpp | 9 +- libsrc/hyperion/PriorityMuxer.cpp | 2 + src/hyperiond/systray.cpp | 25 ++++ 8 files changed, 150 insertions(+), 74 deletions(-) diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 6b869966..08a6f181 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -2,21 +2,17 @@ // hyperion includes #include -#include #include #include +#include // qt includes #include -#include #include -// HyperionInstanceManager -#include - +class QTimer; class JsonCB; class AuthManager; -class HyperionIManager; class JsonAPI : public QObject { @@ -43,15 +39,20 @@ public: public slots: /// - /// @brief is called whenever the current Hyperion instance pushes new led raw values (if enabled) - /// @param ledColors The current ledColors + /// @brief Is called whenever the current Hyperion instance pushes new led raw values (if enabled) + /// @param ledColors The current led colors /// void streamLedcolorsUpdate(const std::vector& ledColors); - /// push images whenever hyperion emits (if enabled) + /// + /// @brief Push images whenever hyperion emits (if enabled) + /// @param image The current image + /// void setImage(const Image & image); - /// process and push new log messages from logger (if enabled) + /// + /// @brief Process and push new log messages from logger (if enabled) + /// void incommingLogMessage(const Logger::T_LOG_MESSAGE&); private slots: @@ -128,17 +129,23 @@ private: /// flag to determine state of log streaming bool _streaming_logging_activated; - /// mutex to determine state of image streaming - QMutex _image_stream_mutex; + /// timer for live video refresh + QTimer* _imageStreamTimer; - /// mutex to determine state of led streaming - QMutex _led_stream_mutex; + /// image stream connection handle + QMetaObject::Connection _imageStreamConnection; - /// timeout for live video refresh - volatile qint64 _image_stream_timeout; + /// the current streaming image + Image _currentImage; - /// timeout for led color refresh - volatile qint64 _led_stream_timeout; + /// timer for led color refresh + QTimer* _ledStreamTimer; + + /// led stream connection handle + QMetaObject::Connection _ledStreamConnection; + + /// the current streaming led values + std::vector _currentLedValues; /// /// @brief Handle the switches of Hyperion instances diff --git a/include/utils/Image.h b/include/utils/Image.h index a883cc2f..0c5c8456 100644 --- a/include/utils/Image.h +++ b/include/utils/Image.h @@ -245,6 +245,17 @@ public: return (ssize_t) _width * _height * sizeof(Pixel_T); } + /// Clear the image + // + void clear() + { + _width = 1; + _height = 1; + _pixels = new Pixel_T[2]; + _endOfPixels = _pixels + 1; + memset(_pixels, 0, _width * _height * sizeof(Pixel_T)); + } + private: /// diff --git a/libsrc/api/JSONRPC_schema/schema-ledcolors.json b/libsrc/api/JSONRPC_schema/schema-ledcolors.json index 93d5e95a..b1bda0a3 100644 --- a/libsrc/api/JSONRPC_schema/schema-ledcolors.json +++ b/libsrc/api/JSONRPC_schema/schema-ledcolors.json @@ -19,7 +19,9 @@ "type" : "bool" }, "interval": { - "type" : "integer" + "type" : "integer", + "required" : false, + "minimum": 50 } }, diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 17e43859..83127145 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -12,17 +12,16 @@ #include #include #include -#include -#include -#include +#include // hyperion includes -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #include #include @@ -53,8 +52,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger* log, const bool& localConnection, , _hyperion(nullptr) , _jsonCB(nullptr) , _streaming_logging_activated(false) - , _image_stream_timeout(0) - , _led_stream_timeout(0) + , _imageStreamTimer(new QTimer(this)) + , _ledStreamTimer(new QTimer(this)) { Q_INIT_RESOURCE(JSONRPC_schemas); @@ -1018,28 +1017,68 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString & // create result QString subcommand = message["subcommand"].toString(""); + // max 20 Hz (50ms) interval for streaming (default: 10 Hz (100ms)) + qint64 streaming_interval = qMax(message["interval"].toInt(100), 50); + if (subcommand == "ledstream-start") { _streaming_leds_reply["success"] = true; _streaming_leds_reply["command"] = command+"-ledstream-update"; _streaming_leds_reply["tan"] = tan; - connect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate, Qt::UniqueConnection); + + connect(_hyperion, &Hyperion::rawLedColors, this, [=](const std::vector& ledValues) + { + _currentLedValues = ledValues; + + // necessary because Qt::UniqueConnection for lambdas does not work until 5.9 + // see: https://bugreports.qt.io/browse/QTBUG-52438 + if (!_ledStreamConnection) + _ledStreamConnection = connect(_ledStreamTimer, &QTimer::timeout, this, [=]() + { + emit streamLedcolorsUpdate(_currentLedValues); + }, Qt::UniqueConnection); + + // start the timer + if (!_ledStreamTimer->isActive() || _ledStreamTimer->interval() != streaming_interval) + _ledStreamTimer->start(streaming_interval); + }, Qt::UniqueConnection); } else if (subcommand == "ledstream-stop") { - disconnect(_hyperion, &Hyperion::rawLedColors, this, &JsonAPI::streamLedcolorsUpdate); + disconnect(_hyperion, &Hyperion::rawLedColors, this, 0); + _ledStreamTimer->stop(); + disconnect(_ledStreamConnection); } else if (subcommand == "imagestream-start") { _streaming_image_reply["success"] = true; _streaming_image_reply["command"] = command+"-imagestream-update"; _streaming_image_reply["tan"] = tan; - connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection); + + connect(_hyperion, &Hyperion::currentImage, this, [=](const Image& image) + { + _currentImage = image; + + // necessary because Qt::UniqueConnection for lambdas does not work until 5.9 + // see: https://bugreports.qt.io/browse/QTBUG-52438 + if (!_imageStreamConnection) + _imageStreamConnection = connect(_imageStreamTimer, &QTimer::timeout, this, [=]() + { + emit setImage(_currentImage); + }, Qt::UniqueConnection); + + // start timer + if (!_imageStreamTimer->isActive() || _imageStreamTimer->interval() != streaming_interval) + _imageStreamTimer->start(streaming_interval); + }, Qt::UniqueConnection); + _hyperion->update(); } else if (subcommand == "imagestream-stop") { - disconnect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage); + disconnect(_hyperion, &Hyperion::currentImage, this, 0); + _imageStreamTimer->stop(); + disconnect(_imageStreamConnection); } else { @@ -1420,52 +1459,40 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, const emit callbackMessage(reply); } - void JsonAPI::streamLedcolorsUpdate(const std::vector& ledColors) { - QMutexLocker lock(&_led_stream_mutex); - if ( (_led_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() ) + QJsonObject result; + QJsonArray leds; + + for(auto color = ledColors.begin(); color != ledColors.end(); ++color) { - _led_stream_timeout = QDateTime::currentMSecsSinceEpoch(); - QJsonObject result; - QJsonArray leds; - - for(auto color = ledColors.begin(); color != ledColors.end(); ++color) - { - QJsonObject item; - item["index"] = int(color - ledColors.begin()); - item["red"] = color->red; - item["green"] = color->green; - item["blue"] = color->blue; - leds.append(item); - } - - result["leds"] = leds; - _streaming_leds_reply["result"] = result; - - // send the result - emit callbackMessage(_streaming_leds_reply); + QJsonObject item; + item["index"] = int(color - ledColors.begin()); + item["red"] = color->red; + item["green"] = color->green; + item["blue"] = color->blue; + leds.append(item); } + + result["leds"] = leds; + _streaming_leds_reply["result"] = result; + + // send the result + emit callbackMessage(_streaming_leds_reply); } void JsonAPI::setImage(const Image & image) { - QMutexLocker lock(&_image_stream_mutex); - if ( (_image_stream_timeout+100) < QDateTime::currentMSecsSinceEpoch() ) - { - _image_stream_timeout = QDateTime::currentMSecsSinceEpoch(); + QImage jpgImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + jpgImage.save(&buffer, "jpg"); - QImage jpgImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - jpgImage.save(&buffer, "jpg"); - - QJsonObject result; - result["image"] = "data:image/jpg;base64,"+QString(ba.toBase64()); - _streaming_image_reply["result"] = result; - emit callbackMessage(_streaming_image_reply); - } + QJsonObject result; + result["image"] = "data:image/jpg;base64,"+QString(ba.toBase64()); + _streaming_image_reply["result"] = result; + emit callbackMessage(_streaming_image_reply); } void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index fb735143..3995cb2f 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -368,6 +368,9 @@ void Hyperion::setColor(const int priority, const ColorRgb &color, const int tim // create led vector from single color std::vector ledColors(_ledString.leds().size(), color); + if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR) + clear(priority); + // register color registerInput(priority, hyperion::COMP_COLOR, origin); diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index d49e0c4b..0acaf127 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -72,7 +72,7 @@ int LinearColorSmoothing::write(const std::vector &ledValues) _previousTime = QDateTime::currentMSecsSinceEpoch(); _previousValues = ledValues; - _timer->start(); + QMetaObject::invokeMethod(_timer, "start", Qt::QueuedConnection, Q_ARG(int, _updateInterval)); } else { @@ -182,7 +182,7 @@ void LinearColorSmoothing::setEnable(bool enable) { if (!enable) { - _timer->stop(); + QMetaObject::invokeMethod(_timer, "stop", Qt::QueuedConnection); _previousValues.clear(); } // update comp register @@ -218,10 +218,9 @@ bool LinearColorSmoothing::selectConfig(unsigned cfg, const bool& force) if (_cfgList[cfg].updateInterval != _updateInterval) { - _timer->stop(); + QMetaObject::invokeMethod(_timer, "stop", Qt::QueuedConnection); _updateInterval = _cfgList[cfg].updateInterval; - _timer->setInterval(_updateInterval); - _timer->start(); + QMetaObject::invokeMethod(_timer, "start", Qt::QueuedConnection, Q_ARG(int, _updateInterval)); } _currentConfigId = cfg; //DebugIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay ); diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index b833e59d..d9a112b4 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -185,6 +185,7 @@ bool PriorityMuxer::setInput(const int priority, const std::vector& le // update input input.timeoutTime_ms = timeout_ms; input.ledColors = ledColors; + input.image.clear(); // emit active change if(activeChange) @@ -224,6 +225,7 @@ bool PriorityMuxer::setInputImage(const int priority, const Image& ima // update input input.timeoutTime_ms = timeout_ms; input.image = image; + input.ledColors.clear(); // emit active change if(activeChange) diff --git a/src/hyperiond/systray.cpp b/src/hyperiond/systray.cpp index 066527f2..994966b2 100644 --- a/src/hyperiond/systray.cpp +++ b/src/hyperiond/systray.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -128,7 +129,31 @@ void SysTray::closeEvent(QCloseEvent *event) void SysTray::settings() { + // Hide error messages when opening webbrowser + + int out_pipe[2]; + int saved_stdout; + int saved_stderr; + + // saving stdout and stderr file descriptor + saved_stdout = ::dup( STDOUT_FILENO ); + saved_stderr = ::dup( STDERR_FILENO ); + + if(::pipe(out_pipe) == 0) + { + // redirecting stdout to pipe + ::dup2(out_pipe[1], STDOUT_FILENO); + ::close(out_pipe[1]); + // redirecting stderr to stdout + ::dup2(STDOUT_FILENO, STDERR_FILENO); + } + QDesktopServices::openUrl(QUrl("http://localhost:"+QString::number(_webPort)+"/", QUrl::TolerantMode)); + + // restoring stdout + ::dup2(saved_stdout, STDOUT_FILENO); + // restoring stderr + ::dup2(saved_stderr, STDERR_FILENO); } void SysTray::setEffect()