194 lines
6.3 KiB
C++
194 lines
6.3 KiB
C++
#include <QCommandLineParser>
|
|
#include <signal.h>
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QDateTime>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QCoreApplication>
|
|
#include <QTimer>
|
|
#include <QApplication> // Added for QApplication
|
|
|
|
#include "hyperiongrabber.h"
|
|
#include "HyperionProcessor.h"
|
|
#include "wledclient.h"
|
|
#include "LedColorMapping.h"
|
|
#include "LinearColorSmoothing.h"
|
|
|
|
// Global pointers for cleanup
|
|
static HyperionGrabber *grabber = nullptr;
|
|
static QApplication *qapp = nullptr;
|
|
static HyperionProcessor *processor = nullptr;
|
|
static WledClient *wledClient = nullptr;
|
|
static QTimer *processTimer = nullptr;
|
|
static QImage currentImage; // Store the latest captured image
|
|
|
|
// Custom message handler for logging to file
|
|
static QFile *logFile = nullptr;
|
|
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
|
{
|
|
QByteArray localMsg = msg.toLocal8Bit();
|
|
QString logMessage = QString("%1 %2 %3 %4 %5")
|
|
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
|
|
.arg(context.category)
|
|
.arg(context.function)
|
|
.arg(context.line)
|
|
.arg(localMsg.constData());
|
|
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
logMessage = "Debug: " + logMessage;
|
|
break;
|
|
case QtInfoMsg:
|
|
logMessage = "Info: " + logMessage;
|
|
break;
|
|
case QtWarningMsg:
|
|
logMessage = "Warning: " + logMessage;
|
|
break;
|
|
case QtCriticalMsg:
|
|
logMessage = "Critical: " + logMessage;
|
|
break;
|
|
case QtFatalMsg:
|
|
logMessage = "Fatal: " + logMessage;
|
|
break;
|
|
}
|
|
|
|
if (logFile && logFile->isOpen()) {
|
|
QTextStream stream(logFile);
|
|
stream << logMessage << Qt::endl;
|
|
stream.flush();
|
|
}
|
|
|
|
if (type == QtCriticalMsg || type == QtFatalMsg || !logFile || !logFile->isOpen()) {
|
|
fprintf(stderr, "%s\n", logMessage.toLocal8Bit().constData());
|
|
}
|
|
}
|
|
|
|
// Signal handler for graceful shutdown
|
|
static void quit(int)
|
|
{
|
|
if (processTimer) {
|
|
processTimer->stop();
|
|
delete processTimer;
|
|
processTimer = nullptr;
|
|
}
|
|
if (grabber != nullptr) {
|
|
delete grabber;
|
|
grabber = nullptr;
|
|
}
|
|
if (processor != nullptr) {
|
|
delete processor;
|
|
processor = nullptr;
|
|
}
|
|
if (wledClient != nullptr) {
|
|
delete wledClient;
|
|
wledClient = nullptr;
|
|
}
|
|
if (logFile) {
|
|
logFile->close();
|
|
delete logFile;
|
|
logFile = nullptr;
|
|
}
|
|
if (qapp != nullptr) {
|
|
qapp->exit();
|
|
}
|
|
}
|
|
|
|
// Slot to receive and store the latest image from HyperionGrabber
|
|
void onImageReady(const QImage &image)
|
|
{
|
|
currentImage = image;
|
|
}
|
|
|
|
// Slot to process the current image and send to WLED (triggered by timer)
|
|
void processCurrentFrame()
|
|
{
|
|
if (currentImage.isNull()) {
|
|
qDebug() << "No image available yet for processing.";
|
|
return;
|
|
}
|
|
|
|
// Process the image with HyperionProcessor
|
|
QVector<QColor> ledColors = processor->process(currentImage);
|
|
|
|
// Send colors to WLED
|
|
wledClient->setLedsColor(ledColors);
|
|
|
|
qDebug() << "Processed frame and sent to WLED.";
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// Initialize logging to file
|
|
logFile = new QFile("output.log");
|
|
if (!logFile->open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
|
|
fprintf(stderr, "Warning: Could not open log file output.log for writing.\n");
|
|
delete logFile;
|
|
logFile = nullptr;
|
|
}
|
|
qInstallMessageHandler(customMessageOutput);
|
|
|
|
qapp = new QApplication(argc, argv);
|
|
signal(SIGINT, quit);
|
|
signal(SIGTERM, quit);
|
|
|
|
QApplication::setApplicationName("HyperionGrabber");
|
|
QApplication::setApplicationVersion("0.2");
|
|
|
|
// Read configuration from environment variables
|
|
QString wledAddress = qgetenv("HYPERION_GRABBER_WLED_ADDRESS");
|
|
if (wledAddress.isEmpty()) {
|
|
qCritical() << "Error: HYPERION_GRABBER_WLED_ADDRESS environment variable not set.";
|
|
return 1;
|
|
}
|
|
|
|
quint16 wledPort = qgetenv("HYPERION_GRABBER_WLED_PORT").toUShort();
|
|
if (wledPort == 0) wledPort = 21324; // Default WLED UDP Realtime port
|
|
|
|
QHash<QString, QString> grabberOpts;
|
|
grabberOpts.insert("scale", QString::number(qgetenv("HYPERION_GRABBER_SCALE").toInt()));
|
|
grabberOpts.insert("frameskip", QString::number(qgetenv("HYPERION_GRABBER_FRAMESKIP").toInt()));
|
|
grabberOpts.insert("changeThreshold", QString::number(qgetenv("HYPERION_GRABBER_CHANGE_THRESHOLD").toInt()));
|
|
|
|
LedLayout layout;
|
|
layout.bottom = qgetenv("HYPERION_GRABBER_LEDS_BOTTOM").toInt();
|
|
layout.right = qgetenv("HYPERION_GRABBER_LEDS_RIGHT").toInt();
|
|
layout.top = qgetenv("HYPERION_GRABBER_LEDS_TOP").toInt();
|
|
layout.left = qgetenv("HYPERION_GRABBER_LEDS_LEFT").toInt();
|
|
|
|
QJsonObject processorConfig;
|
|
processorConfig["blackBorderThreshold"] = qgetenv("HYPERION_GRABBER_BLACK_BORDER_THRESHOLD").toDouble();
|
|
processorConfig["smoothingFactor"] = qgetenv("HYPERION_GRABBER_SMOOTHING_FACTOR").toDouble();
|
|
|
|
// Apply default values if environment variables are not set or invalid
|
|
if (grabberOpts["scale"].isEmpty()) grabberOpts["scale"] = "8";
|
|
if (grabberOpts["frameskip"].isEmpty()) grabberOpts["frameskip"] = "0";
|
|
if (grabberOpts["changeThreshold"].isEmpty()) grabberOpts["changeThreshold"] = "100";
|
|
|
|
if (layout.bottom == 0) layout.bottom = 70;
|
|
if (layout.right == 0) layout.right = 20;
|
|
if (layout.top == 0) layout.top = 70;
|
|
if (layout.left == 0) layout.left = 20;
|
|
|
|
if (!processorConfig.contains("blackBorderThreshold")) processorConfig["blackBorderThreshold"] = 0.2;
|
|
if (!processorConfig.contains("smoothingFactor")) processorConfig["smoothingFactor"] = 0.1;
|
|
|
|
// Instantiate components
|
|
grabber = new HyperionGrabber(grabberOpts);
|
|
processor = new HyperionProcessor(layout, processorConfig);
|
|
wledClient = new WledClient(wledAddress, wledPort);
|
|
|
|
// Connect grabber to image receiver slot
|
|
QObject::connect(grabber, &HyperionGrabber::imageReady, &onImageReady);
|
|
|
|
// Setup timer for processing frames
|
|
processTimer = new QTimer();
|
|
processTimer->setInterval(1000); // Process every 1 second
|
|
QObject::connect(processTimer, &QTimer::timeout, &processCurrentFrame);
|
|
processTimer->start();
|
|
|
|
qInfo() << "HyperionGrabber started. Sending LED colors to WLED device:" << wledAddress << ":" << wledPort;
|
|
|
|
return qapp->exec();
|
|
}
|