From bcf14716a352653a2d30a37b9c99625add2dd9cc Mon Sep 17 00:00:00 2001 From: Awawa <69086569+awawa-dev@users.noreply.github.com> Date: Thu, 26 Oct 2023 02:31:34 +0200 Subject: [PATCH] Pipewire DMA & EGL hardware support 2 (Wayland/x11 grabber) (#556) --- include/base/Grabber.h | 2 - include/base/SystemWrapper.h | 1 - include/grabber/PipewireGrabber.h | 12 +- include/grabber/PipewireWrapper.h | 1 - include/grabber/smartPipewire.h | 11 +- sources/base/Grabber.cpp | 5 - sources/base/HyperHdrInstance.cpp | 2 +- sources/base/SystemControl.cpp | 3 - sources/base/SystemWrapper.cpp | 10 -- sources/grabber/pipewire/PipewireGrabber.cpp | 129 +++++++++---------- sources/grabber/pipewire/PipewireHandler.cpp | 50 +++---- sources/grabber/pipewire/PipewireHandler.h | 7 +- sources/grabber/pipewire/PipewireWrapper.cpp | 5 - sources/grabber/pipewire/smartPipewire.cpp | 24 +++- 14 files changed, 113 insertions(+), 149 deletions(-) diff --git a/include/base/Grabber.h b/include/base/Grabber.h index 3acc5cfbe..faacd5dab 100644 --- a/include/base/Grabber.h +++ b/include/base/Grabber.h @@ -141,8 +141,6 @@ class Grabber : public DetectionAutomatic, public DetectionManual }; public slots: - virtual bool isRunning(); - virtual bool start() = 0; virtual void stop() = 0; diff --git a/include/base/SystemWrapper.h b/include/base/SystemWrapper.h index 7045d9c48..74bde6323 100644 --- a/include/base/SystemWrapper.h +++ b/include/base/SystemWrapper.h @@ -59,7 +59,6 @@ public slots: void setHdrToneMappingEnabled(int mode); void handleSettingsUpdate(settings::type type, const QJsonDocument& config); virtual void stateChanged(bool state); - bool isRunning(); protected: virtual QString getGrabberInfo(); diff --git a/include/grabber/PipewireGrabber.h b/include/grabber/PipewireGrabber.h index 7ca3228cb..395c91b58 100644 --- a/include/grabber/PipewireGrabber.h +++ b/include/grabber/PipewireGrabber.h @@ -18,7 +18,6 @@ #include #include #include -#include // general JPEG decoder includes #include @@ -43,11 +42,11 @@ class PipewireGrabber : public Grabber void stateChanged(bool state); - static void callbackFunction(const PipewireImage& frame); +private slots: -public slots: + void grabFrame(); - void grabFrame(const PipewireImage& data); +public slots: bool start() override; @@ -57,8 +56,6 @@ public slots: void newWorkerFrameError(unsigned int workerIndex, QString error, quint64 sourceCount) override {}; - bool isRunning() override; - private: QString GetSharedLut(); @@ -78,11 +75,12 @@ public slots: private: QString _configurationPath; + QTimer _timer; + QSemaphore _semaphore; void* _library; int _actualDisplay; bool _isActive; bool _storedToken; bool _versionCheck; - bool _hasFrame; }; diff --git a/include/grabber/PipewireWrapper.h b/include/grabber/PipewireWrapper.h index 88f88baae..5b83948ab 100644 --- a/include/grabber/PipewireWrapper.h +++ b/include/grabber/PipewireWrapper.h @@ -13,7 +13,6 @@ class PipewireWrapper : public SystemWrapper public slots: void stateChanged(bool state) override; - void processFrame(const PipewireImage& frame); protected: QString getGrabberInfo() override; diff --git a/include/grabber/smartPipewire.h b/include/grabber/smartPipewire.h index 5c98e8ca4..3f7040e91 100644 --- a/include/grabber/smartPipewire.h +++ b/include/grabber/smartPipewire.h @@ -8,14 +8,13 @@ struct PipewireImage bool isError; int width, height, stride; bool isOrderRgb; - uint8_t* data; + uint8_t* data = nullptr; }; - -typedef int (*pipewire_callback_func)(const PipewireImage& frame); - extern "C" const char* getPipewireToken(); extern "C" const char* getPipewireError(); extern "C" bool hasPipewire(); -extern "C" void initPipewireDisplay(const char* restorationToken, uint32_t requestedFPS, pipewire_callback_func callback); -extern "C" void uniniPipewireDisplay(); +extern "C" void initPipewireDisplay(const char* restorationToken, uint32_t requestedFPS); +extern "C" void uninitPipewireDisplay(); +extern "C" PipewireImage getFramePipewire(); +extern "C" void releaseFramePipewire(); diff --git a/sources/base/Grabber.cpp b/sources/base/Grabber.cpp index d1500e072..3812eccaa 100644 --- a/sources/base/Grabber.cpp +++ b/sources/base/Grabber.cpp @@ -927,8 +927,3 @@ QString Grabber::getConfigurationPath() { return _configurationPath; } - -bool Grabber::isRunning() -{ - return false; -} diff --git a/sources/base/HyperHdrInstance.cpp b/sources/base/HyperHdrInstance.cpp index f71760d69..7a974bcbc 100644 --- a/sources/base/HyperHdrInstance.cpp +++ b/sources/base/HyperHdrInstance.cpp @@ -701,7 +701,7 @@ void HyperHdrInstance::updateResult(std::vector _ledBuffer) else if (prevToken != (_computeStats.token = PerformanceCounters::currentToken())) { - if (diff >= 59000) + if (diff >= 59000 && diff <= 65000) emit PerformanceCounters::getInstance()->newCounter( PerformanceReport(static_cast(PerformanceReportType::INSTANCE), _computeStats.token, _name, _computeStats.total / qMax(diff/1000.0, 1.0), _computeStats.total, 0, 0, getInstanceIndex())); diff --git a/sources/base/SystemControl.cpp b/sources/base/SystemControl.cpp index 1307d407c..1c47bf70e 100644 --- a/sources/base/SystemControl.cpp +++ b/sources/base/SystemControl.cpp @@ -136,9 +136,6 @@ void SystemControl::handleCompStateChangeRequest(hyperhdr::Components component, void SystemControl::setSysInactive() { - if (SystemWrapper::getInstance() != nullptr && SystemWrapper::getInstance()->isRunning()) - return; - if (!_alive) _hyperhdr->setInputInactive(_sysCaptPrio); diff --git a/sources/base/SystemWrapper.cpp b/sources/base/SystemWrapper.cpp index 8f7d434f9..910ce7d40 100644 --- a/sources/base/SystemWrapper.cpp +++ b/sources/base/SystemWrapper.cpp @@ -231,13 +231,3 @@ QJsonObject SystemWrapper::getJsonInfo() return systemDevice; } - -bool SystemWrapper::isRunning() -{ - if (_grabber != NULL) - { - return _grabber->isRunning(); - } - - return false; -} diff --git a/sources/grabber/pipewire/PipewireGrabber.cpp b/sources/grabber/pipewire/PipewireGrabber.cpp index ad830dce4..6edf5729e 100644 --- a/sources/grabber/pipewire/PipewireGrabber.cpp +++ b/sources/grabber/pipewire/PipewireGrabber.cpp @@ -47,27 +47,31 @@ #include #include -#include -#include +#include #include #include bool (*_hasPipewire)() = nullptr; const char* (*_getPipewireError)() = nullptr; -void (*_initPipewireDisplay)(const char* restorationToken, uint32_t requestedFPS, pipewire_callback_func callback) = nullptr; +void (*_initPipewireDisplay)(const char* restorationToken, uint32_t requestedFPS) = nullptr; void (*_uninitPipewireDisplay)() = nullptr; +PipewireImage (*_getFramePipewire)() = nullptr; +void (*_releaseFramePipewire)() = nullptr; const char* (*_getPipewireToken)() = nullptr; PipewireGrabber::PipewireGrabber(const QString& device, const QString& configurationPath) : Grabber("PIPEWIRE_SYSTEM:" + device.left(14)) , _configurationPath(configurationPath) + , _semaphore(1) , _library(nullptr) , _actualDisplay(0) , _isActive(false) , _storedToken(false) , _versionCheck(false) - , _hasFrame(false) { + _timer.setTimerType(Qt::PreciseTimer); + connect(&_timer, &QTimer::timeout, this, &PipewireGrabber::grabFrame); + // Load library _library = dlopen("libsmartPipewire.so", RTLD_NOW); @@ -76,13 +80,15 @@ PipewireGrabber::PipewireGrabber(const QString& device, const QString& configura _getPipewireToken = (const char* (*)()) dlsym(_library, "getPipewireToken"); _getPipewireError = (const char* (*)()) dlsym(_library, "getPipewireError"); _hasPipewire = (bool (*)()) dlsym(_library, "hasPipewire"); - _initPipewireDisplay = (void (*)(const char*, uint32_t, pipewire_callback_func)) dlsym(_library, "initPipewireDisplay"); - _uninitPipewireDisplay = (void (*)()) dlsym(_library, "uniniPipewireDisplay"); + _initPipewireDisplay = (void (*)(const char*, uint32_t)) dlsym(_library, "initPipewireDisplay"); + _uninitPipewireDisplay = (void (*)()) dlsym(_library, "uninitPipewireDisplay"); + _getFramePipewire = (PipewireImage (*)()) dlsym(_library, "getFramePipewire"); + _releaseFramePipewire = (void (*)()) dlsym(_library, "releaseFramePipewire"); } else Warning(_log, "Could not load Pipewire proxy library. Error: %s", dlerror()); - if (_library && (_getPipewireToken == nullptr || _hasPipewire == nullptr || _initPipewireDisplay == nullptr || _uninitPipewireDisplay == nullptr )) + if (_library && (_getPipewireToken == nullptr || _hasPipewire == nullptr || _releaseFramePipewire == nullptr || _initPipewireDisplay == nullptr || _uninitPipewireDisplay == nullptr || _getFramePipewire == nullptr)) { Error(_log, "Could not load Pipewire proxy library definition. Error: %s", dlerror()); @@ -147,6 +153,7 @@ void PipewireGrabber::uninit() Debug(_log, "Uninit grabber: %s", QSTRING_CSTR(_deviceName)); } + _initialized = false; } @@ -154,7 +161,6 @@ bool PipewireGrabber::init() { Debug(_log, "init"); - _hasFrame = false; if (!_initialized) { @@ -230,6 +236,8 @@ bool PipewireGrabber::start() { if (init()) { + _timer.setInterval(1000/_fps); + _timer.start(); Info(_log, "Started"); return true; } @@ -246,11 +254,14 @@ void PipewireGrabber::stop() { if (_initialized) { + _semaphore.acquire(); + _timer.stop(); + _uninitPipewireDisplay(); _isActive = false; _initialized = false; - _hasFrame = false; + _semaphore.release(); Info(_log, "Stopped"); } } @@ -268,7 +279,7 @@ bool PipewireGrabber::init_device(int _display) token = ""; else Info(_log, "Loading restoration token: %s", QSTRING_CSTR(maskToken(token))); - _initPipewireDisplay(token.toLatin1().constData(), _fps, (pipewire_callback_func) &PipewireGrabber::callbackFunction); + _initPipewireDisplay(token.toLatin1().constData(), _fps); _isActive = true; @@ -302,60 +313,67 @@ void PipewireGrabber::stateChanged(bool state) } } -void PipewireGrabber::grabFrame(const PipewireImage& data) +void PipewireGrabber::grabFrame() { bool stopNow = false; - if (_initialized && _isActive) + if (_semaphore.tryAcquire()) { - if (!_versionCheck) + if (_initialized && _isActive) { - if (data.version >= 4) - Info(_log, "Portal protocol version: %i", data.version); - else - Warning(_log, "Legacy portal protocol version: %i. To enjoy persistant autorization since version 4, you should update xdg-desktop-portal at least to version 1.12.1 *AND* provide backend that can implement it (for example newest xdg-desktop-portal-gnome).", data.version); + PipewireImage data = _getFramePipewire(); - _versionCheck = true; - } + if (!_versionCheck) + { + if (data.version >= 4) + Info(_log, "Portal protocol version: %i", data.version); + else + Warning(_log, "Legacy portal protocol version: %i. To enjoy persistant autorization since version 4, you should update xdg-desktop-portal at least to version 1.12.1 *AND* provide backend that can implement it (for example newest xdg-desktop-portal-gnome).", data.version); + _versionCheck = true; + } - if (!_storedToken && !data.isError) - { - QString token = QString("%1").arg(_getPipewireToken()); - if (!token.isEmpty()) + if (!_storedToken && !data.isError) { - AuthManager* instance = AuthManager::getInstance(); + QString token = QString("%1").arg(_getPipewireToken()); - Info(_log, "Saving restoration token: %s", QSTRING_CSTR(maskToken(token))); + if (!token.isEmpty()) + { + AuthManager* instance = AuthManager::getInstance(); - instance->savePipewire(token); + Info(_log, "Saving restoration token: %s", QSTRING_CSTR(maskToken(token))); - _storedToken = true; - } - } + instance->savePipewire(token); + _storedToken = true; + } + } - if (data.data == nullptr) - { - if (data.isError) + + if (data.data == nullptr) { - QString err = QString("%1").arg(_getPipewireError()); - Error(_log, "Could not capture pipewire frame: %s", QSTRING_CSTR(err)); - stopNow = true; + if (data.isError) + { + QString err = QString("%1").arg(_getPipewireError()); + Error(_log, "Could not capture pipewire frame: %s", QSTRING_CSTR(err)); + stopNow = true; + } } - } - else - { - _hasFrame = true; - _actualWidth = data.width; - _actualHeight = data.height; - - if (data.isOrderRgb) - processSystemFrameRGBA(data.data, data.stride); else - processSystemFrameBGRA(data.data, data.stride); + { + _actualWidth = data.width; + _actualHeight = data.height; + + if (data.isOrderRgb) + processSystemFrameRGBA(data.data); + else + processSystemFrameBGRA(data.data); + + _releaseFramePipewire(); + } } + _semaphore.release(); } if (stopNow) @@ -372,24 +390,3 @@ void PipewireGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigne _cropTop = cropTop; _cropBottom = cropBottom; } - -void PipewireGrabber::callbackFunction(const PipewireImage& frame) -{ - if (SystemWrapper::getInstance() == nullptr) - return; - - PipewireWrapper* wrapper = dynamic_cast(SystemWrapper::getInstance()); - - if (wrapper != NULL) - { - if (QThread::currentThread() == wrapper->thread()) - wrapper->processFrame(frame); - else - QMetaObject::invokeMethod(wrapper, "processFrame", Qt::ConnectionType::BlockingQueuedConnection, Q_ARG(const PipewireImage&, frame)); - } -} - -bool PipewireGrabber::isRunning() -{ - return _initialized && _hasFrame; -} diff --git a/sources/grabber/pipewire/PipewireHandler.cpp b/sources/grabber/pipewire/PipewireHandler.cpp index d95a42527..f7582d92c 100644 --- a/sources/grabber/pipewire/PipewireHandler.cpp +++ b/sources/grabber/pipewire/PipewireHandler.cpp @@ -81,17 +81,14 @@ const QString PORTAL_RESPONSE = QStringLiteral("Response"); const QString REQUEST_TEMPLATE = QStringLiteral("/org/freedesktop/portal/desktop/request/%1/%2"); PipewireHandler::PipewireHandler() : - _callback(nullptr), _sessionHandle(""), _restorationToken(""), _errorMessage(""), _portalStatus(false), + _sessionHandle(""), _restorationToken(""), _errorMessage(""), _portalStatus(false), _isError(false), _version(0), _streamNodeId(0), _sender(""), _replySessionPath(""), _sourceReplyPath(""), _startReplyPath(""), _pwMainThreadLoop(nullptr), _pwNewContext(nullptr), _pwContextConnection(nullptr), _pwStream(nullptr), _frameWidth(0),_frameHeight(0),_frameOrderRgb(false), _framePaused(false), _requestedFPS(10), _hasFrame(false), _infoUpdated(false), _initEGL(false), _libEglHandle(NULL), _libGlHandle(NULL), - _frameDrmFormat(DRM_FORMAT_MOD_INVALID), _frameDrmModifier(DRM_FORMAT_MOD_INVALID) + _frameDrmFormat(DRM_FORMAT_MOD_INVALID), _frameDrmModifier(DRM_FORMAT_MOD_INVALID), _image() { - _timer.setTimerType(Qt::PreciseTimer); - connect(&_timer, &QTimer::timeout, this, &PipewireHandler::grabFrame); - _pwStreamListener = {}; _pwCoreListener = {}; @@ -102,9 +99,6 @@ PipewireHandler::PipewireHandler() : connect(this, &PipewireHandler::onStateChangedSignal, this, &PipewireHandler::onStateChanged); connect(this, &PipewireHandler::onProcessFrameSignal, this, &PipewireHandler::onProcessFrame); connect(this, &PipewireHandler::onCoreErrorSignal, this, &PipewireHandler::onCoreError); - - _image.isError = true; - _image.data = nullptr; } QString PipewireHandler::getToken() @@ -140,8 +134,6 @@ PipewireHandler::~PipewireHandler() void PipewireHandler::closeSession() { - _timer.stop(); - if (_pwMainThreadLoop != nullptr) { pw_thread_loop_wait(_pwMainThreadLoop); @@ -218,7 +210,6 @@ void PipewireHandler::closeSession() _frameHeight = 0; _frameOrderRgb = false; _framePaused = false; - _callback = nullptr; _requestedFPS = 10; _hasFrame = false; _infoUpdated = false; @@ -245,11 +236,7 @@ void PipewireHandler::closeSession() for (supportedDmaFormat& supVal : _supportedDmaFormatsList) supVal.hasDma = false; - if (_image.data != nullptr) - { - free(_image.data); - _image.data = nullptr; - } + releaseWorkingFrame(); if (_version > 0) { @@ -258,6 +245,15 @@ void PipewireHandler::closeSession() } } +void PipewireHandler::releaseWorkingFrame() +{ + if (_image.data != nullptr) + { + free(_image.data); + _image.data = nullptr; + } +} + QString PipewireHandler::getSessionToken() { return QString("hyperhdr_s%1").arg(QUuid::createUuid().toString(QUuid::Id128)); @@ -300,18 +296,12 @@ int PipewireHandler::readVersion() return version; } -void PipewireHandler::startSession(QString restorationToken, uint32_t requestedFPS, pipewire_callback_func callback) +void PipewireHandler::startSession(QString restorationToken, uint32_t requestedFPS) { std::cout << "Pipewire: initialization invoked. Cleaning up first..." << std::endl; closeSession(); - if (callback == nullptr) - { - reportError("Pipewire: missing callback."); - return; - } - if (requestedFPS < 1 || requestedFPS > 60) { reportError("Pipewire: invalid capture rate."); @@ -329,7 +319,6 @@ void PipewireHandler::startSession(QString restorationToken, uint32_t requestedF } _requestedFPS = requestedFPS; - _callback = callback; _sender = QString("%1").arg(QDBusConnection::sessionBus().baseService()).replace('.','_'); if (_sender.length() > 0 && _sender[0] == ':') @@ -572,9 +561,6 @@ void PipewireHandler::startResponse(uint response, const QVariantMap& results) return; } - _timer.setInterval(1000 / _requestedFPS); - _timer.start(); - pw_thread_loop_unlock(_pwMainThreadLoop); } @@ -683,15 +669,9 @@ void PipewireHandler::onProcessFrame() } }; -void PipewireHandler::grabFrame() +void PipewireHandler::getImage(PipewireImage& retVal) { - if (_image.data != nullptr) - { - _callback(_image); - - free(_image.data); - _image.data = nullptr; - } + retVal = _image; } void PipewireHandler::captureFrame() diff --git a/sources/grabber/pipewire/PipewireHandler.h b/sources/grabber/pipewire/PipewireHandler.h index 4d7b95126..cc6201202 100644 --- a/sources/grabber/pipewire/PipewireHandler.h +++ b/sources/grabber/pipewire/PipewireHandler.h @@ -71,7 +71,7 @@ class PipewireHandler : public QObject PipewireHandler(); ~PipewireHandler(); - void startSession(QString restorationToken, uint32_t requestedFPS, pipewire_callback_func callback); + void startSession(QString restorationToken, uint32_t requestedFPS); void closeSession(); bool hasError(); @@ -89,7 +89,8 @@ class PipewireHandler : public QObject }; public Q_SLOTS: - void grabFrame(); + void releaseWorkingFrame(); + void getImage(PipewireImage& retVal); void createSessionResponse(uint response, const QVariantMap& results); void selectSourcesResponse(uint response, const QVariantMap& results); void startResponse(uint response, const QVariantMap& results); @@ -114,7 +115,6 @@ public Q_SLOTS: QString getRequestToken(); void captureFrame(); - pipewire_callback_func _callback; QString _sessionHandle; QString _restorationToken; QString _errorMessage; @@ -149,7 +149,6 @@ public Q_SLOTS: void* _libGlHandle; int64_t _frameDrmFormat; int64_t _frameDrmModifier; - QTimer _timer; PipewireImage _image; #ifdef ENABLE_PIPEWIRE_EGL diff --git a/sources/grabber/pipewire/PipewireWrapper.cpp b/sources/grabber/pipewire/PipewireWrapper.cpp index b86b129af..6473376fe 100644 --- a/sources/grabber/pipewire/PipewireWrapper.cpp +++ b/sources/grabber/pipewire/PipewireWrapper.cpp @@ -54,8 +54,3 @@ void PipewireWrapper::stateChanged(bool state) _grabber.stateChanged(state); } -void PipewireWrapper::processFrame(const PipewireImage& frame) -{ - _grabber.grabFrame(frame); -} - diff --git a/sources/grabber/pipewire/smartPipewire.cpp b/sources/grabber/pipewire/smartPipewire.cpp index 1bcb6ba0e..335ade0b1 100644 --- a/sources/grabber/pipewire/smartPipewire.cpp +++ b/sources/grabber/pipewire/smartPipewire.cpp @@ -51,11 +51,16 @@ std::unique_ptr _pipewireHandler(nullptr); -void initPipewireDisplay(const char* restorationToken, uint32_t requestedFPS, pipewire_callback_func callback) +void initPipewireDisplay(const char* restorationToken, uint32_t requestedFPS) { QString qRestorationToken = QString("%1").arg(restorationToken); _pipewireHandler = std::unique_ptr(new PipewireHandler()); - _pipewireHandler->startSession(qRestorationToken, requestedFPS, callback); + _pipewireHandler->startSession(qRestorationToken, requestedFPS); +} + +void releaseFramePipewire() +{ + _pipewireHandler->releaseWorkingFrame(); } const char* getPipewireToken() @@ -82,7 +87,7 @@ const char* getPipewireError() return nullptr; } -void uniniPipewireDisplay() +void uninitPipewireDisplay() { if (_pipewireHandler != nullptr) { @@ -125,3 +130,16 @@ bool hasPipewire() } return false; } + +PipewireImage getFramePipewire() +{ + PipewireImage retVal; + + if (_pipewireHandler != nullptr) + { + _pipewireHandler->getImage(retVal); + } + + return retVal; +} +