From bf4dd63531488ea6583ff04bd3d87cada05f7965 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Wed, 29 Jun 2022 16:27:01 +0200 Subject: [PATCH] libtaskmanager: Use KPipeWire --- .kde-ci.yml | 1 + CMakeLists.txt | 1 + libtaskmanager/declarative/CMakeLists.txt | 7 +- libtaskmanager/declarative/pipewirecore.cpp | 100 ----- libtaskmanager/declarative/pipewirecore.h | 37 -- .../declarative/pipewiresourceitem.cpp | 310 --------------- .../declarative/pipewiresourceitem.h | 60 --- .../declarative/pipewiresourcestream.cpp | 373 ------------------ .../declarative/pipewiresourcestream.h | 91 ----- .../declarative/taskmanagerplugin.cpp | 10 +- 10 files changed, 11 insertions(+), 979 deletions(-) delete mode 100644 libtaskmanager/declarative/pipewirecore.cpp delete mode 100644 libtaskmanager/declarative/pipewirecore.h delete mode 100644 libtaskmanager/declarative/pipewiresourceitem.cpp delete mode 100644 libtaskmanager/declarative/pipewiresourceitem.h delete mode 100644 libtaskmanager/declarative/pipewiresourcestream.cpp delete mode 100644 libtaskmanager/declarative/pipewiresourcestream.h diff --git a/.kde-ci.yml b/.kde-ci.yml index 36931c639..6e5651fdf 100644 --- a/.kde-ci.yml +++ b/.kde-ci.yml @@ -56,6 +56,7 @@ Dependencies: 'plasma/libkscreen': '@same' 'plasma/kwin': '@same' 'plasma/libksysguard': '@same' + 'plasma/kpipewire': '@same' 'libraries/plasma-wayland-protocols': '@latest' 'libraries/kuserfeedback': '@stable' diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eae594cc..547af983a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS OPTIONAL_COMPONENTS DocTools) find_package(KDED CONFIG REQUIRED) +find_package(KPipeWire CONFIG REQUIRED) find_package(KF5NetworkManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5NetworkManagerQt PROPERTIES DESCRIPTION "Qt wrapper for NetworkManager API" diff --git a/libtaskmanager/declarative/CMakeLists.txt b/libtaskmanager/declarative/CMakeLists.txt index f4058afe0..797f1fc2c 100644 --- a/libtaskmanager/declarative/CMakeLists.txt +++ b/libtaskmanager/declarative/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(taskmanagerplugin taskmanager) install(TARGETS taskmanagerplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/taskmanager) install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/taskmanager) -if(TARGET PkgConfig::PipeWire) +if(TARGET K::KPipeWire) ecm_qt_declare_logging_category(SRCS HEADER logging.h IDENTIFIER PIPEWIRE_LOGGING @@ -31,12 +31,13 @@ if(TARGET PkgConfig::PipeWire) endif() target_include_directories(taskmanagerplugin PRIVATE ${Libdrm_INCLUDE_DIR}) - target_compile_definitions(taskmanagerplugin PRIVATE -DWITH_PIPEWIRE) - target_sources(taskmanagerplugin PUBLIC pipewirecore.cpp pipewiresourceitem.cpp pipewiresourcestream.cpp screencasting.cpp screencastingrequest.cpp ${SRCS}) + target_compile_definitions(taskmanagerplugin PRIVATE -DWITH_KPIPEWIRE) + target_sources(taskmanagerplugin PUBLIC screencasting.cpp screencastingrequest.cpp ${SRCS}) target_link_libraries(taskmanagerplugin PkgConfig::PipeWire Qt::Qml Qt::GuiPrivate KF5::I18n KF5::WaylandClient + K::KPipeWire Wayland::Client) if (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GLESv2") target_link_libraries(taskmanagerplugin Qt5::Gui_GLESv2) diff --git a/libtaskmanager/declarative/pipewirecore.cpp b/libtaskmanager/declarative/pipewirecore.cpp deleted file mode 100644 index a146718ed..000000000 --- a/libtaskmanager/declarative/pipewirecore.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez - SPDX-FileContributor: Jan Grulich - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#include "pipewirecore.h" -#include "logging.h" -#include -#include -#include - -PipeWireCore::PipeWireCore() -{ - pw_init(nullptr, nullptr); - pwCoreEvents.version = PW_VERSION_CORE_EVENTS; - pwCoreEvents.error = &PipeWireCore::onCoreError; -} - -void PipeWireCore::onCoreError(void *data, uint32_t id, int seq, int res, const char *message) -{ - Q_UNUSED(seq) - - qCWarning(PIPEWIRE_LOGGING) << "PipeWire remote error: " << message; - if (id == PW_ID_CORE && res == -EPIPE) { - PipeWireCore *pw = static_cast(data); - Q_EMIT pw->pipewireFailed(QString::fromUtf8(message)); - } -} - -PipeWireCore::~PipeWireCore() -{ - if (pwMainLoop) { - pw_loop_leave(pwMainLoop); - } - - if (pwCore) { - pw_core_disconnect(pwCore); - } - - if (pwContext) { - pw_context_destroy(pwContext); - } - - if (pwMainLoop) { - pw_loop_destroy(pwMainLoop); - } -} - -bool PipeWireCore::init() -{ - pwMainLoop = pw_loop_new(nullptr); - pw_loop_enter(pwMainLoop); - - QSocketNotifier *notifier = new QSocketNotifier(pw_loop_get_fd(pwMainLoop), QSocketNotifier::Read, this); - connect(notifier, &QSocketNotifier::activated, this, [this] { - int result = pw_loop_iterate(pwMainLoop, 0); - if (result < 0) - qCWarning(PIPEWIRE_LOGGING) << "pipewire_loop_iterate failed: " << spa_strerror(result); - }); - - pwContext = pw_context_new(pwMainLoop, nullptr, 0); - if (!pwContext) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to create PipeWire context"; - m_error = i18n("Failed to create PipeWire context"); - return false; - } - - pwCore = pw_context_connect(pwContext, nullptr, 0); - if (!pwCore) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to connect PipeWire context"; - m_error = i18n("Failed to connect PipeWire context"); - return false; - } - - if (pw_loop_iterate(pwMainLoop, 0) < 0) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to start main PipeWire loop"; - m_error = i18n("Failed to start main PipeWire loop"); - return false; - } - - pw_core_add_listener(pwCore, &coreListener, &pwCoreEvents, this); - return true; -} - -QSharedPointer PipeWireCore::self() -{ - static QWeakPointer global; - QSharedPointer ret; - if (global) { - ret = global.toStrongRef(); - } else { - ret.reset(new PipeWireCore); - if (ret->init()) { - global = ret; - } - } - return ret; -} diff --git a/libtaskmanager/declarative/pipewirecore.h b/libtaskmanager/declarative/pipewirecore.h deleted file mode 100644 index 1d9630c0a..000000000 --- a/libtaskmanager/declarative/pipewirecore.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez - SPDX-FileContributor: Jan Grulich - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#pragma once - -#include -#include - -class PipeWireCore : public QObject -{ - Q_OBJECT -public: - PipeWireCore(); - - static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message); - - ~PipeWireCore(); - - bool init(); - - static QSharedPointer self(); - - struct pw_core *pwCore = nullptr; - struct pw_context *pwContext = nullptr; - struct pw_loop *pwMainLoop = nullptr; - spa_hook coreListener; - QString m_error; - - pw_core_events pwCoreEvents = {}; - -Q_SIGNALS: - void pipewireFailed(const QString &message); -}; diff --git a/libtaskmanager/declarative/pipewiresourceitem.cpp b/libtaskmanager/declarative/pipewiresourceitem.cpp deleted file mode 100644 index a8b26c1a9..000000000 --- a/libtaskmanager/declarative/pipewiresourceitem.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - Render a PipeWire stream into a QtQuick scene as a standard Item - SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL -*/ - -#include "pipewiresourceitem.h" -#include "logging.h" -#include "pipewiresourcestream.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -#endif - -static void pwInit() -{ - pw_init(nullptr, nullptr); -} -Q_COREAPP_STARTUP_FUNCTION(pwInit); - -class DiscardEglPixmapRunnable : public QRunnable -{ -public: - DiscardEglPixmapRunnable(EGLImageKHR image, QOpenGLTexture *texture) - : m_image(image) - , m_texture(texture) - { - } - - void run() override - { - if (m_image != EGL_NO_IMAGE_KHR) { - static auto eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); - eglDestroyImageKHR(eglGetCurrentDisplay(), m_image); - } - - delete m_texture; - } - -private: - const EGLImageKHR m_image; - QOpenGLTexture *m_texture; -}; - -PipeWireSourceItem::PipeWireSourceItem(QQuickItem *parent) - : QQuickItem(parent) -{ - setFlag(ItemHasContents, true); - - connect(this, &QQuickItem::visibleChanged, this, [this]() { - setEnabled(isVisible()); - if (m_stream) - m_stream->setActive(isVisible()); - }); -} - -PipeWireSourceItem::~PipeWireSourceItem() -{ -} - -void PipeWireSourceItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) -{ - switch (change) { - case ItemVisibleHasChanged: - setEnabled(isVisible()); - if (m_stream) - m_stream->setActive(isVisible() && data.boolValue && isComponentComplete()); - break; - case ItemSceneChange: - m_needsRecreateTexture = true; - releaseResources(); - break; - default: - break; - } -} - -void PipeWireSourceItem::releaseResources() -{ - if (window()) { - window()->scheduleRenderJob(new DiscardEglPixmapRunnable(m_image, m_texture.release()), QQuickWindow::NoStage); - m_image = EGL_NO_IMAGE_KHR; - } -} - -void PipeWireSourceItem::setNodeId(uint nodeId) -{ - if (nodeId == m_nodeId) - return; - - m_nodeId = nodeId; - setEnabled(false); - - if (m_nodeId == 0) { - m_stream.reset(nullptr); - m_createNextTexture = [] { - return nullptr; - }; - } else { - m_stream.reset(new PipeWireSourceStream(this)); - m_stream->createStream(m_nodeId); - if (!m_stream->error().isEmpty()) { - m_stream.reset(nullptr); - m_nodeId = 0; - return; - } - m_stream->setActive(isVisible() && isComponentComplete()); - - connect(m_stream.get(), &PipeWireSourceStream::dmabufTextureReceived, this, &PipeWireSourceItem::updateTextureDmaBuf); - connect(m_stream.get(), &PipeWireSourceStream::imageTextureReceived, this, &PipeWireSourceItem::updateTextureImage); - } - - Q_EMIT nodeIdChanged(nodeId); -} - -QSGNode *PipeWireSourceItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) -{ - if (Q_UNLIKELY(!m_createNextTexture)) { - return node; - } - - auto texture = m_createNextTexture(); - if (!texture) { - delete node; - return nullptr; - } - - if (m_needsRecreateTexture) { - delete node; - node = nullptr; - m_needsRecreateTexture = false; - } - - QSGImageNode *textureNode = static_cast(node); - if (!textureNode) { - textureNode = window()->createImageNode(); - textureNode->setOwnsTexture(true); - } - textureNode->setTexture(texture); - - const auto br = boundingRect().toRect(); - QRect rect({0, 0}, texture->textureSize().scaled(br.size(), Qt::KeepAspectRatio)); - rect.moveCenter(br.center()); - textureNode->setRect(rect); - - return textureNode; -} - -QString PipeWireSourceItem::error() const -{ - return m_stream->error(); -} - -static EGLImage createImage(EGLDisplay display, const QVector &planes, uint32_t format, const QSize &size) -{ - const bool hasModifiers = planes[0].modifier != DRM_FORMAT_MOD_INVALID; - - QVector attribs; - attribs << EGL_WIDTH << size.width() << EGL_HEIGHT << size.height() << EGL_LINUX_DRM_FOURCC_EXT << EGLint(format) - - << EGL_DMA_BUF_PLANE0_FD_EXT << planes[0].fd << EGL_DMA_BUF_PLANE0_OFFSET_EXT << EGLint(planes[0].offset) << EGL_DMA_BUF_PLANE0_PITCH_EXT - << EGLint(planes[0].stride); - - if (hasModifiers) { - attribs << EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(planes[0].modifier & 0xffffffff) << EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT - << EGLint(planes[0].modifier >> 32); - } - - if (planes.count() > 1) { - attribs << EGL_DMA_BUF_PLANE1_FD_EXT << planes[1].fd << EGL_DMA_BUF_PLANE1_OFFSET_EXT << EGLint(planes[1].offset) << EGL_DMA_BUF_PLANE1_PITCH_EXT - << EGLint(planes[1].stride); - - if (hasModifiers) { - attribs << EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(planes[1].modifier & 0xffffffff) << EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT - << EGLint(planes[1].modifier >> 32); - } - } - - if (planes.count() > 2) { - attribs << EGL_DMA_BUF_PLANE2_FD_EXT << planes[2].fd << EGL_DMA_BUF_PLANE2_OFFSET_EXT << EGLint(planes[2].offset) << EGL_DMA_BUF_PLANE2_PITCH_EXT - << EGLint(planes[2].stride); - - if (hasModifiers) { - attribs << EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(planes[2].modifier & 0xffffffff) << EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT - << EGLint(planes[2].modifier >> 32); - } - } - - if (planes.count() > 3) { - attribs << EGL_DMA_BUF_PLANE3_FD_EXT << planes[3].fd << EGL_DMA_BUF_PLANE3_OFFSET_EXT << EGLint(planes[3].offset) << EGL_DMA_BUF_PLANE3_PITCH_EXT - << EGLint(planes[3].stride); - - if (hasModifiers) { - attribs << EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(planes[3].modifier & 0xffffffff) << EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT - << EGLint(planes[3].modifier >> 32); - } - } - - attribs << EGL_NONE; - - static auto eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); - Q_ASSERT(eglCreateImageKHR); - - EGLImage ret = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, (EGLClientBuffer) nullptr, attribs.data()); - if (ret == EGL_NO_IMAGE_KHR) { - qCWarning(PIPEWIRE_LOGGING) << "invalid image" << glGetError(); - } - // Q_ASSERT(ret); - return ret; -} - -void PipeWireSourceItem::updateTextureDmaBuf(const QVector &planes, uint32_t format) -{ - static auto s_glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); - if (!s_glEGLImageTargetTexture2DOES) { - qCWarning(PIPEWIRE_LOGGING) << "glEGLImageTargetTexture2DOES is not available" << window(); - return; - } -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - const auto openglContext = window()->openglContext(); -#else - const auto openglContext = static_cast(window()->rendererInterface()->getResource(window(), QSGRendererInterface::OpenGLContextResource)); -#endif - if (!window() || !openglContext || !m_stream) { - qCWarning(PIPEWIRE_LOGGING) << "need a window and a context" << window(); - return; - } - - const EGLDisplay display = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay")); - if (m_image) { - static auto eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); - eglDestroyImageKHR(display, m_image); - } - - const auto size = m_stream->size(); - m_image = createImage(display, planes, format, size); - if (m_image == EGL_NO_IMAGE_KHR) { - QImage img(200, 200, QImage::Format_ARGB32_Premultiplied); - img.fill(Qt::blue); - updateTextureImage(img); - return; - } - - m_createNextTexture = [this, size, format] { - if (!m_texture) { - m_texture.reset(new QOpenGLTexture(QOpenGLTexture::Target2D)); - bool created = m_texture->create(); - Q_ASSERT(created); - } - - m_texture->bind(); - - s_glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)m_image); - - m_texture->setWrapMode(QOpenGLTexture::ClampToEdge); - m_texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - m_texture->release(); - m_texture->setSize(size.width(), size.height()); - - int textureId = m_texture->textureId(); - QQuickWindow::CreateTextureOption textureOption = format == DRM_FORMAT_ARGB8888 ? QQuickWindow::TextureHasAlphaChannel : QQuickWindow::TextureIsOpaque; - setEnabled(true); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - return window()->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, &textureId, 0 /*a vulkan thing?*/, size, textureOption); -#else - return QNativeInterface::QSGOpenGLTexture::fromNative(textureId, window(), size, textureOption); -#endif - ; - }; - if (window()->isVisible()) { - update(); - } -} - -void PipeWireSourceItem::updateTextureImage(const QImage &image) -{ - if (!window()) { - qCWarning(PIPEWIRE_LOGGING) << "pass"; - return; - } - - m_createNextTexture = [this, image] { - setEnabled(true); - return window()->createTextureFromImage(image, QQuickWindow::TextureIsOpaque); - }; - if (window()->isVisible()) - update(); -} - -void PipeWireSourceItem::componentComplete() -{ - if (m_stream) - m_stream->setActive(isVisible()); - QQuickItem::componentComplete(); -} diff --git a/libtaskmanager/declarative/pipewiresourceitem.h b/libtaskmanager/declarative/pipewiresourceitem.h deleted file mode 100644 index a6814276e..000000000 --- a/libtaskmanager/declarative/pipewiresourceitem.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Render a PipeWire stream into a QtQuick scene as a standard Item - SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez - - SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL -*/ - -#pragma once - -#include -#include - -#include -#include -#include -#include - -struct DmaBufPlane; -class PipeWireSourceStream; -class QSGTexture; -class QOpenGLTexture; -typedef void *EGLImage; - -class PipeWireSourceItem : public QQuickItem -{ - Q_OBJECT - /// Specify the pipewire node id that we want to play - Q_PROPERTY(uint nodeId READ nodeId WRITE setNodeId NOTIFY nodeIdChanged) -public: - PipeWireSourceItem(QQuickItem *parent = nullptr); - ~PipeWireSourceItem() override; - - QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override; - Q_SCRIPTABLE QString error() const; - - void setNodeId(uint nodeId); - uint nodeId() const - { - return m_nodeId; - } - - void componentComplete() override; - void releaseResources() override; - -Q_SIGNALS: - void nodeIdChanged(uint nodeId); - -private: - void itemChange(ItemChange change, const ItemChangeData &data) override; - void updateTextureDmaBuf(const QVector &plane, uint32_t format); - void updateTextureImage(const QImage &image); - - uint m_nodeId = 0; - std::function m_createNextTexture; - std::unique_ptr m_stream; - std::unique_ptr m_texture; - - EGLImage m_image = nullptr; - bool m_needsRecreateTexture = false; -}; diff --git a/libtaskmanager/declarative/pipewiresourcestream.cpp b/libtaskmanager/declarative/pipewiresourcestream.cpp deleted file mode 100644 index f5b799f47..000000000 --- a/libtaskmanager/declarative/pipewiresourcestream.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - SPDX-FileCopyrightText: 2018-2020 Red Hat Inc - SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez - SPDX-FileContributor: Jan Grulich - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#include "pipewiresourcestream.h" -#include "logging.h" -#include "pipewirecore.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -#endif -#undef Status - -#if !PW_CHECK_VERSION(0, 3, 29) -#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) -#endif -#if !PW_CHECK_VERSION(0, 3, 33) -#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) -#endif - -static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) -{ - switch (spa_format) { - case SPA_VIDEO_FORMAT_RGBA: - return DRM_FORMAT_ABGR8888; - case SPA_VIDEO_FORMAT_RGBx: - return DRM_FORMAT_XBGR8888; - case SPA_VIDEO_FORMAT_BGRA: - return DRM_FORMAT_ARGB8888; - case SPA_VIDEO_FORMAT_BGRx: - return DRM_FORMAT_XRGB8888; - default: - return DRM_FORMAT_INVALID; - } -} - -static std::vector queryDmaBufModifiers(EGLDisplay display, uint32_t format) -{ - static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT"); - static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress("eglQueryDmaBufFormatsEXT"); - if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT) { - return {}; - } - - uint32_t drm_format = SpaPixelFormatToDrmFormat(format); - if (drm_format == DRM_FORMAT_INVALID) { - qCDebug(PIPEWIRE_LOGGING) << "Failed to find matching DRM format." << format; - return {}; - } - - EGLint count = 0; - EGLBoolean success = eglQueryDmaBufFormatsEXT(display, 0, nullptr, &count); - - if (!success || count == 0) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF format count."; - return {}; - } - - std::vector formats(count); - if (!eglQueryDmaBufFormatsEXT(display, count, reinterpret_cast(formats.data()), &count)) { - if (!success) - qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF formats."; - return {}; - } - - if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) { - qCDebug(PIPEWIRE_LOGGING) << "Format " << drm_format << " not supported for modifiers."; - return {DRM_FORMAT_MOD_INVALID}; - } - - success = eglQueryDmaBufModifiersEXT(display, drm_format, 0, nullptr, nullptr, &count); - if (!success) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF modifier count."; - return {}; - } - - std::vector modifiers(count); - if (count > 0) { - if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, modifiers.data(), nullptr, &count)) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to query DMA-BUF modifiers."; - } - } - - // Support modifier-less buffers - modifiers.push_back(DRM_FORMAT_MOD_INVALID); - return modifiers; -} - -void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message) -{ - PipeWireSourceStream *pw = static_cast(data); - qCDebug(PIPEWIRE_LOGGING) << "state changed" << pw_stream_state_as_string(old) << "->" << pw_stream_state_as_string(state) << error_message; - - switch (state) { - case PW_STREAM_STATE_ERROR: - qCWarning(PIPEWIRE_LOGGING) << "Stream error: " << error_message; - break; - case PW_STREAM_STATE_PAUSED: - Q_EMIT pw->streamReady(); - break; - case PW_STREAM_STATE_STREAMING: - Q_EMIT pw->startStreaming(); - break; - case PW_STREAM_STATE_CONNECTING: - break; - case PW_STREAM_STATE_UNCONNECTED: - if (!pw->m_stopped) { - Q_EMIT pw->stopStreaming(); - } - break; - } -} - -static spa_pod *buildFormat(spa_pod_builder *builder, spa_video_format format, const std::vector &modifiers = {}) -{ - spa_pod_frame f[2]; - const spa_rectangle pw_min_screen_bounds{1, 1}; - const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX}; - - spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); - spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); - spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - - if (modifiers.size()) { - auto pw_version = QVersionNumber::fromString(pw_get_library_version()); - - // SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33 - if (pw_version >= QVersionNumber(0, 3, 33)) { - spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - } else { - spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); - } - spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0); - // mofifiers from the array - for (auto it = modifiers.begin(); it != modifiers.end(); it++) { - spa_pod_builder_long(builder, *it); - if (it == modifiers.begin()) { - spa_pod_builder_long(builder, *it); - } - } - spa_pod_builder_pop(builder, &f[1]); - } - - spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), 0); - - return static_cast(spa_pod_builder_pop(builder, &f[0])); -} - -void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format) -{ - if (!format || id != SPA_PARAM_Format) { - return; - } - - PipeWireSourceStream *pw = static_cast(data); - spa_format_video_raw_parse(format, &pw->videoFormat); - - const int32_t width = pw->videoFormat.size.width; - const int32_t height = pw->videoFormat.size.height; - const int bpp = pw->videoFormat.format == SPA_VIDEO_FORMAT_RGB || pw->videoFormat.format == SPA_VIDEO_FORMAT_BGR ? 3 : 4; - const quint32 stride = SPA_ROUND_UP_N(width * bpp, 4); - qCDebug(PIPEWIRE_LOGGING) << "Stream format changed"; - const int32_t size = height * stride; - - uint8_t paramsBuffer[1024]; - spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer)); - - const auto bufferTypes = pw->m_allowDmaBuf && spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier) - ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) - : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); - - const spa_pod *param = (spa_pod *)spa_pod_builder_add_object(&pod_builder, - SPA_TYPE_OBJECT_ParamBuffers, - SPA_PARAM_Buffers, - SPA_PARAM_BUFFERS_buffers, - SPA_POD_CHOICE_RANGE_Int(16, 2, 16), - SPA_PARAM_BUFFERS_blocks, - SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, - SPA_POD_Int(size), - SPA_PARAM_BUFFERS_stride, - SPA_POD_CHOICE_RANGE_Int(stride, stride, INT32_MAX), - SPA_PARAM_BUFFERS_align, - SPA_POD_Int(16), - SPA_PARAM_BUFFERS_dataType, - SPA_POD_CHOICE_FLAGS_Int(bufferTypes)); - pw_stream_update_params(pw->pwStream, ¶m, 1); -} - -static void onProcess(void *data) -{ - PipeWireSourceStream *stream = static_cast(data); - stream->process(); -} - -PipeWireSourceStream::PipeWireSourceStream(QObject *parent) - : QObject(parent) -{ - pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; - pwStreamEvents.process = &onProcess; - pwStreamEvents.state_changed = &PipeWireSourceStream::onStreamStateChanged; - pwStreamEvents.param_changed = &PipeWireSourceStream::onStreamParamChanged; -} - -PipeWireSourceStream::~PipeWireSourceStream() -{ - m_stopped = true; - if (pwStream) { - pw_stream_destroy(pwStream); - } -} - -uint PipeWireSourceStream::framerate() -{ - if (pwStream) { - return videoFormat.max_framerate.num / videoFormat.max_framerate.denom; - } - - return 0; -} - -uint PipeWireSourceStream::nodeId() -{ - return pwNodeId; -} - -bool PipeWireSourceStream::createStream(uint nodeid) -{ - pwCore = PipeWireCore::self(); - if (!pwCore->m_error.isEmpty()) { - m_error = pwCore->m_error; - return false; - } - - connect(pwCore.data(), &PipeWireCore::pipewireFailed, this, &PipeWireSourceStream::coreFailed); - - pwStream = pw_stream_new(pwCore->pwCore, "plasma-screencast", nullptr); - pwNodeId = nodeid; - pw_stream_add_listener(pwStream, &streamListener, &pwStreamEvents, this); - - uint8_t buffer[4096]; - spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - - const QVector formats = - {SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGB, SPA_VIDEO_FORMAT_BGR}; - QVector params; - params.reserve(formats.size() * 2); - const EGLDisplay display = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay")); - for (spa_video_format format : formats) { - if (m_allowDmaBuf) { - if (auto modifiers = queryDmaBufModifiers(display, format); modifiers.size() > 0) { - params += buildFormat(&podBuilder, format, modifiers); - } - } - - params += buildFormat(&podBuilder, format, {}); - } - - pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT); - if (pw_stream_connect(pwStream, PW_DIRECTION_INPUT, pwNodeId, s, params.data(), params.size()) != 0) { - qCWarning(PIPEWIRE_LOGGING) << "Could not connect to stream"; - pw_stream_destroy(pwStream); - return false; - } - return true; -} - -void PipeWireSourceStream::handleFrame(struct pw_buffer *buffer) -{ - spa_buffer *spaBuffer = buffer->buffer; - - if (spaBuffer->datas->chunk->size == 0) { - return; - } - - if (spaBuffer->datas->type == SPA_DATA_MemFd) { - uint8_t *map = - static_cast(mmap(nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset, PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0)); - - if (map == MAP_FAILED) { - qCWarning(PIPEWIRE_LOGGING) << "Failed to mmap the memory: " << strerror(errno); - return; - } - const QImage::Format format = spaBuffer->datas->chunk->stride / videoFormat.size.width == 3 ? QImage::Format_RGB888 : QImage::Format_ARGB32; - - QImage img(map, videoFormat.size.width, videoFormat.size.height, spaBuffer->datas->chunk->stride, format); - Q_EMIT imageTextureReceived(img.copy()); - - munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset); - } else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) { - QVector planes; - planes.reserve(spaBuffer->n_datas); - for (uint i = 0; i < spaBuffer->n_datas; ++i) { - const auto &data = spaBuffer->datas[i]; - - DmaBufPlane plane; - plane.fd = data.fd; - plane.stride = data.chunk->stride; - plane.offset = data.chunk->offset; - plane.modifier = DRM_FORMAT_MOD_INVALID; - planes += plane; - } - Q_EMIT dmabufTextureReceived(planes, DRM_FORMAT_ARGB8888); - } else if (spaBuffer->datas->type == SPA_DATA_MemPtr) { - QImage img(static_cast(spaBuffer->datas->data), - videoFormat.size.width, - videoFormat.size.height, - spaBuffer->datas->chunk->stride, - QImage::Format_ARGB32); - Q_EMIT imageTextureReceived(img); - } else { - qCWarning(PIPEWIRE_LOGGING) << "unsupported buffer type" << spaBuffer->datas->type; - QImage errorImage(200, 200, QImage::Format_ARGB32_Premultiplied); - errorImage.fill(Qt::red); - Q_EMIT imageTextureReceived(errorImage); - } -} - -void PipeWireSourceStream::coreFailed(const QString &errorMessage) -{ - m_error = errorMessage; - Q_EMIT stopStreaming(); -} - -void PipeWireSourceStream::process() -{ - pw_buffer *buf = pw_stream_dequeue_buffer(pwStream); - if (!buf) { - return; - } - - handleFrame(buf); - - pw_stream_queue_buffer(pwStream, buf); -} - -void PipeWireSourceStream::stop() -{ - if (!m_stopped) - pw_stream_set_active(pwStream, false); - m_stopped = true; - delete this; -} - -void PipeWireSourceStream::setActive(bool active) -{ - Q_ASSERT(pwStream); - pw_stream_set_active(pwStream, active); -} diff --git a/libtaskmanager/declarative/pipewiresourcestream.h b/libtaskmanager/declarative/pipewiresourcestream.h deleted file mode 100644 index 93133c0bb..000000000 --- a/libtaskmanager/declarative/pipewiresourcestream.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - SPDX-FileCopyrightText: 2018-2020 Red Hat Inc - SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez - SPDX-FileContributor: Jan Grulich - - SPDX-License-Identifier: LGPL-2.0-or-later -*/ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -#undef Status - -namespace KWin -{ -class AbstractEglBackend; -class GLTexture; -} -class PipeWireCore; - -typedef void *EGLDisplay; - -struct DmaBufPlane { - int fd; /// The dmabuf file descriptor - uint32_t offset; /// The offset from the start of buffer - uint32_t stride; /// The distance from the start of a row to the next row in bytes - uint64_t modifier = 0; /// The layout modifier -}; - -class PipeWireSourceStream : public QObject -{ - Q_OBJECT -public: - explicit PipeWireSourceStream(QObject *parent); - ~PipeWireSourceStream(); - - static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format); - static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message); - - uint framerate(); - uint nodeId(); - QString error() const - { - return m_error; - } - - QSize size() const - { - return QSize(videoFormat.size.width, videoFormat.size.height); - } - bool createStream(uint nodeid); - void stop(); - void setActive(bool active); - - void handleFrame(struct pw_buffer *buffer); - void process(); - - bool setAllowDmaBuf(bool allowed); - -Q_SIGNALS: - void streamReady(); - void startStreaming(); - void stopStreaming(); - void dmabufTextureReceived(const QVector &planes, uint32_t format); - void imageTextureReceived(const QImage &image); - -private: - void coreFailed(const QString &errorMessage); - - QSharedPointer pwCore; - pw_stream *pwStream = nullptr; - spa_hook streamListener; - pw_stream_events pwStreamEvents = {}; - - uint32_t pwNodeId = 0; - - bool m_stopped = false; - - spa_video_info_raw videoFormat; - QString m_error; - bool m_allowDmaBuf = true; -}; diff --git a/libtaskmanager/declarative/taskmanagerplugin.cpp b/libtaskmanager/declarative/taskmanagerplugin.cpp index f5ea670af..2262aa0e2 100644 --- a/libtaskmanager/declarative/taskmanagerplugin.cpp +++ b/libtaskmanager/declarative/taskmanagerplugin.cpp @@ -11,11 +11,11 @@ #include "tasksmodel.h" #include "virtualdesktopinfo.h" -#ifdef WITH_PIPEWIRE -#include "pipewiresourceitem.h" +#ifdef WITH_KPIPEWIRE +#include +#endif #include "screencasting.h" #include "screencastingrequest.h" -#endif namespace TaskManager { @@ -33,11 +33,11 @@ void TaskManagerPlugin::registerTypes(const char *uri) qmlRegisterType(uri, 0, 1, "TasksModel"); qmlRegisterType(uri, 0, 1, "ActivityInfo"); qmlRegisterType(uri, 0, 1, "VirtualDesktopInfo"); -#ifdef WITH_PIPEWIRE +#ifdef WITH_KPIPEWIRE qmlRegisterType(uri, 0, 1, "PipeWireSourceItem"); +#endif qmlRegisterType(uri, 0, 1, "ScreencastingRequest"); qmlRegisterUncreatableType(uri, 0, 1, "Screencasting", "Use ScreencastingItem"); -#endif } }