From 4f2861415a472a498cab3a3dc4c040a041706c73 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Fri, 21 May 2021 14:11:18 +0200 Subject: [PATCH] Revert "Improve PipeWire code" This reverts commit eb1dc503bdafd26ebaeeeb348203ce98fcf14b9b. Revert "Add dma-buf defines to build dma-buf support everywhere" This reverts commit 8f0de62401569bc8cb96158feb9b0c20553cd540. Revert "Drop support for PipeWire 0.2" This reverts commit 028ac099eac17ad08c84a778b0324ae98aeb6f3f. These were accidentally pushed without review. They were meant to be pushed to my fork instead. --- cmake/modules/FindPipeWire.cmake | 19 +- framebuffers/pipewire/CMakeLists.txt | 6 + framebuffers/pipewire/pw_framebuffer.cpp | 404 ++++++++++++++++------- 3 files changed, 294 insertions(+), 135 deletions(-) diff --git a/cmake/modules/FindPipeWire.cmake b/cmake/modules/FindPipeWire.cmake index 2a5e154e..9717ac09 100644 --- a/cmake/modules/FindPipeWire.cmake +++ b/cmake/modules/FindPipeWire.cmake @@ -62,8 +62,8 @@ # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig QUIET) -pkg_search_module(PKG_PipeWire QUIET libpipewire-0.3) -pkg_search_module(PKG_Spa QUIET libspa-0.2) +pkg_search_module(PKG_PipeWire QUIET libpipewire-0.3 libpipewire-0.2) +pkg_search_module(PKG_Spa QUIET libspa-0.2 libspa-0.1) set(PipeWire_DEFINITIONS "${PKG_PipeWire_CFLAGS}" "${PKG_Spa_CFLAGS}") set(PipeWire_VERSION "${PKG_PipeWire_VERSION}") @@ -76,13 +76,6 @@ find_path(PipeWire_INCLUDE_DIRS ${PKG_PipeWire_INCLUDE_DIRS}/pipewire-0.3 ) -find_library(PipeWire_LIBRARIES - NAMES - pipewire-0.3 - HINTS - ${PKG_PipeWire_LIBRARY_DIRS} -) - find_path(Spa_INCLUDE_DIRS NAMES spa/param/props.h @@ -91,6 +84,14 @@ find_path(Spa_INCLUDE_DIRS ${PKG_Spa_INCLUDE_DIRS}/spa-0.2 ) +find_library(PipeWire_LIBRARIES + NAMES + pipewire-0.3 + pipewire-0.2 + HINTS + ${PKG_PipeWire_LIBRARY_DIRS} +) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PipeWire FOUND_VAR diff --git a/framebuffers/pipewire/CMakeLists.txt b/framebuffers/pipewire/CMakeLists.txt index d9755b50..cd7d9ce4 100644 --- a/framebuffers/pipewire/CMakeLists.txt +++ b/framebuffers/pipewire/CMakeLists.txt @@ -33,6 +33,12 @@ add_library(krfb_framebuffer_pw ${krfb_framebuffer_pw_SRCS} ) + +check_include_file("linux/dma-buf.h" HAVE_LINUX_DMABUF_H) +if (HAVE_LINUX_DMABUF_H) + target_compile_definitions(krfb_framebuffer_pw PRIVATE -DHAVE_LINUX_DMABUF_H) +endif () + target_link_libraries (krfb_framebuffer_pw Qt5::Core Qt5::Gui diff --git a/framebuffers/pipewire/pw_framebuffer.cpp b/framebuffers/pipewire/pw_framebuffer.cpp index 62058e0f..5be74cc5 100644 --- a/framebuffers/pipewire/pw_framebuffer.cpp +++ b/framebuffers/pipewire/pw_framebuffer.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - Copyright (C) 2018-2021 Jan Grulich Copyright (C) 2018 Oleg Chernovskiy + Copyright (C) 2018 Jan Grulich This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -23,12 +23,19 @@ #endif // pipewire +#include + +#if PW_CHECK_VERSION(0, 2, 90) +#include +#ifdef HAVE_LINUX_DMABUF_H +#include +#endif #include +#endif #include #include #include -#include #include @@ -39,18 +46,6 @@ #include "xdp_dbus_remotedesktop_interface.h" #include "krfb_fb_pipewire_debug.h" -// static -struct dma_buf_sync { - uint64_t flags; -}; -#define DMA_BUF_SYNC_READ (1 << 0) -#define DMA_BUF_SYNC_START (0 << 2) -#define DMA_BUF_SYNC_END (1 << 2) -#define DMA_BUF_BASE 'b' -#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) - - -static const int BYTES_PER_PIXEL = 4; static const uint MIN_SUPPORTED_XDP_KDE_SC_VERSION = 1; Q_DECLARE_METATYPE(PWFrameBuffer::Stream); @@ -76,6 +71,19 @@ const QDBusArgument &operator >> (const QDBusArgument &arg, PWFrameBuffer::Strea return arg; } +#if !PW_CHECK_VERSION(0, 2, 90) +/** + * @brief The PwType class - helper class to contain pointers to raw C pipewire media mappings + */ +class PwType { +public: + spa_type_media_type media_type; + spa_type_media_subtype media_subtype; + spa_type_format_video format_video; + spa_type_video_format video_format; +}; +#endif + /** * @brief The PWFrameBuffer::Private class - private counterpart of PWFramebuffer class. This is the entity where * whole logic resides, for more info search for "d-pointer pattern" information. @@ -88,13 +96,21 @@ public: private: friend class PWFrameBuffer; +#if PW_CHECK_VERSION(0, 2, 90) static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message); static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format); +#else + static void onStateChanged(void *data, pw_remote_state old, pw_remote_state state, const char *error); + static void onStreamFormatChanged(void *data, const struct spa_pod *format); +#endif static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message); static void onStreamProcess(void *data); void initDbus(); void initPw(); +#if !PW_CHECK_VERSION(0, 2, 90) + void initializePwTypes(); +#endif // dbus handling void handleSessionCreated(quint32 &code, QVariantMap &results); @@ -110,6 +126,7 @@ private: PWFrameBuffer *q; // pipewire stuff +#if PW_CHECK_VERSION(0, 2, 90) struct pw_context *pwContext = nullptr; struct pw_core *pwCore = nullptr; struct pw_stream *pwStream = nullptr; @@ -123,6 +140,22 @@ private: // event handlers pw_core_events pwCoreEvents = {}; pw_stream_events pwStreamEvents = {}; +#else + pw_core *pwCore = nullptr; + pw_loop *pwLoop = nullptr; + pw_thread_loop *pwMainLoop = nullptr; + pw_stream *pwStream = nullptr; + pw_remote *pwRemote = nullptr; + pw_type *pwCoreType = nullptr; + PwType *pwType = nullptr; + + spa_hook remoteListener = {}; + spa_hook streamListener = {}; + + // event handlers + pw_remote_events pwRemoteEvents = {}; + pw_stream_events pwStreamEvents = {}; +#endif uint pwStreamNodeId = 0; @@ -140,8 +173,10 @@ private: QDBusUnixFileDescriptor pipewireFd; // screen geometry holder - QSize streamSize; - QSize videoSize; + struct { + quint32 width; + quint32 height; + } screenGeometry = {}; // Allowed devices uint devices = 0; @@ -152,6 +187,7 @@ private: PWFrameBuffer::Private::Private(PWFrameBuffer *q) : q(q) { +#if PW_CHECK_VERSION(0, 2, 90) pwCoreEvents.version = PW_VERSION_CORE_EVENTS; pwCoreEvents.error = &onCoreError; @@ -159,6 +195,16 @@ PWFrameBuffer::Private::Private(PWFrameBuffer *q) : q(q) pwStreamEvents.state_changed = &onStreamStateChanged; pwStreamEvents.param_changed = &onStreamParamChanged; pwStreamEvents.process = &onStreamProcess; +#else + // initialize event handlers, remote end and stream-related + pwRemoteEvents.version = PW_VERSION_REMOTE_EVENTS; + pwRemoteEvents.state_changed = &onStateChanged; + + pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; + pwStreamEvents.state_changed = &onStreamStateChanged; + pwStreamEvents.format_changed = &onStreamFormatChanged; + pwStreamEvents.process = &onStreamProcess; +#endif } /** @@ -385,10 +431,25 @@ void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 &code, QVariantM return; } + QSize streamResolution = qdbus_cast(streams.first().map.value(QStringLiteral("size"))); + screenGeometry.width = streamResolution.width(); + screenGeometry.height = streamResolution.height(); + devices = results.value(QStringLiteral("types")).toUInt(); pwStreamNodeId = streams.first().nodeId; + // Reallocate our buffer with actual needed size + q->fb = static_cast(malloc(screenGeometry.width * screenGeometry.height * 4)); + + if (!q->fb) { + qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer"; + isValid = false; + return; + } + + Q_EMIT q->frameBufferChanged(); + initPw(); } @@ -402,9 +463,8 @@ void PWFrameBuffer::Private::initPw() { // init pipewire (required) pw_init(nullptr, nullptr); // args are not used anyways +#if PW_CHECK_VERSION(0, 2, 90) pwMainLoop = pw_thread_loop_new("pipewire-main-loop", nullptr); - pw_thread_loop_lock(pwMainLoop); - pwContext = pw_context_new(pw_thread_loop_get_loop(pwMainLoop), nullptr, 0); if (!pwContext) { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire context"; @@ -424,15 +484,49 @@ void PWFrameBuffer::Private::initPw() { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire stream"; return; } +#else + // initialize our source + pwLoop = pw_loop_new(nullptr); + pwMainLoop = pw_thread_loop_new(pwLoop, "pipewire-main-loop"); + // create PipeWire core object (required) + pwCore = pw_core_new(pwLoop, nullptr); + pwCoreType = pw_core_get_type(pwCore); + + initializePwTypes(); + + // pw_remote should be initialized before type maps or connection error will happen + pwRemote = pw_remote_new(pwCore, nullptr, 0); + // init PipeWire remote, add listener to handle events + pw_remote_add_listener(pwRemote, &remoteListener, &pwRemoteEvents, this); + pw_remote_connect_fd(pwRemote, pipewireFd.fileDescriptor()); +#endif if (pw_thread_loop_start(pwMainLoop) < 0) { qCWarning(KRFB_FB_PIPEWIRE) << "Failed to start main PipeWire loop"; isValid = false; } - - pw_thread_loop_unlock(pwMainLoop); } +#if !PW_CHECK_VERSION(0, 2, 90) +/** + * @brief PWFrameBuffer::Private::initializePwTypes - helper method to initialize and map all needed + * Pipewire types from core to type structure. + */ +void PWFrameBuffer::Private::initializePwTypes() +{ + // raw C-like PipeWire type map + spa_type_map *map = pwCoreType->map; + pwType = new PwType(); + + spa_type_media_type_map(map, &pwType->media_type); + spa_type_media_subtype_map(map, &pwType->media_subtype); + spa_type_format_video_map(map, &pwType->format_video); + spa_type_video_format_map(map, &pwType->video_format); +} +#endif + + +#if PW_CHECK_VERSION(0, 2, 90) void PWFrameBuffer::Private::onCoreError(void *data, uint32_t id, int seq, int res, const char *message) { Q_UNUSED(data); @@ -442,6 +536,32 @@ void PWFrameBuffer::Private::onCoreError(void *data, uint32_t id, int seq, int r qInfo() << "core error: " << message; } +#else +/** + * @brief PWFrameBuffer::Private::onStateChanged - global state tracking for pipewire connection + * @param data pointer that you have set in pw_remote_add_listener call's last argument + * @param state new state that connection has changed to + * @param error optional error message, is set to non-null if state is error + */ +void PWFrameBuffer::Private::onStateChanged(void *data, pw_remote_state /*old*/, pw_remote_state state, const char *error) +{ + qInfo() << "remote state: " << pw_remote_state_as_string(state); + + auto d = static_cast(data); + + switch (state) { + case PW_REMOTE_STATE_ERROR: + qCWarning(KRFB_FB_PIPEWIRE) << "remote error: " << error; + break; + case PW_REMOTE_STATE_CONNECTED: + d->pwStream = d->createReceivingStream(); + break; + default: + qInfo() << "remote state: " << pw_remote_state_as_string(state); + break; + } +} +#endif /** * @brief PWFrameBuffer::Private::onStreamStateChanged - called whenever stream state changes on pipewire server @@ -451,20 +571,35 @@ void PWFrameBuffer::Private::onCoreError(void *data, uint32_t id, int seq, int r */ void PWFrameBuffer::Private::onStreamStateChanged(void *data, pw_stream_state /*old*/, pw_stream_state state, const char *error_message) { - Q_UNUSED(data); - qInfo() << "Stream state changed: " << pw_stream_state_as_string(state); + auto *d = static_cast(data); + +#if PW_CHECK_VERSION(0, 2, 90) switch (state) { case PW_STREAM_STATE_ERROR: qCWarning(KRFB_FB_PIPEWIRE) << "pipewire stream error: " << error_message; break; case PW_STREAM_STATE_PAUSED: + pw_stream_set_active(d->pwStream, true); + break; case PW_STREAM_STATE_STREAMING: case PW_STREAM_STATE_UNCONNECTED: case PW_STREAM_STATE_CONNECTING: break; } +#else + switch (state) { + case PW_STREAM_STATE_ERROR: + qCWarning(KRFB_FB_PIPEWIRE) << "pipewire stream error: " << error_message; + break; + case PW_STREAM_STATE_CONFIGURE: + pw_stream_set_active(d->pwStream, true); + break; + default: + break; + } +#endif } /** @@ -473,29 +608,44 @@ void PWFrameBuffer::Private::onStreamStateChanged(void *data, pw_stream_state /* * @param data pointer that you have set in pw_stream_add_listener call's last argument * @param format format that's being proposed */ +#if PW_CHECK_VERSION(0, 2, 90) void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format) +#else +void PWFrameBuffer::Private::onStreamFormatChanged(void *data, const struct spa_pod *format) +#endif { qInfo() << "Stream format changed"; auto *d = static_cast(data); + const int bpp = 4; + +#if PW_CHECK_VERSION(0, 2, 90) if (!format || id != SPA_PARAM_Format) { +#else + if (!format) { + pw_stream_finish_format(d->pwStream, 0, nullptr, 0); +#endif return; } d->videoFormat = new spa_video_info_raw(); +#if PW_CHECK_VERSION(0, 2, 90) spa_format_video_raw_parse(format, d->videoFormat); +#else + spa_format_video_raw_parse(format, d->videoFormat, &d->pwType->format_video); +#endif auto width = d->videoFormat->size.width; auto height = d->videoFormat->size.height; - auto stride = SPA_ROUND_UP_N(width * BYTES_PER_PIXEL, 4); + auto stride = SPA_ROUND_UP_N(width * bpp, 4); auto size = height * stride; - d->streamSize = QSize(width, height); uint8_t buffer[1024]; auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); // setup buffers and meta header for new format - const struct spa_pod *params[3]; + const struct spa_pod *params[2]; +#if PW_CHECK_VERSION(0, 2, 90) params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), @@ -507,11 +657,20 @@ void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))); - params[2] = reinterpret_cast(spa_pod_builder_add_object(&builder, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, - SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size, - SPA_POD_Int(sizeof(struct spa_meta_region)))); - pw_stream_update_params(d->pwStream, params, 3); + pw_stream_update_params(d->pwStream, params, 2); +#else + params[0] = reinterpret_cast(spa_pod_builder_object(&builder, + d->pwCoreType->param.idBuffers, d->pwCoreType->param_buffers.Buffers, + ":", d->pwCoreType->param_buffers.size, "i", size, + ":", d->pwCoreType->param_buffers.stride, "i", stride, + ":", d->pwCoreType->param_buffers.buffers, "iru", 8, SPA_POD_PROP_MIN_MAX(1, 32), + ":", d->pwCoreType->param_buffers.align, "i", 16)); + params[1] = reinterpret_cast(spa_pod_builder_object(&builder, + d->pwCoreType->param.idMeta, d->pwCoreType->param_meta.Meta, + ":", d->pwCoreType->param_meta.type, "I", d->pwCoreType->meta.Header, + ":", d->pwCoreType->param_meta.size, "i", sizeof(struct spa_meta_header))); + pw_stream_finish_format(d->pwStream, 0, params, 2); +#endif } /** @@ -523,28 +682,17 @@ void PWFrameBuffer::Private::onStreamProcess(void *data) { auto *d = static_cast(data); - pw_buffer* next_buffer; - pw_buffer* buffer = nullptr; - - next_buffer = pw_stream_dequeue_buffer(d->pwStream); - while (next_buffer) { - buffer = next_buffer; - next_buffer = pw_stream_dequeue_buffer(d->pwStream); - - if (next_buffer) { - pw_stream_queue_buffer(d->pwStream, buffer); - } - } - - if (!buffer) { + pw_buffer *buf; + if (!(buf = pw_stream_dequeue_buffer(d->pwStream))) { return; } - d->handleFrame(buffer); + d->handleFrame(buf); - pw_stream_queue_buffer(d->pwStream, buffer); + pw_stream_queue_buffer(d->pwStream, buf); } +#if PW_CHECK_VERSION(0, 2, 90) && defined(HAVE_LINUX_DMABUF_H) static void syncDmaBuf(int fd, uint64_t start_or_end) { struct dma_buf_sync sync = { 0 }; @@ -563,18 +711,25 @@ static void syncDmaBuf(int fd, uint64_t start_or_end) } } } +#endif void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) { auto *spaBuffer = pwBuffer->buffer; - uint8_t *src = nullptr; + void *src = spaBuffer->datas[0].data; - if (spaBuffer->datas[0].chunk->size == 0) { +#if PW_CHECK_VERSION(0, 2, 90) + if (!src && spaBuffer->datas->type != SPA_DATA_DmaBuf) { qCDebug(KRFB_FB_PIPEWIRE) << "discarding null buffer"; return; } +#endif + + const quint32 maxSize = spaBuffer->datas[0].maxsize; std::function cleanup; +#if PW_CHECK_VERSION(0, 2, 90) +#ifdef HAVE_LINUX_DMABUF_H if (spaBuffer->datas->type == SPA_DATA_DmaBuf) { const int fd = spaBuffer->datas[0].fd; auto map = mmap( @@ -592,7 +747,9 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) syncDmaBuf(fd, DMA_BUF_SYNC_END); munmap(map, spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset); }; - } else if (spaBuffer->datas->type == SPA_DATA_MemFd) { + } else +#endif + 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)); @@ -606,80 +763,24 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) cleanup = [map, spaBuffer] { munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset); }; - } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) { - src = static_cast(spaBuffer->datas[0].data); } +#endif - struct spa_meta_region* videoMetadata = - static_cast(spa_buffer_find_meta_data( - spaBuffer, SPA_META_VideoCrop, sizeof(*videoMetadata))); - - if (videoMetadata && (videoMetadata->region.size.width > static_cast(q->width()) || - videoMetadata->region.size.height > static_cast(q->height()))) { - qCWarning(KRFB_FB_PIPEWIRE) << "Stream metadata sizes are wrong!"; + const qint32 srcStride = spaBuffer->datas[0].chunk->stride; + if (srcStride != q->paddedWidth()) { + qCWarning(KRFB_FB_PIPEWIRE) << "Got buffer with stride different from screen stride" << srcStride << "!=" << q->paddedWidth(); return; } - // Use video metadata when video size from metadata is set and smaller than - // video stream size, so we need to adjust it. - bool videoFullWidth = true; - bool videoFullHeight = true; - if (videoMetadata && videoMetadata->region.size.width != 0 && - videoMetadata->region.size.height != 0) { - if (videoMetadata->region.size.width < static_cast(q->width())) { - videoFullWidth = false; - } else if (videoMetadata->region.size.height < static_cast(q->height())) { - videoFullHeight = false; - } - } - - QSize prevVideoSize = videoSize; - if (!videoFullHeight || !videoFullWidth) { - videoSize = QSize(videoMetadata->region.size.width, videoMetadata->region.size.height); - } else { - videoSize = streamSize; - } - - if (!q->fb || videoSize != prevVideoSize) { - if (q->fb) { - free(q->fb); - } - q->fb = static_cast(malloc(videoSize.width() * videoSize.height() * BYTES_PER_PIXEL)); - - if (!q->fb) { - qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer"; - isValid = false; - return; - } - - Q_EMIT q->frameBufferChanged(); - } - - const qint32 dstStride = videoSize.width() * BYTES_PER_PIXEL; - const qint32 srcStride = spaBuffer->datas[0].chunk->stride; - - if (!videoFullHeight && (videoMetadata->region.position.y + videoSize.height() <= streamSize.height())) { - src += srcStride * videoMetadata->region.position.y; - } - - const int xOffset = !videoFullWidth && (videoMetadata->region.position.x + videoSize.width() <= streamSize.width()) - ? videoMetadata->region.position.x * BYTES_PER_PIXEL : 0; - - char *dst = q->fb; - for (int i = 0; i < videoSize.height(); ++i) { - // Adjust source content based on crop video position if needed - src += xOffset; - std::memcpy(dst, src, dstStride); - src += srcStride - xOffset; - dst += dstStride; - } - + q->tiles.append(QRect(0, 0, q->width(), q->height())); + std::memcpy(q->fb, src, maxSize); cleanup(); +#if PW_CHECK_VERSION(0, 2, 90) if (videoFormat->format == SPA_VIDEO_FORMAT_BGRA || videoFormat->format == SPA_VIDEO_FORMAT_BGRx) { - for (uint y = 0; y < videoSize.height(); y++) { - for (uint x = 0; x < videoSize.width(); x++) { - uint offset = y * dstStride + x * 4; + for (uint y = 0; y < videoFormat->size.height; y++) { + for (uint x = 0; x < videoFormat->size.width; x++) { + uint offset = y * spaBuffer->datas->chunk->stride + x * 4; std::swap(q->fb[offset], q->fb[offset + 2]); } } @@ -688,11 +789,10 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) : videoFormat->format == SPA_VIDEO_FORMAT_RGBx ? QImage::Format_RGBX8888 : QImage::Format_RGB32; - QImage img((uchar*) q->fb, videoSize.width(), videoSize.height(), dstStride, format); + QImage img((uchar*) q->fb, videoFormat->size.width, videoFormat->size.height, spaBuffer->datas->chunk->stride, format); img.convertTo(QImage::Format_RGB888); } - - q->tiles.append(QRect(0, 0, videoSize.width(), videoSize.height())); +#endif } /** @@ -703,19 +803,28 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) pw_stream *PWFrameBuffer::Private::createReceivingStream() { spa_rectangle pwMinScreenBounds = SPA_RECTANGLE(1, 1); - spa_rectangle pwMaxScreenBounds = SPA_RECTANGLE(UINT32_MAX, UINT32_MAX); + spa_rectangle pwMaxScreenBounds = SPA_RECTANGLE(screenGeometry.width, screenGeometry.height); spa_fraction pwFramerateMin = SPA_FRACTION(0, 1); spa_fraction pwFramerateMax = SPA_FRACTION(60, 1); - pw_properties* reuseProps = pw_properties_new_string("pipewire.client.reuse=1"); - - auto stream = pw_stream_new(pwCore, "krfb-fb-consume-stream", reuseProps); +#if PW_CHECK_VERSION(0, 2, 90) + auto stream = pw_stream_new_simple(pw_thread_loop_get_loop(pwMainLoop), "krfb-fb-consume-stream", + pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Screen", + nullptr), + &pwStreamEvents, this); +#else + auto reuseProps = pw_properties_new("pipewire.client.reuse", "1", nullptr); // null marks end of varargs + auto stream = pw_stream_new(pwRemote, "krfb-fb-consume-stream", reuseProps); +#endif uint8_t buffer[1024] = {}; const spa_pod *params[1]; auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); +#if PW_CHECK_VERSION(0, 2, 90) params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), @@ -727,10 +836,25 @@ pw_stream *PWFrameBuffer::Private::createReceivingStream() SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMaxScreenBounds, &pwMinScreenBounds, &pwMaxScreenBounds), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&pwFramerateMin), SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&pwFramerateMax, &pwFramerateMin, &pwFramerateMax))); - +#else + params[0] = reinterpret_cast(spa_pod_builder_object(&builder, + pwCoreType->param.idEnumFormat, pwCoreType->spa_format, + "I", pwType->media_type.video, + "I", pwType->media_subtype.raw, + ":", pwType->format_video.format, "I", pwType->video_format.RGBx, + ":", pwType->format_video.size, "Rru", &pwMaxScreenBounds, SPA_POD_PROP_MIN_MAX(&pwMinScreenBounds, &pwMaxScreenBounds), + ":", pwType->format_video.framerate, "F", &pwFramerateMin, + ":", pwType->format_video.max_framerate, "Fru", &pwFramerateMax, 2, &pwFramerateMin, &pwFramerateMax)); pw_stream_add_listener(stream, &streamListener, &pwStreamEvents, this); +#endif - if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pwStreamNodeId, PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) { + auto flags = static_cast(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS); +#if PW_CHECK_VERSION(0, 2, 90) + if (pw_stream_connect(stream, PW_DIRECTION_INPUT, PW_ID_ANY, flags, params, 1) != 0) { +#else + if (pw_stream_connect(stream, PW_DIRECTION_INPUT, nullptr, flags, params, 1) != 0) { +#endif + qCWarning(KRFB_FB_PIPEWIRE) << "Could not connect receiving stream"; isValid = false; } @@ -743,10 +867,23 @@ PWFrameBuffer::Private::~Private() pw_thread_loop_stop(pwMainLoop); } +#if !PW_CHECK_VERSION(0, 2, 9) + if (pwType) { + delete pwType; + } +#endif + if (pwStream) { pw_stream_destroy(pwStream); } +#if !PW_CHECK_VERSION(0, 2, 90) + if (pwRemote) { + pw_remote_destroy(pwRemote); + } +#endif + +#if PW_CHECK_VERSION(0, 2, 90) if (pwCore) { pw_core_disconnect(pwCore); } @@ -754,10 +891,22 @@ PWFrameBuffer::Private::~Private() if (pwContext) { pw_context_destroy(pwContext); } +#else + if (pwCore) { + pw_core_destroy(pwCore); + } +#endif if (pwMainLoop) { pw_thread_loop_destroy(pwMainLoop); } + +#if !PW_CHECK_VERSION(0, 2, 90) + if (pwLoop) { + pw_loop_leave(pwLoop); + pw_loop_destroy(pwLoop); + } +#endif } PWFrameBuffer::PWFrameBuffer(WId winid, QObject *parent) @@ -768,6 +917,9 @@ PWFrameBuffer::PWFrameBuffer(WId winid, QObject *parent) // PipeWire connectivity is initialized after D-Bus session is started d->initDbus(); + // FIXME: for now use some initial size, later on we will reallocate this with the actual size we get from portal + d->screenGeometry.width = 800; + d->screenGeometry.height = 600; fb = nullptr; } @@ -784,12 +936,12 @@ int PWFrameBuffer::depth() int PWFrameBuffer::height() { - return d->videoSize.height(); + return static_cast(d->screenGeometry.height); } int PWFrameBuffer::width() { - return d->videoSize.width(); + return static_cast(d->screenGeometry.width); } int PWFrameBuffer::paddedWidth()