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
51 pw_stream *pwStream =
nullptr;
52 spa_hook streamListener;
54 uint32_t pwNodeId = 0;
55 std::optional<std::chrono::nanoseconds> m_currentPresentationTimestamp;
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;
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);
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,
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)) {
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);
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),
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,
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)
712 Q_ASSERT(d->pwStream);
713 pw_stream_set_active(d->pwStream, active);
716void PipeWireSourceStream::setDamageEnabled(
bool withDamage)
718 d->m_withDamage = withDamage;
721bool PipeWireSourceStream::usingDmaBuf()
const
723 return d->m_usingDmaBuf;
726bool PipeWireSourceStream::allowDmaBuf()
const
728 return d->m_allowDmaBuf;
731void PipeWireSourceStream::setAllowDmaBuf(
bool allowed)
733 d->m_allowDmaBuf = allowed;
736void PipeWireSourceStream::onDestroy(
void *data)
739 auto pw =
static_cast<PipeWireSourceStream *
>(data);
740 pw->d->pwStream =
nullptr;
743#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)