Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpipewire_cameraportalclient.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtMultimedia/private/qpipewire_portal_p.h>
7#include <QtCore/qeventloop.h>
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/qscopeguard.h>
10#include <QtCore/qtimer.h>
11#include <QtCore/qthread.h>
12#include <QtCore/quuid.h>
13#include <QtDBus/qdbusconnection.h>
14#include <QtDBus/qdbusinterface.h>
15#include <QtDBus/qdbusreply.h>
16#include <QtDBus/qdbusunixfiledescriptor.h>
17
18#include <memory>
19
20QT_BEGIN_NAMESPACE
21
22namespace QtPipeWire {
23
24using namespace Qt::StringLiterals;
25
26Q_STATIC_LOGGING_CATEGORY(lcCameraPortal, "qt.multimedia.pipewire.camera.portal")
27
28namespace {
29
30class CameraPortalWorker : public QObject
31{
32 Q_OBJECT
33
34public:
35 q23::expected<QDBusUnixFileDescriptor, QString> run(std::chrono::milliseconds timeout)
36 {
37 const QString connectionName = u"qt_pipewire_camera_portal_%1"_s.arg(
38 QUuid::createUuid().toString(QUuid::WithoutBraces));
39 QDBusConnection bus =
40 QDBusConnection::connectToBus(QDBusConnection::SessionBus, connectionName);
41 auto disconnectFromBus = qScopeGuard([&] {
42 QDBusConnection::disconnectFromBus(connectionName);
43 });
44 if (!bus.isConnected())
45 return q23::unexpected(u"could not open a private session bus connection"_s);
46
47 const QString token = allocatePortalRequestToken();
48 const QString requestPath = portalRequestPath(bus, token);
49
50 if (!bus.connect(u"org.freedesktop.portal.Desktop"_s, requestPath,
51 u"org.freedesktop.portal.Request"_s, u"Response"_s, this,
52 SLOT(onResponse(uint, QVariantMap)))) {
53 return q23::unexpected(u"could not subscribe to the portal Request response"_s);
54 }
55 auto disconnectBus = qScopeGuard([&] {
56 bus.disconnect(u"org.freedesktop.portal.Desktop"_s, requestPath,
57 u"org.freedesktop.portal.Request"_s, u"Response"_s, this,
58 SLOT(onResponse(uint, QVariantMap)));
59 });
60
61 QDBusInterface cameraInterface(u"org.freedesktop.portal.Desktop"_s,
62 u"/org/freedesktop/portal/desktop"_s,
63 u"org.freedesktop.portal.Camera"_s, bus);
64 QDBusReply<QDBusObjectPath> accessReply =
65 cameraInterface.call(u"AccessCamera"_s,
66 QVariantMap{
67 { u"handle_token"_s, token },
68 });
69 if (!accessReply.isValid()) {
70 return q23::unexpected(u"AccessCamera failed: "_s + accessReply.error().message());
71 }
72
73 QEventLoop loop;
74 m_loop = &loop;
75 QTimer::singleShot(timeout, &loop, &QEventLoop::quit);
76 loop.exec();
77 m_loop = nullptr;
78
79 if (!m_gotResponse)
80 return q23::unexpected(u"timed out waiting for the camera access request"_s);
81 if (m_response != 0)
82 return q23::unexpected(
83 u"camera access request was not granted (response=%1)"_s.arg(m_response));
84
85 QDBusReply<QDBusUnixFileDescriptor> fdReply =
86 cameraInterface.call(u"OpenPipeWireRemote"_s, QVariantMap{});
87 if (!fdReply.isValid())
88 return q23::unexpected(u"OpenPipeWireRemote failed: "_s + fdReply.error().message());
89
90 return fdReply.value();
91 }
92
93private Q_SLOTS:
94 void onResponse(uint response, const QVariantMap &)
95 {
96 m_response = response;
97 m_gotResponse = true;
98 if (m_loop)
99 m_loop->quit();
100 }
101
102private:
103 QEventLoop *m_loop = nullptr;
104 uint m_response = 1;
105 bool m_gotResponse = false;
106};
107
108} // namespace
109
111requestCameraPortalFd(std::chrono::milliseconds timeout)
112{
113 // marshall request through a different thread so that we can block the calling thread until
114 // the portal request has completed
115 QThread thread;
116 thread.start();
117
118 auto worker = std::make_unique<CameraPortalWorker>();
119 worker->moveToThread(&thread);
120
121 q23::expected<QDBusUnixFileDescriptor, QString> result =
122 q23::unexpected(u"the camera portal worker did not run"_s);
123 QMetaObject::invokeMethod(worker.get(), [&] {
124 result = worker->run(timeout);
125 }, Qt::BlockingQueuedConnection);
126
127 thread.quit();
128 thread.wait();
129
130 if (!result)
131 qCDebug(lcCameraPortal) << "requestCameraPortalFd failed:" << result.error();
132
133 return result;
134}
135
136} // namespace QtPipeWire
137
138QT_END_NAMESPACE
139
140#include "qpipewire_cameraportalclient.moc"
Q_STATIC_LOGGING_CATEGORY(lcPipewireAudioSink, "qt.multimedia.pipewire.audiosink")
q23::expected< QDBusUnixFileDescriptor, QString > requestCameraPortalFd(std::chrono::milliseconds timeout)