File indexing completed on 2024-05-19 09:23:10
0001 /* 0002 SPDX-FileCopyrightText: 2018-2020 Red Hat Inc 0003 SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org> 0004 SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include <epoxy/egl.h> 0010 0011 #include "pipewirecore_p.h" 0012 #include "pipewiresourcestream.h" 0013 0014 #include <libdrm/drm_fourcc.h> 0015 #include <spa/utils/result.h> 0016 #include <sys/ioctl.h> 0017 #include <sys/mman.h> 0018 #include <unistd.h> 0019 0020 #include <QGuiApplication> 0021 #include <QOpenGLTexture> 0022 #include <QSocketNotifier> 0023 #include <QThread> 0024 #include <QVersionNumber> 0025 #include <qpa/qplatformnativeinterface.h> 0026 0027 #undef Status 0028 0029 #if !PW_CHECK_VERSION(0, 3, 29) 0030 #define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) 0031 #endif 0032 #if !PW_CHECK_VERSION(0, 3, 33) 0033 #define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) 0034 #endif 0035 0036 #define CURSOR_BPP 4 0037 #define CURSOR_META_SIZE(w, h) (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP) 0038 0039 pw_stream_events pwStreamEvents = {}; 0040 0041 static QImage SpaBufferToQImage(const uchar *data, int width, int height, qsizetype bytesPerLine, spa_video_format format) 0042 { 0043 switch (format) { 0044 case SPA_VIDEO_FORMAT_BGRx: 0045 case SPA_VIDEO_FORMAT_BGRA: 0046 case SPA_VIDEO_FORMAT_xBGR: 0047 case SPA_VIDEO_FORMAT_ABGR: { 0048 // This is needed because QImage does not support BGRA 0049 // This is obviously a much slower path, it makes sense to avoid it as much as possible 0050 return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format)).rgbSwapped(); 0051 } 0052 case SPA_VIDEO_FORMAT_BGR: 0053 case SPA_VIDEO_FORMAT_RGBx: 0054 case SPA_VIDEO_FORMAT_RGB: 0055 case SPA_VIDEO_FORMAT_RGBA: 0056 default: 0057 return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format)); 0058 } 0059 } 0060 0061 QImage::Format SpaToQImageFormat(quint32 format) 0062 { 0063 switch (format) { 0064 case SPA_VIDEO_FORMAT_BGRx: 0065 case SPA_VIDEO_FORMAT_BGRA: 0066 return QImage::Format_RGBA8888_Premultiplied; // Handled in SpaBufferToQImage 0067 case SPA_VIDEO_FORMAT_ABGR: 0068 case SPA_VIDEO_FORMAT_xBGR: 0069 return QImage::Format_ARGB32; // Handled in SpaBufferToQImage 0070 case SPA_VIDEO_FORMAT_BGR: 0071 return QImage::Format_BGR888; 0072 case SPA_VIDEO_FORMAT_RGBx: 0073 return QImage::Format_RGBX8888; 0074 case SPA_VIDEO_FORMAT_RGB: 0075 return QImage::Format_RGB888; 0076 case SPA_VIDEO_FORMAT_RGBA: 0077 return QImage::Format_RGBA8888_Premultiplied; 0078 default: 0079 qWarning() << "cannot convert spa format to QImage" << format; 0080 return QImage::Format_RGB32; 0081 } 0082 } 0083 0084 struct PipeWireSourceStreamPrivate 0085 { 0086 QSharedPointer<PipeWireCore> pwCore; 0087 pw_stream *pwStream = nullptr; 0088 spa_hook streamListener; 0089 0090 uint32_t pwNodeId = 0; 0091 std::optional<std::chrono::nanoseconds> m_currentPresentationTimestamp; 0092 0093 QAtomicInt m_stopped = false; 0094 pw_stream_state m_state = PW_STREAM_STATE_UNCONNECTED; 0095 0096 spa_video_info_raw videoFormat; 0097 QString m_error; 0098 bool m_allowDmaBuf = true; 0099 bool m_usingDmaBuf = false; 0100 0101 QHash<spa_video_format, QList<uint64_t>> m_availableModifiers; 0102 spa_source *m_renegotiateEvent = nullptr; 0103 0104 bool m_withDamage = false; 0105 Fraction maxFramerate; 0106 }; 0107 0108 static const QVersionNumber pwClientVersion = QVersionNumber::fromString(QString::fromUtf8(pw_get_library_version())); 0109 static const QVersionNumber kDmaBufMinVersion = {0, 3, 24}; 0110 static const QVersionNumber kDmaBufModifierMinVersion = {0, 3, 33}; 0111 static const QVersionNumber kDropSingleModifierMinVersion = {0, 3, 40}; 0112 0113 uint32_t PipeWireSourceStream::spaVideoFormatToDrmFormat(spa_video_format spa_format) 0114 { 0115 switch (spa_format) { 0116 case SPA_VIDEO_FORMAT_RGBA: 0117 return DRM_FORMAT_ABGR8888; 0118 case SPA_VIDEO_FORMAT_RGBx: 0119 return DRM_FORMAT_XBGR8888; 0120 case SPA_VIDEO_FORMAT_BGRA: 0121 return DRM_FORMAT_ARGB8888; 0122 case SPA_VIDEO_FORMAT_BGRx: 0123 return DRM_FORMAT_XRGB8888; 0124 case SPA_VIDEO_FORMAT_BGR: 0125 return DRM_FORMAT_BGR888; 0126 case SPA_VIDEO_FORMAT_RGB: 0127 return DRM_FORMAT_RGB888; 0128 case SPA_VIDEO_FORMAT_xBGR: 0129 return DRM_FORMAT_RGBX8888; 0130 case SPA_VIDEO_FORMAT_ABGR: 0131 return DRM_FORMAT_RGBA8888; 0132 default: 0133 qWarning() << "cannot convert spa format to fourcc" << spa_format; 0134 return DRM_FORMAT_INVALID; 0135 } 0136 } 0137 0138 static QHash<spa_video_format, QList<uint64_t>> queryDmaBufModifiers(EGLDisplay display, const QList<spa_video_format> &formats) 0139 { 0140 QHash<spa_video_format, QList<uint64_t>> ret; 0141 ret.reserve(formats.size()); 0142 const bool hasEglImageDmaBufImportExt = epoxy_has_egl_extension(display, "EGL_EXT_image_dma_buf_import"); 0143 static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT"); 0144 static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress("eglQueryDmaBufFormatsEXT"); 0145 0146 EGLint count = 0; 0147 EGLBoolean successFormats = eglQueryDmaBufFormatsEXT(display, 0, nullptr, &count); 0148 0149 QList<uint32_t> drmFormats(count); 0150 successFormats &= eglQueryDmaBufFormatsEXT(display, count, reinterpret_cast<EGLint *>(drmFormats.data()), &count); 0151 if (!successFormats) 0152 qWarning() << "Failed to query DMA-BUF formats."; 0153 0154 const QList<uint64_t> mods = hasEglImageDmaBufImportExt ? QList<uint64_t>{DRM_FORMAT_MOD_INVALID} : QList<uint64_t>{}; 0155 if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT || !hasEglImageDmaBufImportExt || !successFormats) { 0156 for (spa_video_format format : formats) { 0157 ret[format] = mods; 0158 } 0159 return ret; 0160 } 0161 0162 for (spa_video_format format : formats) { 0163 uint32_t drm_format = PipeWireSourceStream::spaVideoFormatToDrmFormat(format); 0164 if (drm_format == DRM_FORMAT_INVALID) { 0165 qDebug() << "Failed to find matching DRM format." << format; 0166 break; 0167 } 0168 0169 if (std::find(drmFormats.begin(), drmFormats.end(), drm_format) == drmFormats.end()) { 0170 qDebug() << "Format " << drm_format << " not supported for modifiers."; 0171 ret[format] = mods; 0172 break; 0173 } 0174 0175 successFormats = eglQueryDmaBufModifiersEXT(display, drm_format, 0, nullptr, nullptr, &count); 0176 if (!successFormats) { 0177 qWarning() << "Failed to query DMA-BUF modifier count."; 0178 ret[format] = mods; 0179 break; 0180 } 0181 0182 QList<uint64_t> modifiers(count); 0183 if (count > 0) { 0184 if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, modifiers.data(), nullptr, &count)) { 0185 qWarning() << "Failed to query DMA-BUF modifiers."; 0186 } 0187 } 0188 0189 // Support modifier-less buffers 0190 modifiers.push_back(DRM_FORMAT_MOD_INVALID); 0191 ret[format] = modifiers; 0192 } 0193 return ret; 0194 } 0195 0196 void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message) 0197 { 0198 PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data); 0199 qDebug() << "state changed" << pw_stream_state_as_string(old) << "->" << pw_stream_state_as_string(state) << error_message; 0200 pw->d->m_state = state; 0201 Q_EMIT pw->stateChanged(state, old); 0202 0203 switch (state) { 0204 case PW_STREAM_STATE_ERROR: 0205 qWarning() << "Stream error: " << error_message; 0206 break; 0207 case PW_STREAM_STATE_PAUSED: 0208 Q_EMIT pw->streamReady(); 0209 break; 0210 case PW_STREAM_STATE_STREAMING: 0211 Q_EMIT pw->startStreaming(); 0212 break; 0213 case PW_STREAM_STATE_CONNECTING: 0214 break; 0215 case PW_STREAM_STATE_UNCONNECTED: 0216 if (!pw->d->m_stopped) { 0217 Q_EMIT pw->stopStreaming(); 0218 } 0219 break; 0220 } 0221 } 0222 0223 void PipeWireSourceStream::onRenegotiate(void *data, uint64_t) 0224 { 0225 PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data); 0226 uint8_t buffer[4096]; 0227 spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); 0228 auto params = pw->createFormatsParams(podBuilder); 0229 pw_stream_update_params(pw->d->pwStream, params.data(), params.size()); 0230 } 0231 0232 void PipeWireSourceStream::renegotiateModifierFailed(spa_video_format format, quint64 modifier) 0233 { 0234 if (d->pwCore->serverVersion() >= kDropSingleModifierMinVersion) { 0235 const int removed = d->m_availableModifiers[format].removeAll(modifier); 0236 if (removed == 0) { 0237 d->m_allowDmaBuf = false; 0238 } 0239 } else { 0240 d->m_allowDmaBuf = false; 0241 } 0242 qDebug() << "renegotiating, modifier didn't work" << format << modifier << "now only offering" << d->m_availableModifiers[format].count(); 0243 pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent); 0244 } 0245 0246 static spa_pod * 0247 buildFormat(spa_pod_builder *builder, spa_video_format format, const QList<uint64_t> &modifiers, bool withDontFixate, const Fraction &requestedMaxFramerate) 0248 { 0249 spa_pod_frame f[2]; 0250 const spa_rectangle pw_min_screen_bounds{1, 1}; 0251 const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX}; 0252 0253 spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); 0254 spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); 0255 spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); 0256 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); 0257 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); 0258 if (requestedMaxFramerate) { 0259 auto defFramerate = SPA_FRACTION(0, 1); 0260 auto minFramerate = SPA_FRACTION(1, 1); 0261 auto maxFramerate = SPA_FRACTION(requestedMaxFramerate.numerator, requestedMaxFramerate.denominator); 0262 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&defFramerate), 0); 0263 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&maxFramerate, &minFramerate, &maxFramerate), 0); 0264 } 0265 0266 if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { 0267 // we only support implicit modifiers, use shortpath to skip fixation phase 0268 spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); 0269 spa_pod_builder_long(builder, modifiers[0]); 0270 } else if (!modifiers.isEmpty()) { 0271 // SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33 0272 if (withDontFixate) { 0273 spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); 0274 } else { 0275 spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); 0276 } 0277 spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0); 0278 // mofifiers from the array 0279 for (auto it = modifiers.begin(); it != modifiers.end(); it++) { 0280 spa_pod_builder_long(builder, *it); 0281 if (it == modifiers.begin()) { 0282 spa_pod_builder_long(builder, *it); 0283 } 0284 } 0285 spa_pod_builder_pop(builder, &f[1]); 0286 } 0287 0288 return static_cast<spa_pod *>(spa_pod_builder_pop(builder, &f[0])); 0289 } 0290 0291 static const int videoDamageRegionCount = 16; 0292 0293 void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format) 0294 { 0295 if (!format || id != SPA_PARAM_Format) { 0296 return; 0297 } 0298 0299 PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data); 0300 spa_format_video_raw_parse(format, &pw->d->videoFormat); 0301 0302 uint8_t paramsBuffer[1024]; 0303 spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer)); 0304 0305 // When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as 0306 // the server announces support for it. 0307 // See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox 0308 0309 pw->d->m_usingDmaBuf = pw->d->m_allowDmaBuf && spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); 0310 Q_ASSERT(pw->d->m_allowDmaBuf || !pw->d->m_usingDmaBuf); 0311 const auto bufferTypes = 0312 pw->d->m_usingDmaBuf ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); 0313 0314 QVarLengthArray<const spa_pod *> params = { 0315 (spa_pod *)spa_pod_builder_add_object(&pod_builder, 0316 SPA_TYPE_OBJECT_ParamBuffers, 0317 SPA_PARAM_Buffers, 0318 SPA_PARAM_BUFFERS_buffers, 0319 SPA_POD_CHOICE_RANGE_Int(16, 2, 16), 0320 SPA_PARAM_BUFFERS_align, 0321 SPA_POD_Int(16), 0322 SPA_PARAM_BUFFERS_dataType, 0323 SPA_POD_CHOICE_FLAGS_Int(bufferTypes)), 0324 (spa_pod *)spa_pod_builder_add_object(&pod_builder, 0325 SPA_TYPE_OBJECT_ParamMeta, 0326 SPA_PARAM_Meta, 0327 SPA_PARAM_META_type, 0328 SPA_POD_Id(SPA_META_Header), 0329 SPA_PARAM_META_size, 0330 SPA_POD_Int(sizeof(struct spa_meta_header))), 0331 (spa_pod *)spa_pod_builder_add_object(&pod_builder, 0332 SPA_TYPE_OBJECT_ParamMeta, 0333 SPA_PARAM_Meta, 0334 SPA_PARAM_META_type, 0335 SPA_POD_Id(SPA_META_Cursor), 0336 SPA_PARAM_META_size, 0337 SPA_POD_CHOICE_RANGE_Int(CURSOR_META_SIZE(64, 64), CURSOR_META_SIZE(1, 1), CURSOR_META_SIZE(1024, 1024))), 0338 }; 0339 0340 if (pw->d->m_withDamage) { 0341 params.append((spa_pod *)spa_pod_builder_add_object(&pod_builder, 0342 SPA_TYPE_OBJECT_ParamMeta, 0343 SPA_PARAM_Meta, 0344 SPA_PARAM_META_type, 0345 SPA_POD_Id(SPA_META_VideoDamage), 0346 SPA_PARAM_META_size, 0347 SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * videoDamageRegionCount, 0348 sizeof(struct spa_meta_region) * 1, 0349 sizeof(struct spa_meta_region) * videoDamageRegionCount))); 0350 } 0351 0352 pw_stream_update_params(pw->d->pwStream, params.data(), params.count()); 0353 Q_EMIT pw->streamParametersChanged(); 0354 } 0355 0356 static void onProcess(void *data) 0357 { 0358 PipeWireSourceStream *stream = static_cast<PipeWireSourceStream *>(data); 0359 stream->process(); 0360 } 0361 0362 QSize PipeWireSourceStream::size() const 0363 { 0364 return QSize(d->videoFormat.size.width, d->videoFormat.size.height); 0365 } 0366 0367 pw_stream_state PipeWireSourceStream::state() const 0368 { 0369 return d->m_state; 0370 } 0371 0372 std::optional<std::chrono::nanoseconds> PipeWireSourceStream::currentPresentationTimestamp() const 0373 { 0374 return d->m_currentPresentationTimestamp; 0375 } 0376 0377 QString PipeWireSourceStream::error() const 0378 { 0379 return d->m_error; 0380 } 0381 0382 PipeWireSourceStream::PipeWireSourceStream(QObject *parent) 0383 : QObject(parent) 0384 , d(new PipeWireSourceStreamPrivate) 0385 { 0386 pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; 0387 pwStreamEvents.process = &onProcess; 0388 pwStreamEvents.state_changed = &PipeWireSourceStream::onStreamStateChanged; 0389 pwStreamEvents.param_changed = &PipeWireSourceStream::onStreamParamChanged; 0390 } 0391 0392 PipeWireSourceStream::~PipeWireSourceStream() 0393 { 0394 d->m_stopped = true; 0395 if (d->m_renegotiateEvent) { 0396 pw_loop_destroy_source(d->pwCore->loop(), d->m_renegotiateEvent); 0397 } 0398 if (d->pwStream) { 0399 pw_stream_destroy(d->pwStream); 0400 } 0401 } 0402 0403 Fraction PipeWireSourceStream::framerate() const 0404 { 0405 if (d->pwStream) { 0406 return {d->videoFormat.max_framerate.num, d->videoFormat.max_framerate.denom}; 0407 } 0408 0409 return {0, 1}; 0410 } 0411 0412 void PipeWireSourceStream::setMaxFramerate(const Fraction &framerate) 0413 { 0414 d->maxFramerate = framerate; 0415 0416 if (d->pwStream) { 0417 pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent); 0418 } 0419 } 0420 0421 uint PipeWireSourceStream::nodeId() 0422 { 0423 return d->pwNodeId; 0424 } 0425 0426 QList<const spa_pod *> PipeWireSourceStream::createFormatsParams(spa_pod_builder podBuilder) 0427 { 0428 const auto pwServerVersion = d->pwCore->serverVersion(); 0429 const QList<spa_video_format> formats = { 0430 SPA_VIDEO_FORMAT_RGBx, 0431 SPA_VIDEO_FORMAT_RGBA, 0432 SPA_VIDEO_FORMAT_BGRx, 0433 SPA_VIDEO_FORMAT_BGRA, 0434 SPA_VIDEO_FORMAT_RGB, 0435 SPA_VIDEO_FORMAT_BGR, 0436 SPA_VIDEO_FORMAT_xBGR, 0437 SPA_VIDEO_FORMAT_ABGR, 0438 }; 0439 QList<const spa_pod *> params; 0440 params.reserve(formats.size() * 2); 0441 const EGLDisplay display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay")); 0442 0443 d->m_allowDmaBuf = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufMinVersion && pwServerVersion >= kDmaBufMinVersion)); 0444 const bool withDontFixate = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufModifierMinVersion && pwServerVersion >= kDmaBufModifierMinVersion)); 0445 0446 if (d->m_availableModifiers.isEmpty()) { 0447 d->m_availableModifiers = queryDmaBufModifiers(display, formats); 0448 } 0449 0450 for (auto it = d->m_availableModifiers.constBegin(), itEnd = d->m_availableModifiers.constEnd(); it != itEnd; ++it) { 0451 if (d->m_allowDmaBuf && !it->isEmpty()) { 0452 params += buildFormat(&podBuilder, it.key(), it.value(), withDontFixate, d->maxFramerate); 0453 } 0454 0455 params += buildFormat(&podBuilder, it.key(), {}, withDontFixate, d->maxFramerate); 0456 } 0457 return params; 0458 } 0459 0460 bool PipeWireSourceStream::createStream(uint nodeid, int fd) 0461 { 0462 d->m_availableModifiers.clear(); 0463 d->pwCore = PipeWireCore::fetch(fd); 0464 if (!d->pwCore->error().isEmpty()) { 0465 qDebug() << "received error while creating the stream" << d->pwCore->error(); 0466 d->m_error = d->pwCore->error(); 0467 return false; 0468 } 0469 0470 connect(d->pwCore.data(), &PipeWireCore::pipewireFailed, this, &PipeWireSourceStream::coreFailed); 0471 0472 if (objectName().isEmpty()) { 0473 setObjectName(QStringLiteral("plasma-screencast-%1").arg(nodeid)); 0474 } 0475 0476 const auto pwServerVersion = d->pwCore->serverVersion(); 0477 d->pwStream = pw_stream_new(**d->pwCore, objectName().toUtf8().constData(), nullptr); 0478 d->pwNodeId = nodeid; 0479 pw_stream_add_listener(d->pwStream, &d->streamListener, &pwStreamEvents, this); 0480 0481 d->m_renegotiateEvent = pw_loop_add_event(d->pwCore->loop(), onRenegotiate, this); 0482 0483 uint8_t buffer[4096]; 0484 spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); 0485 auto params = createFormatsParams(podBuilder); 0486 pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT); 0487 if (pw_stream_connect(d->pwStream, PW_DIRECTION_INPUT, d->pwNodeId, s, params.data(), params.size()) != 0) { 0488 qWarning() << "Could not connect to stream"; 0489 pw_stream_destroy(d->pwStream); 0490 d->pwStream = nullptr; 0491 return false; 0492 } 0493 qDebug() << "created successfully" << nodeid; 0494 return true; 0495 } 0496 0497 void PipeWireSourceStream::handleFrame(struct pw_buffer *buffer) 0498 { 0499 spa_buffer *spaBuffer = buffer->buffer; 0500 0501 PipeWireFrame frame; 0502 frame.format = d->videoFormat.format; 0503 0504 struct spa_meta_header *h = (struct spa_meta_header *)spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h)); 0505 if (h) { 0506 d->m_currentPresentationTimestamp = std::chrono::nanoseconds(h->pts); 0507 frame.presentationTimestamp = std::chrono::nanoseconds(h->pts); 0508 frame.sequential = h->seq; 0509 } else { 0510 using namespace std::chrono; 0511 auto now = system_clock::now(); 0512 d->m_currentPresentationTimestamp = time_point_cast<nanoseconds>(now).time_since_epoch(); 0513 frame.presentationTimestamp = d->m_currentPresentationTimestamp; 0514 } 0515 0516 if (spa_meta *vd = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) { 0517 frame.damage = QRegion(); 0518 spa_meta_region *mr; 0519 spa_meta_for_each(mr, vd) 0520 { 0521 *frame.damage += QRect(mr->region.position.x, mr->region.position.y, mr->region.size.width, mr->region.size.height); 0522 } 0523 } 0524 0525 { // process cursor 0526 struct spa_meta_cursor *cursor = static_cast<struct spa_meta_cursor *>(spa_buffer_find_meta_data(spaBuffer, SPA_META_Cursor, sizeof(*cursor))); 0527 if (cursor && spa_meta_cursor_is_valid(cursor)) { 0528 struct spa_meta_bitmap *bitmap = nullptr; 0529 0530 if (cursor->bitmap_offset) 0531 bitmap = SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap); 0532 0533 QImage cursorTexture; 0534 if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) { 0535 const uint8_t *bitmap_data = SPA_MEMBER(bitmap, bitmap->offset, uint8_t); 0536 cursorTexture = 0537 SpaBufferToQImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spa_video_format(bitmap->format)); 0538 } 0539 frame.cursor = {{cursor->position.x, cursor->position.y}, {cursor->hotspot.x, cursor->hotspot.y}, cursorTexture}; 0540 } 0541 } 0542 0543 if (spaBuffer->datas->chunk->size == 0 || spaBuffer->datas->chunk->flags == SPA_CHUNK_FLAG_CORRUPTED) { 0544 // do not get a frame 0545 qDebug() << "skipping empty buffer" << spaBuffer->datas->chunk->size << spaBuffer->datas->chunk->flags; 0546 } else if (spaBuffer->datas->type == SPA_DATA_MemFd) { 0547 uint8_t *map = 0548 static_cast<uint8_t *>(mmap(nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset, PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0)); 0549 0550 if (map == MAP_FAILED) { 0551 qWarning() << "Failed to mmap the memory: " << strerror(errno); 0552 return; 0553 } 0554 QImage img = 0555 SpaBufferToQImage(map, d->videoFormat.size.width, d->videoFormat.size.height, spaBuffer->datas->chunk->stride, d->videoFormat.format); 0556 frame.image = img.copy(); 0557 0558 munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset); 0559 } else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) { 0560 DmaBufAttributes attribs; 0561 attribs.planes.reserve(spaBuffer->n_datas); 0562 attribs.format = spaVideoFormatToDrmFormat(d->videoFormat.format); 0563 attribs.modifier = d->videoFormat.modifier; 0564 attribs.width = d->videoFormat.size.width; 0565 attribs.height = d->videoFormat.size.height; 0566 0567 for (uint i = 0; i < spaBuffer->n_datas; ++i) { 0568 const auto &data = spaBuffer->datas[i]; 0569 0570 DmaBufPlane plane; 0571 plane.fd = data.fd; 0572 plane.stride = data.chunk->stride; 0573 plane.offset = data.chunk->offset; 0574 attribs.planes += plane; 0575 } 0576 Q_ASSERT(!attribs.planes.isEmpty()); 0577 frame.dmabuf = attribs; 0578 } else if (spaBuffer->datas->type == SPA_DATA_MemPtr) { 0579 frame.image = SpaBufferToQImage(static_cast<uint8_t *>(spaBuffer->datas->data), 0580 d->videoFormat.size.width, 0581 d->videoFormat.size.height, 0582 spaBuffer->datas->chunk->stride, 0583 d->videoFormat.format); 0584 } else { 0585 if (spaBuffer->datas->type == SPA_ID_INVALID) 0586 qWarning() << "invalid buffer type"; 0587 else 0588 qWarning() << "unsupported buffer type" << spaBuffer->datas->type; 0589 QImage errorImage(200, 200, QImage::Format_ARGB32_Premultiplied); 0590 errorImage.fill(Qt::red); 0591 frame.image = errorImage; 0592 } 0593 0594 Q_EMIT frameReceived(frame); 0595 } 0596 0597 void PipeWireSourceStream::coreFailed(const QString &errorMessage) 0598 { 0599 qDebug() << "received error message" << errorMessage; 0600 d->m_error = errorMessage; 0601 Q_EMIT stopStreaming(); 0602 } 0603 0604 void PipeWireSourceStream::process() 0605 { 0606 pw_buffer *buf = pw_stream_dequeue_buffer(d->pwStream); 0607 if (!buf) { 0608 qDebug() << "out of buffers"; 0609 return; 0610 } 0611 0612 handleFrame(buf); 0613 0614 pw_stream_queue_buffer(d->pwStream, buf); 0615 } 0616 0617 void PipeWireSourceStream::setActive(bool active) 0618 { 0619 Q_ASSERT(d->pwStream); 0620 pw_stream_set_active(d->pwStream, active); 0621 } 0622 0623 void PipeWireSourceStream::setDamageEnabled(bool withDamage) 0624 { 0625 d->m_withDamage = withDamage; 0626 } 0627 0628 bool PipeWireSourceStream::usingDmaBuf() const 0629 { 0630 return d->m_usingDmaBuf; 0631 } 0632 0633 bool PipeWireSourceStream::allowDmaBuf() const 0634 { 0635 return d->m_allowDmaBuf; 0636 } 0637 0638 void PipeWireSourceStream::setAllowDmaBuf(bool allowed) 0639 { 0640 d->m_allowDmaBuf = allowed; 0641 } 0642 0643 #include "moc_pipewiresourcestream.cpp"