diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea84fc1..4e770924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support gaps on Matrix Layout (#1696) +- Windows: Added a new grabber that uses the DXGI DDA (Desktop Duplication API). This has much better performance than the DX grabber as it does more of its work on the GPU. **JSON-API** - New subscription support for event updates, i.e. `Suspend, Resume, Idle, idleResume, Restart, Quit`. - Support direct or multiple instance addressing via single requests (#809) - Support of `serverinfo` subcommands: `getInfo, subscribe, unsubscribe, getSubscriptions, getSubscriptionCommands` -- [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates +- [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates ### Changed @@ -35,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization. - Provide additional error details with API responses, esp. on JSON parsing, validation or token errors. - Generate random TANs for every API request from the Hyperion UI -- Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections- +- Fixed: Handling of IP4 addresses wrapped in IPv6 for external network connections- ### Removed @@ -135,7 +136,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen - Support streaming to individual WLED segments (requires WLED 0.13.3+). To allow segment streaming, enable "Realtime - Use main segment only" in WLED's Sync Interfaces setup screen - Allow to keep WLED powered on after streaming and restoring state -- Allow to Disable / Enable all instances (#970) by +- Allow to Disable / Enable all instances (#970) by - Suspend/Resume support for Linux and Windows (#1493,#1282, #978). Suspend/Resume/Restart is supported via API, UI, Systray and hyperion-remote - Idle scenario via Screen Locking (Linux/Windows), Screensaver invokation (Linux), hyperion-remote or API diff --git a/CMakeLists.txt b/CMakeLists.txt index 7818a6f1..7cb7366a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(DEFAULT_AMLOGIC OFF) set(DEFAULT_DISPMANX OFF) set(DEFAULT_DX OFF) +set(DEFAULT_DDA OFF) set(DEFAULT_MF OFF) set(DEFAULT_OSX OFF) set(DEFAULT_QT ON ) @@ -121,6 +122,7 @@ if(${CMAKE_SYSTEM} MATCHES "Linux") set(DEFAULT_CEC ON) elseif (WIN32) set(DEFAULT_DX ON) + set(DEFAULT_DDA ON) set(DEFAULT_MF ON) else() set(DEFAULT_FB OFF) @@ -227,6 +229,7 @@ if(HYPERION_LIGHT) SET ( DEFAULT_AMLOGIC OFF ) SET ( DEFAULT_DISPMANX OFF ) SET ( DEFAULT_DX OFF ) + SET ( DEFAULT_DDA OFF ) SET ( DEFAULT_FB OFF ) SET ( DEFAULT_MF OFF ) SET ( DEFAULT_OSX OFF ) @@ -264,6 +267,9 @@ message(STATUS "ENABLE_DISPMANX = ${ENABLE_DISPMANX}") option(ENABLE_DX "Enable the DirectX grabber" ${DEFAULT_DX}) message(STATUS "ENABLE_DX = ${ENABLE_DX}") +option(ENABLE_DDA "Enable the DXGI DDA grabber" ${DEFAULT_DDA}) +message(STATUS "ENABLE_DDA = ${ENABLE_DDA}") + if(ENABLE_AMLOGIC) set(ENABLE_FB ON) else() diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 324b703b..086c4ef0 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -9,6 +9,9 @@ // Define to enable the DirectX grabber #cmakedefine ENABLE_DX +// Define to enable the DXGI DDA (Desktop Duplication API) grabber +#cmakedefine ENABLE_DDA + // Define to enable the framebuffer grabber // Define to enable the Audio grabber #cmakedefine ENABLE_AUDIO diff --git a/doc/development/CompileHowto.md b/doc/development/CompileHowto.md index dcef8093..2de4a770 100644 --- a/doc/development/CompileHowto.md +++ b/doc/development/CompileHowto.md @@ -128,7 +128,7 @@ We assume a 64bit Windows 10. Install the following; - [CMake (Windows win64-x64 installer)](https://cmake.org/download/) (Check: Add to PATH) - [Visual Studio 2022 Community Edition](https://visualstudio.microsoft.com/downloads/#visual-studio-community-2022) - Select 'Desktop development with C++' - - On the right, just select `MSVC v143 VS 2022 C++ x64/x86-Buildtools` and latest `Windows 10 SDK`. Everything else is not needed. + - On the right, just select `MSVC v143 VS 2022 C++ x64/x86-Buildtools`, `C++ ATL for latest v143 build tools (x86 & x64)` and latest `Windows 10 SDK`. Everything else is not needed. - [Win64 OpenSSL v1.1.1w](https://slproweb.com/products/Win32OpenSSL.html) ([direct link](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe)) - [Python 3 (Windows x86-64 executable installer)](https://www.python.org/downloads/windows/) (Check: Add to PATH and Debug Symbols) - Open a console window and execute `pip install aqtinstall`. diff --git a/include/grabber/GrabberConfig.h b/include/grabber/GrabberConfig.h index f3a575c2..0b14132c 100644 --- a/include/grabber/GrabberConfig.h +++ b/include/grabber/GrabberConfig.h @@ -27,6 +27,10 @@ #include #endif +#ifdef ENABLE_DDA +#include +#endif + #if defined(ENABLE_X11) #include #endif @@ -35,10 +39,6 @@ #include #endif -#if defined(ENABLE_DX) -#include -#endif - #if defined(ENABLE_FB) #include #endif diff --git a/include/grabber/dda/DDAGrabber.h b/include/grabber/dda/DDAGrabber.h new file mode 100644 index 00000000..561434f4 --- /dev/null +++ b/include/grabber/dda/DDAGrabber.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include +#include + +class DDAGrabberImpl; + +class DDAGrabber : public Grabber +{ +public: + DDAGrabber(int display = 0, int cropLeft = 0, int cropRight = 0, int cropTop = 0, int cropBottom = 0); + + virtual ~DDAGrabber(); + + /// + /// Captures a single snapshot of the display and writes the data to the given image. The + /// provided image should have the same dimensions as the configured values (_width and _height) + /// + /// @param[out] image The snapped screenshot + /// + int grabFrame(Image &image); + + /// + /// @brief Set a new video mode + /// + void setVideoMode(VideoMode mode) override; + + /// + /// @brief Apply new width/height values, overwrite Grabber.h implementation + /// + bool setWidthHeight(int /* width */, int /*height*/) override + { + return true; + } + + /// + /// @brief Apply new pixelDecimation + /// + bool setPixelDecimation(int pixelDecimation) override; + + /// + /// Set the crop values + /// @param cropLeft Left pixel crop + /// @param cropRight Right pixel crop + /// @param cropTop Top pixel crop + /// @param cropBottom Bottom pixel crop + /// + void setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom); + + /// + /// @brief Apply display index + /// + bool setDisplayIndex(int index) override; + + /// @brief Discover QT screens available (for configuration). + /// + /// @param[in] params Parameters used to overwrite discovery default behaviour + /// + /// @return A JSON structure holding a list of devices found + /// + QJsonObject discover(const QJsonObject ¶ms); + +private: + /// + /// @brief Setup a new capture display, will free the previous one + /// @return True on success, false if no display is found + /// + bool restartCapture(); + +private: + std::unique_ptr d; +}; diff --git a/include/grabber/dda/DDAWrapper.h b/include/grabber/dda/DDAWrapper.h new file mode 100644 index 00000000..8b8266a3 --- /dev/null +++ b/include/grabber/dda/DDAWrapper.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +class DDAWrapper : public GrabberWrapper +{ +public: + static constexpr const char *GRABBERTYPE = "DDA"; + + DDAWrapper(int updateRate_Hz = GrabberWrapper::DEFAULT_RATE_HZ, int display = 0, + int pixelDecimation = GrabberWrapper::DEFAULT_PIXELDECIMATION, int cropLeft = 0, int cropRight = 0, + int cropTop = 0, int cropBottom = 0); + + DDAWrapper(const QJsonDocument &grabberConfig = QJsonDocument()); + + virtual ~DDAWrapper(){}; + +public slots: + virtual void action(); + +private: + DDAGrabber _grabber; +}; diff --git a/libsrc/api/JsonInfo.cpp b/libsrc/api/JsonInfo.cpp index e2a73ffe..7c3f8129 100644 --- a/libsrc/api/JsonInfo.cpp +++ b/libsrc/api/JsonInfo.cpp @@ -592,6 +592,10 @@ QJsonArray JsonInfo::discoverScreenInputs(const QJsonObject& params) const discoverGrabber(screenInputs, params); #endif +#ifdef ENABLE_DDA + discoverGrabber(screenInputs, params); +#endif + #ifdef ENABLE_X11 discoverGrabber(screenInputs, params); #endif diff --git a/libsrc/grabber/CMakeLists.txt b/libsrc/grabber/CMakeLists.txt index 75f8c529..daf0bc57 100644 --- a/libsrc/grabber/CMakeLists.txt +++ b/libsrc/grabber/CMakeLists.txt @@ -34,6 +34,10 @@ if(ENABLE_DX) add_subdirectory(directx) endif(ENABLE_DX) +if(ENABLE_DDA) + add_subdirectory(dda) +endif(ENABLE_DDA) + if(ENABLE_AUDIO) add_subdirectory(audio) endif() diff --git a/libsrc/grabber/dda/CMakeLists.txt b/libsrc/grabber/dda/CMakeLists.txt new file mode 100644 index 00000000..e78ecacc --- /dev/null +++ b/libsrc/grabber/dda/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(dda-grabber + ${CMAKE_SOURCE_DIR}/include/grabber/dda/DDAGrabber.h + ${CMAKE_SOURCE_DIR}/include/grabber/dda/DDAWrapper.h + ${CMAKE_SOURCE_DIR}/libsrc/grabber/dda/DDAGrabber.cpp + ${CMAKE_SOURCE_DIR}/libsrc/grabber/dda/DDAWrapper.cpp +) + +target_link_libraries(dda-grabber + hyperion + d3d11.lib + dxgi.lib +) diff --git a/libsrc/grabber/dda/DDAGrabber.cpp b/libsrc/grabber/dda/DDAGrabber.cpp new file mode 100644 index 00000000..aea46046 --- /dev/null +++ b/libsrc/grabber/dda/DDAGrabber.cpp @@ -0,0 +1,357 @@ +#include "grabber/dda/DDAGrabber.h" + +#include +#include +#include +#include +#include + +#pragma comment(lib, "d3d9.lib") +#pragma comment(lib, "dxva2.lib") + +namespace +{ +// Driver types supported. +constexpr D3D_DRIVER_TYPE kDriverTypes[] = { + D3D_DRIVER_TYPE_HARDWARE, + D3D_DRIVER_TYPE_WARP, + D3D_DRIVER_TYPE_REFERENCE, +}; + +// Feature levels supported. +D3D_FEATURE_LEVEL kFeatureLevels[] = {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_1}; + +// Returns true if the two texture descriptors are compatible for copying. +bool areTextureDescriptionsCompatible(D3D11_TEXTURE2D_DESC a, D3D11_TEXTURE2D_DESC b) +{ + return a.Width == b.Width && a.Height == b.Height && a.MipLevels == b.MipLevels && a.ArraySize == b.ArraySize && + a.Format == b.Format; +} + +} // namespace + +// Logs a message along with the hex error HRESULT. +#define LOG_ERROR(hr, msg) Error(_log, msg ": 0x%x", hr) + +// Checks if the HRESULT is an error, and if so, logs it and returns from the +// current function. +#define RETURN_IF_ERROR(hr, msg, returnValue) \ + if (FAILED(hr)) \ + { \ + LOG_ERROR(hr, msg); \ + return returnValue; \ + } + +// Checks if the condition is false, and if so, logs an error and returns from +// the current function. +#define RET_CHECK(cond, returnValue) \ + if (!(cond)) \ + { \ + Error(_log, "Assertion failed: " #cond); \ + return returnValue; \ + } + +// Private implementation. These member variables are here and not in the .h +// so we don't have to include in the header and pollute everything +// else that includes it. +class DDAGrabberImpl +{ +public: + int display = 0; + int desktopWidth = 0; + int desktopHeight = 0; + + // Created in the constructor. + CComPtr device; + CComPtr deviceContext; + CComPtr dxgiDevice; + CComPtr dxgiAdapter; + + // Created in restartCapture - only valid while desktop capture is in + // progress. + CComPtr desktopDuplication; + CComPtr intermediateTexture; + D3D11_TEXTURE2D_DESC intermediateTextureDesc; +}; + +DDAGrabber::DDAGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom) + : Grabber("GRABBER-DDA", cropLeft, cropRight, cropTop, cropBottom), d(new DDAGrabberImpl) +{ + d->display = display; + + HRESULT hr = S_OK; + + // Iterate through driver types until we find one that succeeds. + D3D_FEATURE_LEVEL featureLevel; + for (D3D_DRIVER_TYPE driverType : kDriverTypes) + { + hr = D3D11CreateDevice(nullptr, driverType, nullptr, 0, kFeatureLevels, std::size(kFeatureLevels), + D3D11_SDK_VERSION, &d->device, &featureLevel, &d->deviceContext); + if (SUCCEEDED(hr)) + { + break; + } + } + RETURN_IF_ERROR(hr, "CreateDevice failed", ); + + // Get the DXGI factory. + hr = d->device.QueryInterface(&d->dxgiDevice); + RETURN_IF_ERROR(hr, "Failed to get DXGI device", ); + + // Get the factory's adapter. + hr = d->dxgiDevice->GetAdapter(&d->dxgiAdapter); + RETURN_IF_ERROR(hr, "Failed to get DXGI Adapter", ); +} + +DDAGrabber::~DDAGrabber() +{ +} + +bool DDAGrabber::restartCapture() +{ + if (!d->dxgiAdapter) + { + return false; + } + + HRESULT hr = S_OK; + + d->desktopDuplication.Release(); + + // Get the output that was selected. + CComPtr output; + hr = d->dxgiAdapter->EnumOutputs(d->display, &output); + RETURN_IF_ERROR(hr, "Failed to get output", false); + + // Get the descriptor which has the size of the display. + DXGI_OUTPUT_DESC desc; + hr = output->GetDesc(&desc); + RETURN_IF_ERROR(hr, "Failed to get output description", false); + + d->desktopWidth = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; + d->desktopHeight = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + _width = (d->desktopWidth - _cropLeft - _cropRight) / _pixelDecimation; + _height = (d->desktopHeight - _cropTop - _cropBottom) / _pixelDecimation; + Info(_log, "Desktop size: %dx%d, cropping=%d,%d,%d,%d, decimation=%d, final image size=%dx%d", d->desktopWidth, + d->desktopHeight, _cropLeft, _cropTop, _cropRight, _cropBottom, _pixelDecimation, _width, _height); + + // Get the DXGIOutput1 interface. + CComPtr output1; + hr = output.QueryInterface(&output1); + RETURN_IF_ERROR(hr, "Failed to get output1", false); + + // Create the desktop duplication interface. + hr = output1->DuplicateOutput(d->device, &d->desktopDuplication); + RETURN_IF_ERROR(hr, "Failed to create desktop duplication interface", false); + + return true; +} + +int DDAGrabber::grabFrame(Image &image) +{ + // Do nothing if we're disabled. + if (!_isEnabled) + { + return 0; + } + + // Start the capture if it's not already running. + if (!d->desktopDuplication && !restartCapture()) + { + return -1; + } + + HRESULT hr = S_OK; + + // Release the last frame, if any. + hr = d->desktopDuplication->ReleaseFrame(); + if (FAILED(hr) && hr != DXGI_ERROR_INVALID_CALL) + { + LOG_ERROR(hr, "Failed to release frame"); + } + + // Acquire the next frame. + CComPtr desktopResource; + DXGI_OUTDUPL_FRAME_INFO frameInfo; + hr = d->desktopDuplication->AcquireNextFrame(INFINITE, &frameInfo, &desktopResource); + if (hr == DXGI_ERROR_ACCESS_LOST || hr == DXGI_ERROR_INVALID_CALL) + { + if (!restartCapture()) + { + return -1; + } + return 0; + } + if (hr == DXGI_ERROR_WAIT_TIMEOUT) + { + // This shouldn't happen since we specified an INFINITE timeout. + return 0; + } + RETURN_IF_ERROR(hr, "Failed to acquire next frame", 0); + + // Get the 2D texture. + CComPtr texture; + hr = desktopResource.QueryInterface(&texture); + RETURN_IF_ERROR(hr, "Failed to get 2D texture", 0); + + // The texture we acquired is on the GPU and can't be accessed from the CPU, + // so we have to copy it into another texture that can. + D3D11_TEXTURE2D_DESC textureDesc; + texture->GetDesc(&textureDesc); + + // Create a new intermediate texture if we haven't done so already, or the + // existing one is incompatible with the acquired texture (i.e. it has + // different dimensions). + if (!d->intermediateTexture || !areTextureDescriptionsCompatible(d->intermediateTextureDesc, textureDesc)) + { + Info(_log, "Creating intermediate texture"); + d->intermediateTexture.Release(); + + d->intermediateTextureDesc = textureDesc; + d->intermediateTextureDesc.Usage = D3D11_USAGE_STAGING; + d->intermediateTextureDesc.BindFlags = 0; + d->intermediateTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + d->intermediateTextureDesc.MiscFlags = 0; + + hr = d->device->CreateTexture2D(&d->intermediateTextureDesc, nullptr, &d->intermediateTexture); + RETURN_IF_ERROR(hr, "Failed to create intermediate texture", 0); + } + + // Copy the texture to the intermediate texture. + d->deviceContext->CopyResource(d->intermediateTexture, texture); + RETURN_IF_ERROR(hr, "Failed to copy texture", 0); + + // Map the texture so we can access its pixels. + D3D11_MAPPED_SUBRESOURCE resource; + hr = d->deviceContext->Map(d->intermediateTexture, 0, D3D11_MAP_READ, 0, &resource); + RETURN_IF_ERROR(hr, "Failed to map texture", 0); + + // Copy the texture to the output image. + RET_CHECK(textureDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM, 0); + + ColorRgb *dest = image.memptr(); + for (size_t destY = 0, srcY = _cropTop; destY < image.height(); destY++, srcY += _pixelDecimation) + { + uint32_t *src = + reinterpret_cast(reinterpret_cast(resource.pData) + srcY * resource.RowPitch) + + _cropLeft; + for (size_t destX = 0; destX < image.width(); destX++, src += _pixelDecimation, dest++) + { + *dest = ColorRgb{static_cast(((*src) >> 16) & 0xff), static_cast(((*src) >> 8) & 0xff), + static_cast(((*src) >> 0) & 0xff)}; + } + } + + return 0; +} + +void DDAGrabber::setVideoMode(VideoMode mode) +{ + Grabber::setVideoMode(mode); + restartCapture(); +} + +bool DDAGrabber::setPixelDecimation(int pixelDecimation) +{ + if (Grabber::setPixelDecimation(pixelDecimation)) + return restartCapture(); + + return false; +} + +void DDAGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom) +{ + // Grabber::setCropping rejects the cropped size if it is larger than _width + // and _height, so temporarily set those back to the original pre-cropped full + // desktop sizes first. They'll be set back to the cropped sizes by + // restartCapture. + _width = d->desktopWidth; + _height = d->desktopHeight; + Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom); + restartCapture(); +} + +bool DDAGrabber::setDisplayIndex(int index) +{ + bool rc = true; + if (d->display != index) + { + d->display = index; + rc = restartCapture(); + } + return rc; +} + +QJsonObject DDAGrabber::discover(const QJsonObject ¶ms) +{ + QJsonObject ret; + if (!d->dxgiAdapter) + { + return ret; + } + + HRESULT hr = S_OK; + + // Enumerate through the outputs. + QJsonArray videoInputs; + for (int i = 0;; ++i) + { + CComPtr output; + hr = d->dxgiAdapter->EnumOutputs(i, &output); + if (!output || !SUCCEEDED(hr)) + { + break; + } + + // Get the output description. + DXGI_OUTPUT_DESC desc; + hr = output->GetDesc(&desc); + if (FAILED(hr)) + { + Error(_log, "Failed to get output description"); + continue; + } + + // Add it to the JSON. + const int width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; + const int height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + videoInputs.append(QJsonObject{ + {"inputIdx", i}, + {"name", QString::fromWCharArray(desc.DeviceName)}, + {"formats", + QJsonArray{ + QJsonObject{ + {"resolutions", + QJsonArray{ + QJsonObject{ + {"width", width}, + {"height", height}, + {"fps", QJsonArray{1, 5, 10, 15, 20, 25, 30, 40, 50, 60, 120, 144}}, + }, + }}, + }, + }}, + }); + } + + ret["video_inputs"] = videoInputs; + if (!videoInputs.isEmpty()) + { + ret["device"] = "dda"; + ret["device_name"] = "DXGI DDA"; + ret["type"] = "screen"; + ret["default"] = QJsonObject{ + {"video_input", + QJsonObject{ + {"inputIdx", 0}, + {"resolution", + QJsonObject{ + {"fps", 60}, + }}, + }}, + }; + } + + return ret; +} diff --git a/libsrc/grabber/dda/DDAWrapper.cpp b/libsrc/grabber/dda/DDAWrapper.cpp new file mode 100644 index 00000000..e0a8147f --- /dev/null +++ b/libsrc/grabber/dda/DDAWrapper.cpp @@ -0,0 +1,20 @@ +#include "grabber/dda/DDAWrapper.h" + +DDAWrapper::DDAWrapper(int updateRate_Hz, int display, int pixelDecimation, int cropLeft, int cropRight, int cropTop, + int cropBottom) + : GrabberWrapper(GRABBERTYPE, &_grabber, updateRate_Hz), _grabber(display, cropLeft, cropRight, cropTop, cropBottom) + +{ + _grabber.setPixelDecimation(pixelDecimation); +} + +DDAWrapper::DDAWrapper(const QJsonDocument &grabberConfig) + : DDAWrapper(GrabberWrapper::DEFAULT_RATE_HZ, 0, GrabberWrapper::DEFAULT_PIXELDECIMATION, 0, 0, 0, 0) +{ + this->handleSettingsUpdate(settings::SYSTEMCAPTURE, grabberConfig); +} + +void DDAWrapper::action() +{ + transferFrame(_grabber); +} diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index d7cc4a8f..a5f48b62 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -173,6 +173,10 @@ QStringList GrabberWrapper::availableGrabbers(GrabberTypeFilter type) #ifdef ENABLE_DX grabbers << "dx"; #endif + + #ifdef ENABLE_DDA + grabbers << "dda"; + #endif } if (type == GrabberTypeFilter::VIDEO || type == GrabberTypeFilter::ALL) diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 8ecbce4c..092260ad 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -118,6 +118,10 @@ if(ENABLE_DX) target_link_libraries(${PROJECT_NAME} directx-grabber) endif (ENABLE_DX) +if(ENABLE_DDA) + target_link_libraries(${PROJECT_NAME} dda-grabber) +endif (ENABLE_DDA) + if(ENABLE_CEC) target_link_libraries(${PROJECT_NAME} cechandler) endif (ENABLE_CEC) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 6c71475d..1b1bb739 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -425,7 +425,7 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs void HyperionDaemon::updateScreenGrabbers(const QJsonDocument& grabberConfig) { -#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT) && !defined(ENABLE_DX) +#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT) && !defined(ENABLE_DX) && !defined(ENABLE_DDA) Info(_log, "No screen capture supported on this platform"); return; #endif @@ -469,6 +469,12 @@ void HyperionDaemon::updateScreenGrabbers(const QJsonDocument& grabberConfig) startGrabber(_screenGrabber, grabberConfig); } #endif +#ifdef ENABLE_DDA + else if (type == "dda") + { + startGrabber(_screenGrabber, grabberConfig); + } +#endif #ifdef ENABLE_FB else if (type == "framebuffer") { diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 70c5b74d..cf64c134 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -64,6 +64,12 @@ typedef QObject DirectXWrapper; #endif +#ifdef ENABLE_DDA + #include +#else + typedef QObject DDAWrapper; +#endif + #include #ifdef ENABLE_AUDIO #include