6#include <QtMultimedia/private/qcapturablewindow_p.h>
7#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
8#include <QtMultimedia/private/qpipewire_instance_p.h>
9#include <QtMultimedia/private/qvideoframe_p.h>
10#include <QtMultimedia/private/qvideoframeconversionhelper_p.h>
11#include <QtMultimedia/qabstractvideobuffer.h>
12#include <QtGui/private/qdesktopunixservices_p.h>
13#include <QtGui/private/qguiapplication_p.h>
14#include <QtGui/qguiapplication.h>
15#include <QtGui/qpa/qplatformintegration.h>
16#include <QtGui/qscreen.h>
17#include <QtGui/qwindow.h>
18#include <QtCore/private/qcore_unix_p.h>
19#include <QtCore/qdebug.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/qloggingcategory.h>
22#include <QtCore/qmutex.h>
23#include <QtCore/qrandom.h>
24#include <QtCore/qurlquery.h>
25#include <QtCore/quuid.h>
26#include <QtCore/qvariantmap.h>
27#include <QtDBus/qdbusconnection.h>
28#include <QtDBus/qdbusinterface.h>
29#include <QtDBus/qdbusmessage.h>
30#include <QtDBus/qdbuspendingcall.h>
31#include <QtDBus/qdbuspendingreply.h>
32#include <QtDBus/qdbusreply.h>
33#include <QtDBus/qdbusunixfiledescriptor.h>
39QT_WARNING_DISABLE_CLANG(
"-Wunused-value")
43using namespace Qt::StringLiterals;
53 QDBusConnection bus = QDBusConnection::sessionBus();
54 QDBusInterface *interface =
new QDBusInterface(
55 u"org.freedesktop.portal.Desktop"_s, u"/org/freedesktop/portal/desktop"_s,
56 u"org.freedesktop.DBus.Properties"_s, bus, qGuiApp);
59 args << u"org.freedesktop.portal.ScreenCast"_s << u"version"_s;
61 QDBusMessage reply = interface->callWithArgumentList(QDBus::Block, u"Get"_s, args);
62 qCDebug(qLcPipeWireCapture) <<
"v1=" << reply.type()
63 <<
"v2=" << reply.arguments().size()
64 <<
"v3=" << reply.arguments().at(0).toUInt();
65 if (reply.type() == QDBusMessage::ReplyMessage
66 && reply.arguments().size() == 1
71 qCDebug(qLcPipeWireCapture) << Q_FUNC_INFO <<
"hasScreenCastPortal=" << hasScreenCastPortal;
82 if (active && m_state == NoState)
84 if (!active && m_state == Streaming)
90 updateError(QPlatformSurfaceCapture::InternalError,
91 u"There is no ScreenCast service available in org.freedesktop.portal!"_s);
97 const QString &description)
99 m_capture.updateError(error, description);
111 : m_capture(capture),
112 m_requestTokenPrefix(QUuid::createUuid().toString(QUuid::WithoutBraces).left(8))
118 if (m_state != NoState)
124 return m_videoFrameFormat;
132 return globalState->hasScreenCastPortal;
138 qCDebug(qLcPipeWireCapture) << Q_FUNC_INFO <<
"result=" << result <<
"map=" << map;
140 m_operationState = NoOperation;
141 qWarning() <<
"Failed to capture screen via pipewire, perhaps because user cancelled the operation.";
146 switch (m_operationState) {
148 selectSources(map[u"session_handle"_s].toString());
154 updateStreams(map[u"streams"_s].value<QDBusArgument>());
155 openPipeWireRemote();
156 m_operationState = NoOperation;
159 case OpenPipeWireRemote:
160 m_operationState = NoOperation;
169 if (m_requestToken <= 0)
170 m_requestToken = generateRequestToken();
171 return u"u%1%2"_s.arg(m_requestTokenPrefix).arg(m_requestToken);
176 return QRandomGenerator::global()->bounded(1, 25600);
183 if (!globalState->hasScreenCastPortal)
186 m_operationState = NoOperation;
188 if (!m_screenCastInterface) {
189 m_screenCastInterface = std::make_unique<QDBusInterface>(
190 u"org.freedesktop.portal.Desktop"_s, u"/org/freedesktop/portal/desktop"_s,
191 u"org.freedesktop.portal.ScreenCast"_s, QDBusConnection::sessionBus());
192 bool ok = m_screenCastInterface->connection().connect(
193 u"org.freedesktop.portal.Desktop"_s, u""_s, u"org.freedesktop.portal.Request"_s,
194 u"Response"_s,
this, SLOT(gotRequestResponse(uint,QVariantMap)));
198 QPlatformSurfaceCapture::InternalError,
199 u"Failed to connect to org.freedesktop.portal.ScreenCast dbus interface."_s);
208 if (!m_screenCastInterface)
213 { u"session_handle_token"_s, getRequestToken() },
215 QDBusMessage reply = m_screenCastInterface->call(u"CreateSession"_s, options);
216 if (!reply.errorMessage().isEmpty()) {
217 updateError(QPlatformSurfaceCapture::InternalError,
218 u"Failed to create session for org.freedesktop.portal.ScreenCast. Error: "_s
219 + reply.errorName() + u": "_s + reply.errorMessage());
223 m_operationState = CreateSession;
228 if (!m_screenCastInterface)
231 m_sessionHandle = sessionHandle;
233 { u"handle_token"_s, getRequestToken() },
234 { u"types"_s, (uint)1 },
235 { u"multiple"_s,
false },
236 { u"cursor_mode"_s, (uint)1 },
237 { u"persist_mode"_s, (uint)0 },
239 QDBusMessage reply = m_screenCastInterface->call(u"SelectSources"_s,
240 QDBusObjectPath(sessionHandle), options);
241 if (!reply.errorMessage().isEmpty()) {
242 updateError(QPlatformSurfaceCapture::InternalError,
243 u"Failed to select sources for org.freedesktop.portal.ScreenCast. Error: "_s
244 + reply.errorName() + u": "_s + reply.errorMessage());
248 m_operationState = SelectSources;
253 if (!m_screenCastInterface)
257 { u"handle_token"_s, getRequestToken() },
260 const auto unixServices =
dynamic_cast<QDesktopUnixServices *>(QGuiApplicationPrivate::platformIntegration()->services());
261 const QString parentWindow = QGuiApplication::focusWindow() && unixServices
262 ? unixServices->portalWindowIdentifier(QGuiApplication::focusWindow())
264 QDBusMessage reply = m_screenCastInterface->call(
"Start"_L1, QDBusObjectPath(m_sessionHandle),
265 parentWindow, options);
266 if (!reply.errorMessage().isEmpty()) {
267 updateError(QPlatformSurfaceCapture::InternalError,
268 u"Failed to start stream for org.freedesktop.portal.ScreenCast. Error: "_s
269 + reply.errorName() + u": "_s + reply.errorMessage());
273 m_operationState = StartStream;
280 streamsInfo.beginStructure();
281 streamsInfo.beginArray();
283 while (!streamsInfo.atEnd()) {
285 streamsInfo >> nodeId;
286 QMap<QString, QVariant> properties;
287 streamsInfo >> properties;
291 if (properties.contains(u"position"_s)) {
292 const QDBusArgument position = properties[u"position"_s].value<QDBusArgument>();
293 position.beginStructure();
296 position.endStructure();
301 if (properties.contains(u"size"_s)) {
302 const QDBusArgument size = properties[u"size"_s].value<QDBusArgument>();
303 size.beginStructure();
310 if (properties.contains(u"source_type"_s))
311 sourceType = properties[u"source_type"_s].toUInt();
313 StreamInfo streamInfo;
314 streamInfo.nodeId = nodeId;
315 streamInfo.sourceType = sourceType;
316 streamInfo.rect = {x, y, width, height};
317 m_streams << streamInfo;
320 streamsInfo.endArray();
321 streamsInfo.endStructure();
327 if (!m_screenCastInterface)
331 QDBusReply<QDBusUnixFileDescriptor> reply = m_screenCastInterface->call(
332 u"OpenPipeWireRemote"_s, QDBusObjectPath(m_sessionHandle), options);
333 if (!reply.isValid()) {
335 QPlatformSurfaceCapture::InternalError,
336 u"Failed to open pipewire remote for org.freedesktop.portal.ScreenCast. Error: name="_s
337 + reply.error().name() + u", message="_s + reply.error().message());
341 m_pipewireFd = reply.value().fileDescriptor();
342 bool ok =
open(m_pipewireFd
);
343 qCDebug(qLcPipeWireCapture) <<
"open(" << m_pipewireFd <<
") result=" << ok;
345 updateError(QPlatformSurfaceCapture::InternalError,
346 u"Failed to open pipewire remote file descriptor"_s);
350 m_operationState = OpenPipeWireRemote;
357 LoopLocker(pw_thread_loop *threadLoop)
358 : m_threadLoop(threadLoop) {
367 pw_thread_loop_lock(m_threadLoop);
372 pw_thread_loop_unlock(m_threadLoop);
373 m_threadLoop =
nullptr;
378 pw_thread_loop *m_threadLoop =
nullptr;
384 if (m_streams.isEmpty())
391 m_instance = QPipeWireInstance::instance();
393 static const pw_core_events coreEvents = {
394 .version = PW_VERSION_CORE_EVENTS,
395 .info = [](
void *data,
const struct pw_core_info *info) {
399 .done = [](
void *object, uint32_t id,
int seq) {
402 .ping = [](
void *data, uint32_t id,
int seq) {
407 .error = [](
void *data, uint32_t id,
int seq,
int res,
const char *message) {
414 .remove_id = [](
void *data, uint32_t id) {
418 .bound_id = [](
void *data, uint32_t id, uint32_t global_id) {
423 .add_mem = [](
void *data, uint32_t id, uint32_t type,
int fd, uint32_t flags) {
430 .remove_mem = [](
void *data, uint32_t id) {
434#if defined(PW_CORE_EVENT_BOUND_PROPS)
435 .bound_props = [](
void *data, uint32_t id, uint32_t global_id,
const struct spa_dict *props) {
444 static const pw_registry_events registryEvents = {
445 .version = PW_VERSION_REGISTRY_EVENTS,
446 .global = [](
void *object, uint32_t id, uint32_t permissions,
const char *type, uint32_t version,
const spa_dict *props) {
447 reinterpret_cast<QPipeWireCaptureHelper *>(object)->onRegistryEventGlobal(id, permissions, type, version, props);
449 .global_remove = [](
void *data, uint32_t id) {
455 m_threadLoop = PwThreadLoopHandle{
456 pw_thread_loop_new(
"qt-multimedia-pipewire-loop",
nullptr),
460 updateError(QPlatformSurfaceCapture::InternalError,
461 u"QPipeWireCaptureHelper failed at pw_thread_loop_new()."_s);
465 m_context = PwContextHandle{
466 pw_context_new(pw_thread_loop_get_loop(m_threadLoop.get()),
nullptr, 0),
470 updateError(QPlatformSurfaceCapture::InternalError,
471 u"QPipeWireCaptureHelper failed at pw_context_new()."_s);
475 m_core = PwCoreConnectionHandle{
476 pw_context_connect_fd(m_context.get(), fcntl(pipewireFd, F_DUPFD_CLOEXEC, 5),
nullptr, 0),
480 updateError(QPlatformSurfaceCapture::InternalError,
481 u"QPipeWireCaptureHelper failed at pw_context_connect_fd()."_s);
485 pw_core_add_listener(m_core.get(), &m_coreListener, &coreEvents,
this);
487 m_registry = PwRegistryHandle{
488 pw_core_get_registry(m_core.get(), PW_VERSION_REGISTRY, 0),
492 updateError(QPlatformSurfaceCapture::InternalError,
493 u"QPipeWireCaptureHelper failed at pw_core_get_registry()."_s);
496 pw_registry_add_listener(m_registry.get(), &m_registryListener, ®istryEvents,
this);
500 if (pw_thread_loop_start(m_threadLoop.get()) != 0) {
502 updateError(QPlatformSurfaceCapture::InternalError,
503 u"QPipeWireCaptureHelper failed at pw_thread_loop_start()."_s);
507 LoopLocker locker(m_threadLoop.get());
508 while (!m_initDone) {
509 if (pw_thread_loop_timed_wait(m_threadLoop.get(), 2) != 0)
513 return m_initDone && m_hasSource;
518 m_coreInitSeq = pw_core_sync(m_core.get(), PW_ID_CORE, m_coreInitSeq);
523 if (id == PW_ID_CORE && seq == m_coreInitSeq) {
524 spa_hook_remove(&m_registryListener);
525 spa_hook_remove(&m_coreListener);
528 pw_thread_loop_signal(m_threadLoop.get(),
false);
532void QPipeWireCaptureHelper::onRegistryEventGlobal(uint32_t id, uint32_t permissions,
const char *type, uint32_t version,
const spa_dict *props)
535 Q_UNUSED(permissions)
538 if (qstrcmp(type, PW_TYPE_INTERFACE_Node) != 0)
541 auto media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
545 if (qstrcmp(media_class,
"Stream/Output/Video") != 0
546 && qstrcmp(media_class,
"Video/Source") != 0)
558 static const pw_stream_events streamEvents = {
559 .version = PW_VERSION_STREAM_EVENTS,
560 .destroy = [](
void *data) {
563 .state_changed = [](
void *data, pw_stream_state old, pw_stream_state state,
const char *error) {
566 .control_info = [](
void *data, uint32_t id,
const struct pw_stream_control *control) {
571 .io_changed = [](
void *data, uint32_t id,
void *area, uint32_t size) {
577 .param_changed = [](
void *data, uint32_t id,
const struct spa_pod *param) {
580 .add_buffer = [](
void *data,
struct pw_buffer *buffer) {
584 .remove_buffer = [](
void *data,
struct pw_buffer *buffer) {
588 .process = [](
void *data) {
591 .drained = [](
void *data) {
594#if PW_VERSION_STREAM_EVENTS
>= 1
595 .command = [](
void *data,
const struct spa_command *command) {
600#if PW_VERSION_STREAM_EVENTS
>= 2
601 .trigger_done = [](
void *data) {
609 auto streamInfo = m_streams[0];
610 struct spa_dict_item items[4];
611 struct spa_dict info;
612 items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_TYPE,
"Video");
613 items[1] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_CATEGORY,
"Capture");
614 items[2] = SPA_DICT_ITEM_INIT(PW_KEY_MEDIA_ROLE,
"Screen");
615 info = SPA_DICT_INIT(items, 3);
616 auto props = pw_properties_new_dict(&info);
618 LoopLocker locker(m_threadLoop.get());
620 m_stream = PwStreamHandle{
621 pw_stream_new(m_core.get(),
"video-capture", props),
626 updateError(QPlatformSurfaceCapture::InternalError,
627 u"QPipeWireCaptureHelper failed at pw_stream_new()."_s);
631 m_streamListener = {};
632 pw_stream_add_listener(m_stream.get(), &m_streamListener, &streamEvents,
this);
636 QT_WARNING_DISABLE_GCC(
"-Wmissing-field-initializers")
637 QT_WARNING_DISABLE_CLANG(
"-Wmissing-field-initializers")
639 uint8_t buffer[4096];
640 struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
641 const struct spa_pod *params[1];
642 struct spa_rectangle defsize = SPA_RECTANGLE(quint32(streamInfo.rect.width()), quint32(streamInfo.rect.height()));
643 struct spa_rectangle maxsize = SPA_RECTANGLE(4096, 4096);
644 struct spa_rectangle minsize = SPA_RECTANGLE(1,1);
645 struct spa_fraction defrate = SPA_FRACTION(25, 1);
646 struct spa_fraction maxrate = SPA_FRACTION(1000, 1);
647 struct spa_fraction minrate = SPA_FRACTION(0, 1);
649 params[0] =
static_cast<
const spa_pod*>(spa_pod_builder_add_object(
651 SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
652 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
653 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
654 SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(6,
655 SPA_VIDEO_FORMAT_RGB,
656 SPA_VIDEO_FORMAT_BGR,
657 SPA_VIDEO_FORMAT_RGBA,
658 SPA_VIDEO_FORMAT_BGRA,
659 SPA_VIDEO_FORMAT_RGBx,
660 SPA_VIDEO_FORMAT_BGRx),
661 SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(
662 &defsize, &minsize, &maxsize),
663 SPA_FORMAT_VIDEO_framerate, SPA_POD_CHOICE_RANGE_Fraction(
664 &defrate, &minrate, &maxrate))
668 const int connectErr = pw_stream_connect(
669 m_stream.get(), PW_DIRECTION_INPUT, streamInfo.nodeId,
670 static_cast<pw_stream_flags>(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS),
672 if (connectErr != 0) {
675 updateError(QPlatformSurfaceCapture::InternalError,
676 u"QPipeWireCaptureHelper failed at pw_stream_connect()."_s);
686 LoopLocker locker(m_threadLoop.get());
687 while (!m_streamPaused && !m_silence && !m_err) {
688 if (pw_thread_loop_timed_wait(m_threadLoop.get(), 1) != 0)
693 LoopLocker locker(m_threadLoop.get());
694 m_ignoreStateChange =
true;
695 pw_stream_disconnect(m_stream.get());
697 m_ignoreStateChange =
false;
709 pw_thread_loop_signal(m_threadLoop.get(),
false);
717 if (m_ignoreStateChange)
722 case PW_STREAM_STATE_UNCONNECTED:
723 signalLoop(
false,
true);
725 case PW_STREAM_STATE_PAUSED:
726 m_streamPaused =
true;
727 signalLoop(
false,
false);
729 case PW_STREAM_STATE_STREAMING:
730 m_streamPaused =
false;
731 signalLoop(
false,
false);
740 struct spa_buffer *buf;
745 if ((b = pw_stream_dequeue_buffer(m_stream.get())) ==
nullptr) {
746 updateError(QPlatformSurfaceCapture::InternalError,
747 u"Out of buffers in pipewire stream dequeue."_s);
752 if ((sdata = buf->datas[0].data) ==
nullptr)
755 sstride = buf->datas[0].chunk->stride;
757 sstride = buf->datas[0].chunk->size / m_size.height();
758 size = buf->datas[0].chunk->size;
760 if (m_videoFrameFormat.frameSize() != m_size || m_videoFrameFormat.pixelFormat() != m_pixelFormat)
761 m_videoFrameFormat = QVideoFrameFormat(m_size, m_pixelFormat);
763 m_currentFrame = QVideoFramePrivate::createFrame(
764 std::make_unique<QMemoryVideoBuffer>(QByteArray(
static_cast<
const char *>(sdata), size), sstride),
766 emit m_capture.newVideoFrame(m_currentFrame);
767 qCDebug(qLcPipeWireCaptureMore) <<
"got a frame of size " << buf->datas[0].chunk->size;
769 pw_stream_queue_buffer(m_stream.get(), b);
771 signalLoop(
true,
false);
779 destroyStream(
false);
781 pw_thread_loop_stop(m_threadLoop.get());
793 if (param ==
nullptr || id != SPA_PARAM_Format)
796 if (spa_format_parse(param,
797 &m_format.media_type,
798 &m_format.media_subtype) < 0)
801 if (m_format.media_type != SPA_MEDIA_TYPE_video
802 || m_format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
805 if (spa_format_video_raw_parse(param, &m_format.info.raw) < 0)
808 qCDebug(qLcPipeWireCapture) <<
"got video format:";
809 qCDebug(qLcPipeWireCapture) <<
" format: " << m_format.info.raw.format
810 <<
" (" << spa_debug_type_find_name(spa_type_video_format, m_format.info.raw.format) <<
")";
811 qCDebug(qLcPipeWireCapture) <<
" size: " << m_format.info.raw.size.width
812 <<
" x " << m_format.info.raw.size.height;
813 qCDebug(qLcPipeWireCapture) <<
" framerate: " << m_format.info.raw.framerate.num
814 <<
" / " << m_format.info.raw.framerate.denom;
816 m_size = QSize(m_format.info.raw.size.width, m_format.info.raw.size.height);
817 m_pixelFormat = QPipeWireCaptureHelper::toQtPixelFormat(m_format.info.raw.format);
818 qCDebug(qLcPipeWireCapture) <<
"m_pixelFormat=" << m_pixelFormat;
826 switch (spaVideoFormat) {
829 case SPA_VIDEO_FORMAT_I420:
830 return QVideoFrameFormat::Format_YUV420P;
831 case SPA_VIDEO_FORMAT_Y42B:
832 return QVideoFrameFormat::Format_YUV422P;
833 case SPA_VIDEO_FORMAT_YV12:
834 return QVideoFrameFormat::Format_YV12;
835 case SPA_VIDEO_FORMAT_UYVY:
836 return QVideoFrameFormat::Format_UYVY;
837 case SPA_VIDEO_FORMAT_YUY2:
838 return QVideoFrameFormat::Format_YUYV;
839 case SPA_VIDEO_FORMAT_NV12:
840 return QVideoFrameFormat::Format_NV12;
841 case SPA_VIDEO_FORMAT_NV21:
842 return QVideoFrameFormat::Format_NV21;
843 case SPA_VIDEO_FORMAT_AYUV:
844 return QVideoFrameFormat::Format_AYUV;
845 case SPA_VIDEO_FORMAT_GRAY8:
846 return QVideoFrameFormat::Format_Y8;
847 case SPA_VIDEO_FORMAT_xRGB:
848 return QVideoFrameFormat::Format_XRGB8888;
849 case SPA_VIDEO_FORMAT_xBGR:
850 return QVideoFrameFormat::Format_XBGR8888;
851 case SPA_VIDEO_FORMAT_RGBx:
852 return QVideoFrameFormat::Format_RGBX8888;
853 case SPA_VIDEO_FORMAT_BGRx:
854 return QVideoFrameFormat::Format_BGRX8888;
855 case SPA_VIDEO_FORMAT_ARGB:
856 return QVideoFrameFormat::Format_ARGB8888;
857 case SPA_VIDEO_FORMAT_ABGR:
858 return QVideoFrameFormat::Format_ABGR8888;
859 case SPA_VIDEO_FORMAT_RGBA:
860 return QVideoFrameFormat::Format_RGBA8888;
861 case SPA_VIDEO_FORMAT_BGRA:
862 return QVideoFrameFormat::Format_BGRA8888;
863#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
864 case SPA_VIDEO_FORMAT_GRAY16_LE:
865 return QVideoFrameFormat::Format_Y16;
866 case SPA_VIDEO_FORMAT_P010_10LE:
867 return QVideoFrameFormat::Format_P010;
869 case SPA_VIDEO_FORMAT_GRAY16_BE:
870 return QVideoFrameFormat::Format_Y16;
871 case SPA_VIDEO_FORMAT_P010_10BE:
872 return QVideoFrameFormat::Format_P010;
876 return QVideoFrameFormat::Format_Invalid;
881 switch (pixelFormat) {
884 case QVideoFrameFormat::Format_YUV420P:
885 return SPA_VIDEO_FORMAT_I420;
886 case QVideoFrameFormat::Format_YUV422P:
887 return SPA_VIDEO_FORMAT_Y42B;
888 case QVideoFrameFormat::Format_YV12:
889 return SPA_VIDEO_FORMAT_YV12;
890 case QVideoFrameFormat::Format_UYVY:
891 return SPA_VIDEO_FORMAT_UYVY;
892 case QVideoFrameFormat::Format_YUYV:
893 return SPA_VIDEO_FORMAT_YUY2;
894 case QVideoFrameFormat::Format_NV12:
895 return SPA_VIDEO_FORMAT_NV12;
896 case QVideoFrameFormat::Format_NV21:
897 return SPA_VIDEO_FORMAT_NV21;
898 case QVideoFrameFormat::Format_AYUV:
899 return SPA_VIDEO_FORMAT_AYUV;
900 case QVideoFrameFormat::Format_Y8:
901 return SPA_VIDEO_FORMAT_GRAY8;
902 case QVideoFrameFormat::Format_XRGB8888:
903 return SPA_VIDEO_FORMAT_xRGB;
904 case QVideoFrameFormat::Format_XBGR8888:
905 return SPA_VIDEO_FORMAT_xBGR;
906 case QVideoFrameFormat::Format_RGBX8888:
907 return SPA_VIDEO_FORMAT_RGBx;
908 case QVideoFrameFormat::Format_BGRX8888:
909 return SPA_VIDEO_FORMAT_BGRx;
910 case QVideoFrameFormat::Format_ARGB8888:
911 return SPA_VIDEO_FORMAT_ARGB;
912 case QVideoFrameFormat::Format_ABGR8888:
913 return SPA_VIDEO_FORMAT_ABGR;
914 case QVideoFrameFormat::Format_RGBA8888:
915 return SPA_VIDEO_FORMAT_RGBA;
916 case QVideoFrameFormat::Format_BGRA8888:
917 return SPA_VIDEO_FORMAT_BGRA;
918#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
919 case QVideoFrameFormat::Format_Y16:
920 return SPA_VIDEO_FORMAT_GRAY16_LE;
921 case QVideoFrameFormat::Format_P010:
922 return SPA_VIDEO_FORMAT_P010_10LE;
924 case QVideoFrameFormat::Format_Y16:
925 return SPA_VIDEO_FORMAT_GRAY16_BE;
926 case QVideoFrameFormat::Format_P010:
927 return SPA_VIDEO_FORMAT_P010_10BE;
931 return SPA_VIDEO_FORMAT_UNKNOWN;
bool setActiveInternal(bool active)
static bool isSupported()
QVideoFrameFormat frameFormat() const
void updateError(QPlatformSurfaceCapture::Error error, const QString &description={})
~QPipeWireCaptureHelper() override
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
PipeWireCaptureGlobalState()