9#include "pipewiresourcestream.h" 
   12#include "pipewirecore_p.h" 
   14#include "vaapiutils_p.h" 
   16#include <libdrm/drm_fourcc.h> 
   17#include <spa/utils/result.h> 
   22#include <QGuiApplication> 
   23#include <QOpenGLTexture> 
   24#include <QSocketNotifier> 
   26#include <QVersionNumber> 
   27#include <qpa/qplatformnativeinterface.h> 
   29#include <KLocalizedString> 
   32#include <EGL/eglext.h> 
   36#if !PW_CHECK_VERSION(0, 3, 29) 
   37#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) 
   39#if !PW_CHECK_VERSION(0, 3, 33) 
   40#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) 
   44#define CURSOR_META_SIZE(w, h) (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP) 
   46pw_stream_events pwStreamEvents = {};
 
   48struct PipeWireSourceStreamPrivate
 
   50    QSharedPointer<PipeWireCore> pwCore;
 
   51    pw_stream *pwStream = 
nullptr;
 
   52    spa_hook streamListener;
 
   54    uint32_t pwNodeId = 0;
 
   55    std::optional<std::chrono::nanoseconds> m_currentPresentationTimestamp;
 
   57    QAtomicInt m_stopped = 
false;
 
   58    pw_stream_state m_state = PW_STREAM_STATE_UNCONNECTED;
 
   60    spa_video_info_raw videoFormat;
 
   62    bool m_allowDmaBuf = 
true;
 
   63    bool m_usingDmaBuf = 
false;
 
   65    QHash<spa_video_format, QList<uint64_t>> m_availableModifiers;
 
   66    spa_source *m_renegotiateEvent = 
nullptr;
 
   68    bool m_withDamage = 
false;
 
   69    Fraction maxFramerate;
 
   71    PipeWireSourceStream::UsageHint usageHint = PipeWireSourceStream::UsageHint::Render;
 
   77static const QVersionNumber kDropSingleModifierMinVersion = {0, 3, 40};
 
   79uint32_t PipeWireSourceStream::spaVideoFormatToDrmFormat(spa_video_format spa_format)
 
   82    case SPA_VIDEO_FORMAT_RGBA:
 
   83        return DRM_FORMAT_ABGR8888;
 
   84    case SPA_VIDEO_FORMAT_RGBx:
 
   85        return DRM_FORMAT_XBGR8888;
 
   86    case SPA_VIDEO_FORMAT_BGRA:
 
   87        return DRM_FORMAT_ARGB8888;
 
   88    case SPA_VIDEO_FORMAT_BGRx:
 
   89        return DRM_FORMAT_XRGB8888;
 
   90    case SPA_VIDEO_FORMAT_BGR:
 
   91        return DRM_FORMAT_BGR888;
 
   92    case SPA_VIDEO_FORMAT_RGB:
 
   93        return DRM_FORMAT_RGB888;
 
   94    case SPA_VIDEO_FORMAT_xBGR:
 
   95        return DRM_FORMAT_RGBX8888;
 
   96    case SPA_VIDEO_FORMAT_ABGR:
 
   97        return DRM_FORMAT_RGBA8888;
 
   98    case SPA_VIDEO_FORMAT_GRAY8:
 
  101        qCWarning(PIPEWIRE_LOGGING) << 
"cannot convert spa format to fourcc" << spa_format;
 
  102        return DRM_FORMAT_INVALID;
 
  106static QString drmFormatName(uint32_t format)
 
  113                             format & DRM_FORMAT_BIG_ENDIAN ? 
"big" : 
"little",
 
  117spa_video_format drmFormatToSpaVideoFormat(uint32_t drm_format)
 
  119    switch (drm_format) {
 
  120    case DRM_FORMAT_ABGR8888:
 
  121        return SPA_VIDEO_FORMAT_RGBA;
 
  122    case DRM_FORMAT_XBGR8888:
 
  123        return SPA_VIDEO_FORMAT_RGBx;
 
  124    case DRM_FORMAT_ARGB8888:
 
  125        return SPA_VIDEO_FORMAT_BGRA;
 
  126    case DRM_FORMAT_XRGB8888:
 
  127        return SPA_VIDEO_FORMAT_BGRx;
 
  128    case DRM_FORMAT_BGR888:
 
  129        return SPA_VIDEO_FORMAT_BGR;
 
  130    case DRM_FORMAT_RGB888:
 
  131        return SPA_VIDEO_FORMAT_RGB;
 
  132    case DRM_FORMAT_YUYV:
 
  133        return SPA_VIDEO_FORMAT_YUY2;
 
  135        return SPA_VIDEO_FORMAT_GRAY8;
 
  137        qCWarning(PIPEWIRE_LOGGING) << 
"cannot convert drm format to spa" << drmFormatName(drm_format);
 
  138        return SPA_VIDEO_FORMAT_UNKNOWN;
 
  146    const bool hasEglImageDmaBufImportExt = epoxy_has_egl_extension(display, 
"EGL_EXT_image_dma_buf_import");
 
  147    static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress(
"eglQueryDmaBufModifiersEXT");
 
  148    static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress(
"eglQueryDmaBufFormatsEXT");
 
  151    EGLBoolean successFormats = eglQueryDmaBufFormatsEXT(display, 0, 
nullptr, &count);
 
  154    successFormats &= eglQueryDmaBufFormatsEXT(display, count, 
reinterpret_cast<EGLint *
>(drmFormats.data()), &count);
 
  156        qCWarning(PIPEWIRE_LOGGING) << 
"Failed to query DMA-BUF formats.";
 
  159    if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT || !hasEglImageDmaBufImportExt || !successFormats) {
 
  160        for (spa_video_format format : formats) {
 
  166    for (spa_video_format format : formats) {
 
  167        uint32_t drm_format = PipeWireSourceStream::spaVideoFormatToDrmFormat(format);
 
  168        if (drm_format == DRM_FORMAT_INVALID) {
 
  169            qCDebug(PIPEWIRE_LOGGING) << 
"Failed to find matching DRM format." << format;
 
  173        if (std::find(drmFormats.begin(), drmFormats.end(), drm_format) == drmFormats.end()) {
 
  174            qCDebug(PIPEWIRE_LOGGING) << 
"Format " << drmFormatName(drm_format) << 
" not supported for modifiers.";
 
  179        successFormats = eglQueryDmaBufModifiersEXT(display, drm_format, 0, 
nullptr, 
nullptr, &count);
 
  180        if (!successFormats) {
 
  181            qCWarning(PIPEWIRE_LOGGING) << 
"Failed to query DMA-BUF modifier count.";
 
  189            if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, queriedModifiers.data(), externalOnly.data(), &count)) {
 
  190                qCWarning(PIPEWIRE_LOGGING) << 
"Failed to query DMA-BUF modifiers.";
 
  195        usableModifiers.
reserve(count + 1);
 
  196        if (usageHint == PipeWireSourceStream::UsageHint::EncodeHardware) {
 
  197            auto vaapi = VaapiUtils::instance();
 
  198            for (
int i = 0; i < queriedModifiers.size(); ++i) {
 
  199                if (externalOnly[i]) {
 
  202                const uint64_t modifier = queriedModifiers[i];
 
  203                if (vaapi->supportsModifier(drm_format, modifier)) {
 
  204                    usableModifiers.
append(modifier);
 
  208            for (
int i = 0; i < queriedModifiers.size(); ++i) {
 
  209                if (!externalOnly[i]) {
 
  210                    usableModifiers.
append(queriedModifiers[i]);
 
  215        if (!usableModifiers.
isEmpty()) {
 
  217            usableModifiers.
push_back(DRM_FORMAT_MOD_INVALID);
 
  220        ret[format] = usableModifiers;
 
  225void PipeWireSourceStream::onStreamStateChanged(
void *data, pw_stream_state old, pw_stream_state state, 
const char *error_message)
 
  227    PipeWireSourceStream *pw = 
static_cast<PipeWireSourceStream *
>(data);
 
  228    qCDebug(PIPEWIRE_LOGGING) << 
"state changed" << pw_stream_state_as_string(old) << 
"->" << pw_stream_state_as_string(state) << error_message;
 
  229    pw->d->m_state = state;
 
  230    Q_EMIT pw->stateChanged(state, old);
 
  233    case PW_STREAM_STATE_ERROR:
 
  234        qCWarning(PIPEWIRE_LOGGING) << 
"Stream error: " << error_message;
 
  236    case PW_STREAM_STATE_PAUSED:
 
  239    case PW_STREAM_STATE_STREAMING:
 
  240        Q_EMIT pw->startStreaming();
 
  242    case PW_STREAM_STATE_CONNECTING:
 
  244    case PW_STREAM_STATE_UNCONNECTED:
 
  245        if (!pw->d->m_stopped) {
 
  246            Q_EMIT pw->stopStreaming();
 
  252void PipeWireSourceStream::onRenegotiate(
void *data, uint64_t)
 
  254    PipeWireSourceStream *pw = 
static_cast<PipeWireSourceStream *
>(data);
 
  255    uint8_t buffer[4096];
 
  256    spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, 
sizeof(buffer));
 
  257    auto params = pw->createFormatsParams(podBuilder);
 
  258    pw_stream_update_params(pw->d->pwStream, params.data(), params.size());
 
  261void PipeWireSourceStream::renegotiateModifierFailed(spa_video_format format, quint64 modifier)
 
  263    if (d->pwCore->serverVersion() >= kDropSingleModifierMinVersion) {
 
  264        const int removed = d->m_availableModifiers[format].removeAll(modifier);
 
  266            d->m_allowDmaBuf = 
false;
 
  269        d->m_allowDmaBuf = 
false;
 
  271    qCDebug(PIPEWIRE_LOGGING) << 
"renegotiating, modifier didn't work" << format << modifier << 
"now only offering" << d->m_availableModifiers[format].count();
 
  272    pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
 
  276buildFormat(spa_pod_builder *builder, spa_video_format format, 
const QList<uint64_t> &modifiers, 
bool withDontFixate, 
const Fraction &requestedMaxFramerate)
 
  279    const spa_rectangle pw_min_screen_bounds{1, 1};
 
  280    const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX};
 
  282    spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
 
  283    spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
 
  284    spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
 
  285    spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
 
  286    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);
 
  287    if (requestedMaxFramerate) {
 
  288        auto defFramerate = SPA_FRACTION(0, 1);
 
  289        auto minFramerate = SPA_FRACTION(1, 1);
 
  290        auto maxFramerate = SPA_FRACTION(requestedMaxFramerate.numerator, requestedMaxFramerate.denominator);
 
  291        spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&defFramerate), 0);
 
  292        spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&maxFramerate, &minFramerate, &maxFramerate), 0);
 
  294        auto defFramerate = SPA_FRACTION(0, 1);
 
  295        auto maxFramerate = SPA_FRACTION(1200, 1);
 
  296        spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(&defFramerate, &defFramerate, &maxFramerate), 0);
 
  301        if (withDontFixate) {
 
  302            spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
 
  304            spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
 
  306        spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0);
 
  308        for (
auto it = modifiers.
begin(); it != modifiers.
end(); it++) {
 
  309            spa_pod_builder_long(builder, *it);
 
  310            if (it == modifiers.
begin()) {
 
  311                spa_pod_builder_long(builder, *it);
 
  314        spa_pod_builder_pop(builder, &f[1]);
 
  317    return static_cast<spa_pod *
>(spa_pod_builder_pop(builder, &f[0]));
 
  320static const int videoDamageRegionCount = 16;
 
  322void PipeWireSourceStream::onStreamParamChanged(
void *data, uint32_t 
id, 
const struct spa_pod *format)
 
  324    if (!format || 
id != SPA_PARAM_Format) {
 
  328    PipeWireSourceStream *pw = 
static_cast<PipeWireSourceStream *
>(data);
 
  329    spa_format_video_raw_parse(format, &pw->d->videoFormat);
 
  331    uint8_t paramsBuffer[1024];
 
  332    spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, 
sizeof(paramsBuffer));
 
  338    pw->d->m_usingDmaBuf = pw->d->m_allowDmaBuf && spa_pod_find_prop(format, 
nullptr, SPA_FORMAT_VIDEO_modifier);
 
  339    Q_ASSERT(pw->d->m_allowDmaBuf || !pw->d->m_usingDmaBuf);
 
  340    const auto bufferTypes =
 
  341        pw->d->m_usingDmaBuf ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
 
  343    QVarLengthArray<const spa_pod *> params = {
 
  344        (spa_pod *)spa_pod_builder_add_object(&pod_builder,
 
  345                                              SPA_TYPE_OBJECT_ParamBuffers,
 
  347                                              SPA_PARAM_BUFFERS_buffers,
 
  348                                              SPA_POD_CHOICE_RANGE_Int(3, 2, 16),
 
  349                                              SPA_PARAM_BUFFERS_align,
 
  351                                              SPA_PARAM_BUFFERS_dataType,
 
  352                                              SPA_POD_CHOICE_FLAGS_Int(bufferTypes)),
 
  353        (spa_pod *)spa_pod_builder_add_object(&pod_builder,
 
  354                                              SPA_TYPE_OBJECT_ParamMeta,
 
  357                                              SPA_POD_Id(SPA_META_Header),
 
  359                                              SPA_POD_Int(
sizeof(
struct spa_meta_header))),
 
  360        (spa_pod *)spa_pod_builder_add_object(&pod_builder,
 
  361                                              SPA_TYPE_OBJECT_ParamMeta,
 
  364                                              SPA_POD_Id(SPA_META_Cursor),
 
  366                                              SPA_POD_CHOICE_RANGE_Int(CURSOR_META_SIZE(64, 64), CURSOR_META_SIZE(1, 1), CURSOR_META_SIZE(1024, 1024))),
 
  369    if (pw->d->m_withDamage) {
 
  370        params.append((spa_pod *)spa_pod_builder_add_object(&pod_builder,
 
  371                                                            SPA_TYPE_OBJECT_ParamMeta,
 
  374                                                            SPA_POD_Id(SPA_META_VideoDamage),
 
  376                                                            SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * videoDamageRegionCount,
 
  377                                                                                     sizeof(struct spa_meta_region) * 1,
 
  378                                                                                     sizeof(struct spa_meta_region) * videoDamageRegionCount)));
 
  381    pw_stream_update_params(pw->d->pwStream, params.
data(), params.
count());
 
  382    Q_EMIT pw->streamParametersChanged();
 
  385static void onProcess(
void *data)
 
  387    PipeWireSourceStream *stream = 
static_cast<PipeWireSourceStream *
>(data);
 
  401PipeWireFrameData::~PipeWireFrameData()
 
  403    PipeWireFrameCleanupFunction::unref(cleanup);
 
  406QSize PipeWireSourceStream::size()
 const 
  408    return QSize(d->videoFormat.size.width, d->videoFormat.size.height);
 
  411pw_stream_state PipeWireSourceStream::state()
 const 
  416std::optional< std::chrono::nanoseconds > PipeWireSourceStream::currentPresentationTimestamp()
 const 
  418    return d->m_currentPresentationTimestamp;
 
  421QString PipeWireSourceStream::error()
 const 
  426PipeWireSourceStream::PipeWireSourceStream(
QObject *parent)
 
  428    , d(new PipeWireSourceStreamPrivate)
 
  430    pwStreamEvents.version = PW_VERSION_STREAM_EVENTS;
 
  431    pwStreamEvents.process = &onProcess;
 
  432    pwStreamEvents.state_changed = &PipeWireSourceStream::onStreamStateChanged;
 
  433    pwStreamEvents.param_changed = &PipeWireSourceStream::onStreamParamChanged;
 
  434    pwStreamEvents.destroy = &PipeWireSourceStream::onDestroy;
 
  437PipeWireSourceStream::~PipeWireSourceStream()
 
  440    if (d->m_renegotiateEvent) {
 
  441        pw_loop_destroy_source(d->pwCore->loop(), d->m_renegotiateEvent);
 
  444        pw_stream_destroy(d->pwStream);
 
  448Fraction PipeWireSourceStream::framerate()
 const 
  451        return {d->videoFormat.max_framerate.num, d->videoFormat.max_framerate.denom};
 
  457void PipeWireSourceStream::setMaxFramerate(
const Fraction &framerate)
 
  459    d->maxFramerate = framerate;
 
  462        pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
 
  466uint PipeWireSourceStream::nodeId()
 
  471PipeWireSourceStream::UsageHint PipeWireSourceStream::usageHint()
 const 
  476void PipeWireSourceStream::setUsageHint(UsageHint hint)
 
  483    const auto pwServerVersion = d->pwCore->serverVersion();
 
  484    static constexpr auto formats = {
 
  485        SPA_VIDEO_FORMAT_RGBx,
 
  486        SPA_VIDEO_FORMAT_RGBA,
 
  487        SPA_VIDEO_FORMAT_BGRx,
 
  488        SPA_VIDEO_FORMAT_BGRA,
 
  489        SPA_VIDEO_FORMAT_RGB,
 
  490        SPA_VIDEO_FORMAT_BGR,
 
  491        SPA_VIDEO_FORMAT_xBGR,
 
  492        SPA_VIDEO_FORMAT_ABGR,
 
  493        SPA_VIDEO_FORMAT_GRAY8,
 
  495    QList<const spa_pod *> params;
 
  496    params.
reserve(formats.size() * 2);
 
  497    const EGLDisplay display = 
static_cast<EGLDisplay
>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration(
"egldisplay"));
 
  499    d->m_allowDmaBuf = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufMinVersion && pwServerVersion >= kDmaBufMinVersion));
 
  500    const bool withDontFixate = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufModifierMinVersion && pwServerVersion >= kDmaBufModifierMinVersion));
 
  502    if (!d->m_allowDmaBuf && d->usageHint == UsageHint::EncodeHardware) {
 
  503        qCWarning(PIPEWIRE_LOGGING) << 
"DMABUF is unsupported but hardware encoding is requested, which requires DMABUF import. This will not work correctly.";
 
  506    if (d->m_availableModifiers.isEmpty()) {
 
  507        static const auto availableModifiers = queryDmaBufModifiers(display, formats, d->usageHint);
 
  508        d->m_availableModifiers = availableModifiers;
 
  511    for (
auto it = d->m_availableModifiers.constBegin(), itEnd = d->m_availableModifiers.constEnd(); it != itEnd; ++it) {
 
  512        if (d->m_allowDmaBuf && !it->isEmpty()) {
 
  513            params += buildFormat(&podBuilder, it.key(), it.value(), withDontFixate, d->maxFramerate);
 
  516        params += buildFormat(&podBuilder, it.key(), {}, withDontFixate, d->maxFramerate);
 
  524bool PipeWireSourceStream::createStream(uint nodeid, 
int fd)
 
  526    d->m_availableModifiers.
clear();
 
  527    d->pwCore = PipeWireCore::fetch(fd);
 
  528    if (!d->pwCore->error().isEmpty()) {
 
  529        qCDebug(PIPEWIRE_LOGGING) << 
"received error while creating the stream" << d->pwCore->error();
 
  530        d->m_error = d->pwCore->error();
 
  534    connect(d->pwCore.data(), &PipeWireCore::pipewireFailed, 
this, &PipeWireSourceStream::coreFailed);
 
  537        setObjectName(QStringLiteral(
"plasma-screencast-%1").arg(nodeid));
 
  540    const auto pwServerVersion = d->pwCore->serverVersion();
 
  541    d->pwStream = pw_stream_new(**d->pwCore, 
objectName().toUtf8().constData(), 
nullptr);
 
  542    d->pwNodeId = nodeid;
 
  543    pw_stream_add_listener(d->pwStream, &d->streamListener, &pwStreamEvents, 
this);
 
  545    d->m_renegotiateEvent = pw_loop_add_event(d->pwCore->loop(), onRenegotiate, 
this);
 
  547    uint8_t buffer[4096];
 
  548    spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, 
sizeof(buffer));
 
  549    auto params = createFormatsParams(podBuilder);
 
  550    pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT);
 
  551    if (pw_stream_connect(d->pwStream, PW_DIRECTION_INPUT, d->pwNodeId, s, params.
data(), params.
size()) != 0) {
 
  552        qCWarning(PIPEWIRE_LOGGING) << 
"Could not connect to stream";
 
  553        pw_stream_destroy(d->pwStream);
 
  554        d->pwStream = 
nullptr;
 
  557    qCDebug(PIPEWIRE_LOGGING) << 
"created successfully" << nodeid;
 
  561void PipeWireSourceStream::handleFrame(
struct pw_buffer *buffer)
 
  563    spa_buffer *spaBuffer = buffer->buffer;
 
  566    frame.format = d->videoFormat.format;
 
  568    struct spa_meta_header *header = (
struct spa_meta_header *)spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, 
sizeof(*header));
 
  570        if (header->flags & SPA_META_HEADER_FLAG_CORRUPTED) {
 
  571            qCDebug(PIPEWIRE_LOGGING) << 
"buffer is corrupt";
 
  575        d->m_currentPresentationTimestamp = std::chrono::nanoseconds(header->pts);
 
  576        frame.presentationTimestamp = std::chrono::nanoseconds(header->pts);
 
  577        frame.sequential = header->seq;
 
  579        d->m_currentPresentationTimestamp = std::chrono::steady_clock::now().time_since_epoch();
 
  580        frame.presentationTimestamp = d->m_currentPresentationTimestamp;
 
  583    if (spa_meta *vd = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) {
 
  584        frame.damage = QRegion();
 
  586        spa_meta_for_each(mr, vd)
 
  588            *frame.damage += QRect(mr->region.position.x, mr->region.position.y, mr->region.size.width, mr->region.size.height);
 
  593        struct spa_meta_cursor *cursor = 
static_cast<struct spa_meta_cursor *
>(spa_buffer_find_meta_data(spaBuffer, SPA_META_Cursor, 
sizeof(*cursor)));
 
  594        if (cursor && spa_meta_cursor_is_valid(cursor)) {
 
  595            struct spa_meta_bitmap *bitmap = 
nullptr;
 
  597            if (cursor->bitmap_offset)
 
  598                bitmap = SPA_MEMBER(cursor, cursor->bitmap_offset, 
struct spa_meta_bitmap);
 
  600            QImage cursorTexture;
 
  601            if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
 
  602                const size_t bufferSize = bitmap->stride * bitmap->size.height * 4;
 
  603                void *bufferData = malloc(bufferSize);
 
  604                memcpy(bufferData, SPA_MEMBER(bitmap, bitmap->offset, uint8_t), bufferSize);
 
  605                cursorTexture = PWHelpers::SpaBufferToQImage(
static_cast<const uchar *
>(bufferData),
 
  609                                                             spa_video_format(bitmap->format),
 
  610                                                             new PipeWireFrameCleanupFunction([bufferData] {
 
  614            frame.cursor = {{cursor->position.x, cursor->position.y}, {cursor->hotspot.x, cursor->hotspot.y}, cursorTexture};
 
  618    if (spaBuffer->datas->chunk->flags == SPA_CHUNK_FLAG_CORRUPTED) {
 
  620        qCDebug(PIPEWIRE_LOGGING) << 
"skipping empty buffer" << spaBuffer->datas->chunk->size << spaBuffer->datas->chunk->flags;
 
  621    } 
else if (spaBuffer->datas->type == SPA_DATA_MemFd) {
 
  622        if (spaBuffer->datas->chunk->size == 0) {
 
  623            qCDebug(PIPEWIRE_LOGGING) << 
"skipping empty memfd buffer";
 
  625            const uint32_t mapEnd = spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset;
 
  626            uint8_t *
map = 
static_cast<uint8_t *
>(mmap(
nullptr, mapEnd, PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0));
 
  628            if (map == MAP_FAILED) {
 
  629                qCWarning(PIPEWIRE_LOGGING) << 
"Failed to mmap the memory: " << strerror(errno);
 
  632            auto cleanup = [
map, mapEnd]() {
 
  635            frame.dataFrame = std::make_shared<PipeWireFrameData>(d->videoFormat.format,
 
  637                                                                  QSize(d->videoFormat.size.width, d->videoFormat.size.height),
 
  638                                                                  spaBuffer->datas->chunk->stride,
 
  639                                                                  new PipeWireFrameCleanupFunction(cleanup));
 
  641    } 
else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) {
 
  642        DmaBufAttributes attribs;
 
  643        attribs.planes.
reserve(spaBuffer->n_datas);
 
  644        attribs.format = spaVideoFormatToDrmFormat(d->videoFormat.format);
 
  645        attribs.modifier = d->videoFormat.modifier;
 
  646        attribs.width = d->videoFormat.size.width;
 
  647        attribs.height = d->videoFormat.size.height;
 
  649        for (uint i = 0; i < spaBuffer->n_datas; ++i) {
 
  650            const auto &data = spaBuffer->datas[i];
 
  654            plane.stride = data.chunk->stride;
 
  655            plane.offset = data.chunk->offset;
 
  656            attribs.planes += plane;
 
  658        Q_ASSERT(!attribs.planes.
isEmpty());
 
  659        frame.dmabuf = attribs;
 
  660    } 
else if (spaBuffer->datas->type == SPA_DATA_MemPtr) {
 
  661        if (spaBuffer->datas->chunk->size == 0) {
 
  662            qCDebug(PIPEWIRE_LOGGING) << 
"skipping empty memptr buffer";
 
  664            frame.dataFrame = std::make_shared<PipeWireFrameData>(d->videoFormat.format,
 
  665                                                                  spaBuffer->datas->data,
 
  666                                                                  QSize(d->videoFormat.size.width, d->videoFormat.size.height),
 
  667                                                                  spaBuffer->datas->chunk->stride,
 
  671        if (spaBuffer->datas->type == SPA_ID_INVALID) {
 
  672            qCWarning(PIPEWIRE_LOGGING) << 
"invalid buffer type";
 
  674            qCWarning(PIPEWIRE_LOGGING) << 
"unsupported buffer type" << spaBuffer->datas->type;
 
  676        frame.dataFrame = {};
 
  679    Q_EMIT frameReceived(frame);
 
  682void PipeWireSourceStream::coreFailed(
const QString &errorMessage)
 
  684    qCDebug(PIPEWIRE_LOGGING) << 
"received error message" << 
errorMessage;
 
  689void PipeWireSourceStream::process()
 
  691#if !PW_CHECK_VERSION(0, 3, 73) 
  692    if (Q_UNLIKELY(!d->pwStream)) {
 
  694        qCDebug(PIPEWIRE_LOGGING) << 
"stream was terminated before processing buffer";
 
  699    pw_buffer *buf = pw_stream_dequeue_buffer(d->pwStream);
 
  701        qCDebug(PIPEWIRE_LOGGING) << 
"out of buffers";
 
  707    pw_stream_queue_buffer(d->pwStream, buf);
 
  710void PipeWireSourceStream::setActive(
bool active)
 
  713        qCWarning(PIPEWIRE_LOGGING) << 
"Tried to make uncreated stream active";
 
  716    pw_stream_set_active(d->pwStream, active);
 
  719void PipeWireSourceStream::setDamageEnabled(
bool withDamage)
 
  721    d->m_withDamage = withDamage;
 
  724bool PipeWireSourceStream::usingDmaBuf()
 const 
  726    return d->m_usingDmaBuf;
 
  729bool PipeWireSourceStream::allowDmaBuf()
 const 
  731    return d->m_allowDmaBuf;
 
  734void PipeWireSourceStream::setAllowDmaBuf(
bool allowed)
 
  736    d->m_allowDmaBuf = allowed;
 
  739void PipeWireSourceStream::onDestroy(
void *data)
 
  742    auto pw = 
static_cast<PipeWireSourceStream *
>(data);
 
  743    pw->d->pwStream = 
nullptr;
 
  746#include "moc_pipewiresourcestream.cpp" 
The to track the lifetime of a pipewire frame.
 
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
 
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
 
void reserve(qsizetype size)
 
void append(QList< T > &&value)
 
bool isEmpty() const const
 
void push_back(parameter_type value)
 
qsizetype removeAll(const AT &t)
 
void reserve(qsizetype size)
 
qsizetype size() const const
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
void setObjectName(QAnyStringView name)
 
QString asprintf(const char *cformat,...)
 
QString fromUtf8(QByteArrayView str)
 
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
 
qsizetype count() const const
 
QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex)