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_instance.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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_propertydict_p.h>
7#include <QtMultimedia/private/qpipewire_support_p.h>
8#include <QtMultimedia/private/qtmultimediaglobal_p.h>
9#if QT_CONFIG(pipewire_symbolloader)
10# include <QtMultimedia/private/qpipewire_symbolloader_p.h>
11#endif
12
13#if QT_CONFIG(pipewire_screencapture)
14# include <QtDBus/qdbusinterface.h>
15# include <QtDBus/qdbusmessage.h>
16#endif
17
18#include <QtCore/qcoreapplication.h>
19#include <QtCore/qmutex.h>
20
21#include <spa/utils/names.h>
22
23#include <array>
24#include <mutex>
25
26QT_BEGIN_NAMESPACE
27
28namespace QtPipeWire {
29
30using namespace Qt::Literals;
31
32Q_LOGGING_CATEGORY(lcPipewire, "qt.multimedia.pipewire");
33
34namespace {
35
36struct InstanceHolder
37{
38 QMutex mutex;
39 std::weak_ptr<QPipeWireInstance> instance;
40};
41
42Q_GLOBAL_STATIC(InstanceHolder, s_pipeWireInstance);
43
44} // namespace
45
47{
48 pw_init(nullptr, nullptr);
49
50 qCDebug(lcPipewire) << "PipeWire initialized: compiled against" << pw_get_headers_version()
51 << " running " << pw_get_library_version();
52}
53
55{
56 if (pw_check_library_version(0, 3, 49))
57 pw_deinit();
58}
59
60std::shared_ptr<QPipeWireInstance> QPipeWireInstance::instance()
61{
62 std::lock_guard guard{ s_pipeWireInstance->mutex };
63 std::shared_ptr<QPipeWireInstance> ret = s_pipeWireInstance->instance.lock();
64 if (!ret) {
65 ret = QPipeWireInstance::create();
66 if (ret)
67 s_pipeWireInstance->instance = ret;
68 }
69 return ret;
70}
71
73{
74#if QT_CONFIG(pipewire_symbolloader)
75 return qPipewireIsLoaded();
76#else
77 return 1;
78#endif
79}
80
81q23::expected<void, std::error_code> QPipeWireInstance::hasSPAFactory(const char *factoryName)
82{
83 std::array<spa_support, 32> support{};
84 uint32_t n_support = pw_get_support(support.data(), uint32_t(support.size()));
85
86 SpaHandleHandle handle{
87 pw_load_spa_handle(/*lib=*/nullptr, factoryName, /*info=*/nullptr, n_support,
88 support.data()),
89 };
90 if (!handle)
91 return q23::unexpected{ make_error_code() };
92
93 return {};
94}
95
97{
98 return m_eventLoop.isInThread();
99}
100
102{
103 return m_eventLoop.loop();
104}
105
106#if QT_CONFIG(pipewire_screencapture)
108{
109 QDBusInterface properties(u"org.freedesktop.portal.Desktop"_s,
110 u"/org/freedesktop/portal/desktop"_s,
111 u"org.freedesktop.DBus.Properties"_s, QDBusConnection::sessionBus());
112
113 QDBusMessage reply = properties.call(u"Get"_s, interface, u"version"_s);
114
115 if (reply.type() != QDBusMessage::ReplyMessage || reply.arguments().size() != 1) {
116 qCDebug(lcPipewire) << interface << "not available:" << reply.errorName()
117 << reply.errorMessage();
118 return std::nullopt;
119 }
120
121 // The property is a variant, which arrives wrapped in a QDBusVariant.
122 const uint version = reply.arguments().at(0).toUInt();
123 qCDebug(lcPipewire) << interface << "version" << version;
124 return version;
125}
126#endif
127
129{
130#if QT_CONFIG(pipewire_screencapture)
131 if (!m_hasScreenCastPortal)
132 m_hasScreenCastPortal =
133 portalInterfaceVersion(u"org.freedesktop.portal.ScreenCast"_s).has_value();
134 return *m_hasScreenCastPortal;
135#else
136 return false;
137#endif
138}
139
141{
142 auto connection = PwCoreConnectionHandle{
143 pw_context_connect(m_context.get(), /*props=*/nullptr,
144 /*user_data_size=*/0),
145 };
146
147 if (!connection)
148 return q23::unexpected(make_error_code(errno));
149 return connection;
150}
151
153{
154 auto connection = PwCoreConnectionHandle{
155 pw_context_connect_fd(m_context.get(), fd, /*props=*/nullptr,
156 /*user_data_size=*/0),
157 };
158
159 if (!connection)
160 return q23::unexpected(make_error_code(errno));
161 return connection;
162}
163
164std::unique_ptr<QPipeWireInstance> QPipeWireInstance::create()
165{
166 auto instance = std::unique_ptr<QPipeWireInstance>(new QPipeWireInstance);
167 if (!instance->m_eventLoop || !instance->m_context)
168 return nullptr;
169
170 return instance;
171}
172
173QPipeWireInstance::QPipeWireInstance()
174{
175 if (!m_eventLoop)
176 return;
177
178 int status = m_eventLoop.start();
179 if (status < 0)
180 qFatal() << "Failed to start event loop" << make_error_code(-status).message();
181
182 m_context = runWithEventLoopLock([&] {
183 auto applicationName = qApp->applicationName().toUtf8();
184 PwPropertiesHandle props = makeProperties({
185 { PW_KEY_APP_NAME, applicationName.data() },
186 });
187
188 return PwContextHandle{
189 pw_context_new(m_eventLoop.loop(), props.release(), /*user_data_size=*/0),
190 };
191 });
192}
193
195{
196 runWithEventLoopLock([&] { m_context = {}; });
197 m_eventLoop.stop();
198}
199
200} // namespace QtPipeWire
201
202QT_END_NAMESPACE
q23::expected< PwCoreConnectionHandle, std::error_code > connectToPortal(int fd)
q23::expected< PwCoreConnectionHandle, std::error_code > connectToDaemon()
Q_LOGGING_CATEGORY(lcPipewire, "qt.multimedia.pipewire")
Q_DECLARE_LOGGING_CATEGORY(lcPipewire)