KDEAmbi/hyperiongrabber.cpp
Tobias J. Endres 4f8d53fedb refactor(grabber): Simplify frame scaling by removing OpenGL
Replaced the complex, low-level OpenGL pipeline in HyperionGrabber with a simple, high-level `QImage::scaled()` call.

This change significantly reduces code complexity and improves maintainability without sacrificing performance, as `QImage::scaled()` is highly optimized and often hardware-accelerated. The new implementation is more robust and easier to understand.

This resolves the following issues:
- Removes fragile, hard-to-debug OpenGL code.
- Eliminates potential bugs related to FBOs, shaders, and texture handling.
- Corrects the image data pipeline to ensure raw pixel data is processed consistently.
2025-08-15 00:41:07 +02:00

155 lines
5.1 KiB
C++

#include "hyperiongrabber.h"
#include <QCoreApplication>
#include <QScreen>
#include <QGuiApplication>
#include <QBuffer>
#include <QImage>
// public
HyperionGrabber::HyperionGrabber(QHash<QString, QString> opts)
{
QString addr = "localhost";
unsigned short port = 19444;
unsigned short scale = 8;
unsigned short priority = 100;
QString redAdjust = "", greenAdjust = "", blueAdjust = "";
QString temperature = "", threshold = "", transform = "";
_scale_m = scale;
QHashIterator<QString, QString> i(opts);
while (i.hasNext()) {
i.next();
if ((i.key() == "a" || i.key() == "address") && !(i.value().isNull() && i.value().isEmpty())) {
addr = i.value();
} else if ((i.key() == "p" || i.key() == "port") && i.value().toUShort()) {
port = i.value().toUShort();
} else if ((i.key() == "c" || i.key() == "priority") && (i.value().toUShort() && i.value().toUShort() <= 255)) {
priority = i.value().toUShort();
} else if ((i.key() == "s" || i.key() == "scale") && i.value().toUShort()) {
scale = i.value().toUShort();
} else if ((i.key() == "f" || i.key() == "frameskip") && i.value().toUShort()) {
_frameskip_m = i.value().toUShort();
} else if ((i.key() == "i" || i.key() == "inactive") && i.value().toInt()) {
_inactiveTime_m = (i.value().toInt() * 1000);
} else if (i.key() == "r" || i.key() == "redadjust") {
redAdjust = _parseColorArr(i.value(), 1);
} else if (i.key() == "g" || i.key() == "greenadjust") {
greenAdjust = _parseColorArr(i.value(), 1);
} else if (i.key() == "b" || i.key() == "blueadjust") {
blueAdjust = _parseColorArr(i.value(), 1);
} else if (i.key() == "t" || i.key() == "temperature") {
temperature = _parseColorArr(i.value(), 1);
} else if (i.key() == "d" || i.key() == "threshold") {
threshold = _parseColorArr(i.value(), 0);
} else if ((i.key() == "l" || i.key() == "transform") && _parseColorArr(i.value(), 0) != "") {
transform = i.value();
}
}
_hyperionPriority_m = QString::number(priority);
_hclient_p = new HyperionClient(addr, port, _hyperionPriority_m);
_hclient_p->ledAdjustments(redAdjust, greenAdjust, blueAdjust, temperature, threshold, transform);
_waylandGrabber_p = new WaylandGrabber(this);
connect(_waylandGrabber_p, &WaylandGrabber::frameReady, this, &HyperionGrabber::_processFrame);
_waylandGrabber_p->start();
QScreen *screen = QGuiApplication::primaryScreen();
if (screen) {
_setImgSize(screen->size().width() / scale, screen->size().height() / scale);
} else {
qWarning() << "Could not get primary screen size for Wayland grabber.";
_setImgSize(1920 / scale, 1080 / scale); // Default to 1080p if screen not found
}
if (_inactiveTime_m) {
_timer_p = new QTimer(this);
connect(_timer_p, &QTimer::timeout, this, &HyperionGrabber::_inActivity);
_timer_p->start(_inactiveTime_m);
}
}
HyperionGrabber::~HyperionGrabber()
{
if (_timer_p) {
_timer_p->stop();
delete _timer_p;
}
delete _hclient_p;
}
// private
QString HyperionGrabber::_parseColorArr(QString value, bool isInt)
{
QStringList values = value.split(',');
if (values.size() != 3) {
return "";
}
value = "[";
for (int i = 0; i < 3; i++) {
if (isInt && (values.at(i).toInt() < 0 || values.at(i).toInt() > 255)) {
return "";
}
if (!isInt && (values.at(i).toDouble() < 0.0 || values.at(i).toDouble() > 1.0)) {
return "";
}
value.append(values.at(i));
value.append(",");
}
value.chop(1);
value.append("]");
return value;
}
// private slots
void HyperionGrabber::_inActivity()
{
_hclient_p->clearLeds();
}
void HyperionGrabber::_processFrame(const QVideoFrame &frame)
{
if (!frame.isValid()) {
return;
}
_frameCounter_m++;
if (_frameskip_m > 0 && (_frameCounter_m % (_frameskip_m + 1) != 0)) {
return;
}
QImage image = frame.toImage();
if (image.isNull()) {
qWarning() << "Failed to convert QVideoFrame to QImage.";
return;
}
// Calculate target size
QSize targetSize(image.width() / _scale_m, image.height() / _scale_m);
if (!targetSize.isValid()) {
qWarning() << "Invalid target size for scaling.";
return;
}
// Scale the image using Qt's optimized scaling
QImage scaledImage = image.scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
// Convert to a format suitable for Hyperion (RGB888)
if (scaledImage.format() != QImage::Format_RGB888) {
scaledImage = scaledImage.convertToFormat(QImage::Format_RGB888);
}
// Update the image size in the client and send
_hclient_p->setImgSize(scaledImage.width(), scaledImage.height());
_hclient_p->sendImage(scaledImage.constBits(), scaledImage.sizeInBytes());
}
void HyperionGrabber::_setImgSize(int width, int height)
{
_hclient_p->setImgSize(width, height);
}