#include "hyperiongrabber.h" #include #include #include #include #include #include #include // For std::abs // public HyperionGrabber::HyperionGrabber(QHash opts) { QString addr = "192.168.1.177"; // Default WLED IP unsigned short port = 21324; // Default WARLS port unsigned short scale = 8; unsigned short frameskip = 0; int inactiveTime = 0; int changeThreshold = 5000; // Default change threshold _scale_m = scale; _frameskip_m = frameskip; _inactiveTime_m = inactiveTime; QHashIterator 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() == "s" || i.key() == "scale") && i.value().toUShort()) { _scale_m = 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() == "t" || i.key() == "threshold") && i.value().toInt()) { // New option for threshold _changeThreshold_m = i.value().toInt(); } } #ifdef DEBUG_MODE _client_p = new DebugClient(this); #else _client_p = new WledClient(addr, port, this); #endif _waylandGrabber_p = new WaylandGrabber(this); connect(_waylandGrabber_p, &WaylandGrabber::frameReady, this, &HyperionGrabber::_processFrame); _waylandGrabber_p->start(); QScreen *screen = QGuiApplication::primaryScreen(); if (screen) { qDebug() << "Screen size:" << screen->size().width() << "x" << screen->size().height(); } else { qWarning() << "Could not get primary screen size for Wayland grabber. Defaulting to 1920x1080."; } } HyperionGrabber::~HyperionGrabber() { if (_timer_p) { _timer_p->stop(); delete _timer_p; } delete _client_p; // Changed from _wledClient_p } // private slots 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 WLED (RGB888) if (scaledImage.format() != QImage::Format_RGB888) { scaledImage = scaledImage.convertToFormat(QImage::Format_RGB888); } // Compare with last scaled image if (_lastScaledImage_m.isNull() || _lastScaledImage_m.size() != scaledImage.size()) { // First frame or size changed, send immediately qDebug() << "First frame or image size changed. Sending to client."; _client_p->sendImage(scaledImage); // Changed from _wledClient_p _lastScaledImage_m = scaledImage; } else { long long diff = 0; for (int y = 0; y < scaledImage.height(); ++y) { for (int x = 0; x < scaledImage.width(); ++x) { QRgb currentPixel = scaledImage.pixel(x, y); QRgb lastPixel = _lastScaledImage_m.pixel(x, y); diff += std::abs(qRed(currentPixel) - qRed(lastPixel)); diff += std::abs(qGreen(currentPixel) - qGreen(lastPixel)); diff += std::abs(qBlue(currentPixel) - qBlue(lastPixel)); } } if (diff > _changeThreshold_m) { qDebug() << "Image changed significantly. Sending to client. Difference:" << diff; _client_p->sendImage(scaledImage); // Changed from _wledClient_p _lastScaledImage_m = scaledImage; } else { qDebug() << "Image is static (below threshold). Not sending to client. Difference:" << diff; } } }