diff --git a/framebuffers/pipewire/pw_framebuffer.cpp b/framebuffers/pipewire/pw_framebuffer.cpp index 98cfe31..41fc635 100644 --- a/framebuffers/pipewire/pw_framebuffer.cpp +++ b/framebuffers/pipewire/pw_framebuffer.cpp @@ -175,6 +175,10 @@ private: // sanity indicator bool isValid = true; + QImage cursorTexture; + QPoint cursorPosition; + QPoint cursorHotspot; + #if HAVE_DMA_BUF struct EGLStruct { QList extensions; @@ -556,6 +560,10 @@ void PWFrameBuffer::Private::onStreamStateChanged(void *data, pw_stream_state /* } } +#define CURSOR_BPP 4 +#define CURSOR_META_SIZE(w,h) (sizeof(struct spa_meta_cursor) + \ + sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP) + /** * @brief PWFrameBuffer::Private::onStreamFormatChanged - being executed after stream is set to active * and after setup has been requested to connect to it. The actual video format is being negotiated here. @@ -583,7 +591,6 @@ void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); // setup buffers and meta header for new format - const struct spa_pod *params[3]; #if HAVE_DMA_BUF const auto bufferTypes = d->m_eglInitialized ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : @@ -592,23 +599,31 @@ void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const const auto bufferTypes = (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); #endif /* HAVE_DMA_BUF */ - params[0] = reinterpret_cast(spa_pod_builder_add_object(&builder, + QVector params = { + reinterpret_cast(spa_pod_builder_add_object(&builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_align, SPA_POD_Int(16), - SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(bufferTypes))); - params[1] = reinterpret_cast(spa_pod_builder_add_object(&builder, + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(bufferTypes))), + reinterpret_cast(spa_pod_builder_add_object(&builder, 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_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))), + 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); + SPA_POD_Int(sizeof(struct spa_meta_region)))), + reinterpret_cast(spa_pod_builder_add_object ( &builder, + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), + SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64, 64), + CURSOR_META_SIZE (1, 1), + CURSOR_META_SIZE (1024, 1024)))), + }; + pw_stream_update_params(d->pwStream, params.data(), params.size()); } /** @@ -642,6 +657,13 @@ void PWFrameBuffer::Private::onStreamProcess(void *data) pw_stream_queue_buffer(d->pwStream, buffer); } +static QImage::Format spaToQImageFormat(quint32 format) +{ + return format == SPA_VIDEO_FORMAT_BGR ? QImage::Format_BGR888 + : format == SPA_VIDEO_FORMAT_RGBx ? QImage::Format_RGBX8888 + : QImage::Format_RGB32; +} + void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) { auto spaBuffer = pwBuffer->buffer; @@ -751,6 +773,28 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) } #endif /* HAVE_DMA_BUF */ + + // process cursor + { + struct spa_meta_cursor *cursor = static_cast(spa_buffer_find_meta_data (spaBuffer, SPA_META_Cursor, sizeof (*cursor))); + if (spa_meta_cursor_is_valid (cursor)) { + struct spa_meta_bitmap *bitmap = nullptr; + + if (cursor->bitmap_offset) + bitmap = SPA_MEMBER (cursor, cursor->bitmap_offset, struct spa_meta_bitmap); + + if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) { + const uint8_t *bitmap_data; + + bitmap_data = SPA_MEMBER (bitmap, bitmap->offset, uint8_t); + cursorHotspot = { cursor->hotspot.x, cursor->hotspot.y }; + cursorTexture = QImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spaToQImageFormat(bitmap->format)); + } + + cursorPosition = QPoint{ cursor->position.x, cursor->position.y }; + } + } + struct spa_meta_region* videoMetadata = static_cast(spa_buffer_find_meta_data( spaBuffer, SPA_META_VideoCrop, sizeof(*videoMetadata))); @@ -828,11 +872,7 @@ void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer) } if (videoFormat->format != SPA_VIDEO_FORMAT_RGB) { - const QImage::Format format = videoFormat->format == SPA_VIDEO_FORMAT_BGR ? QImage::Format_BGR888 - : 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, videoSize.width(), videoSize.height(), dstStride, spaToQImageFormat(videoFormat->format)); img.convertTo(QImage::Format_RGB888); } @@ -1002,3 +1042,8 @@ bool PWFrameBuffer::isValid() const { return d->isValid; } + +QPoint PWFrameBuffer::cursorPosition() +{ + return d->cursorPosition; +} diff --git a/framebuffers/pipewire/pw_framebuffer.h b/framebuffers/pipewire/pw_framebuffer.h index 36aa90d..9b0efb7 100644 --- a/framebuffers/pipewire/pw_framebuffer.h +++ b/framebuffers/pipewire/pw_framebuffer.h @@ -43,6 +43,7 @@ public: void getServerFormat(rfbPixelFormat &format) override; void startMonitor() override; void stopMonitor() override; + QPoint cursorPosition() override; QVariant customProperty(const QString &property) const override;