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_cameracontextmanager.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
4
#
include
"qpipewire_cameracontextmanager_p.h"
5
6
#
include
<
QtMultimedia
/
private
/
qpipewire_async_support_p
.
h
>
7
#
include
<
QtMultimedia
/
private
/
qpipewire_instance_p
.
h
>
8
#
include
<
QtCore
/
qapplicationstatic
.
h
>
9
#
include
<
QtCore
/
qloggingcategory
.
h
>
10
11
#
if
QT_CONFIG
(
dbus
)
12
#
include
<
QtMultimedia
/
private
/
qpipewire_cameraportalclient_p
.
h
>
13
#
include
<
QtMultimedia
/
private
/
qpipewire_portal_p
.
h
>
14
#
include
<
QtDBus
/
qdbusunixfiledescriptor
.
h
>
15
#
endif
16
17
#
include
<
pipewire
/
pipewire
.
h
>
18
19
#
if
!
PW_CHECK_VERSION
(
0
,
3
,
75
)
20
extern
"C"
{
21
bool
pw_check_library_version(
int
major,
int
minor,
int
micro);
22
}
23
#
endif
24
25
QT_BEGIN_NAMESPACE
26
27
namespace
QtPipeWire
{
28
29
Q_APPLICATION_STATIC(
QCameraContextManager
,
s_cameraContextInstance
)
30
Q_STATIC_LOGGING_CATEGORY
(
lcPipewireCameraRegistry
,
"qt.multimedia.pipewire.camera.registry"
)
31
32
QCameraContextManager
::
QCameraContextManager
()
33
:
m_pwInstance
{
34
QPipeWireInstance
::
instance
(),
35
},
36
m_deviceMonitor
{
37
std
::
make_unique
<
QCameraDeviceMonitor
>(),
38
}
39
{
40
static
const
bool
forcePortal
=
41
qEnvironmentVariableIntValue
(
"QT_PIPEWIRE_CAMERA_FORCE_PORTAL"
) != 0;
42
43
if
(!
forcePortal
) {
44
bool
daemonConnected
=
connectToDaemon
();
45
if
(
daemonConnected
)
46
return
;
47
}
48
49
#
if
QT_CONFIG
(
dbus
)
50
connectViaPortal
();
51
#
else
52
qCWarning
(
lcPipewireCameraRegistry
)
53
<<
"Cannot fall back to the camera portal: built without D-Bus support"
;
54
#
endif
55
}
56
57
bool
QCameraContextManager
::
connectToDaemon
()
58
{
59
return
m_pwInstance
->
runWithEventLoopLock
([&] {
60
auto
connection
=
m_pwInstance
->
connectToDaemon
();
61
if
(!
connection
) {
62
qCDebug
(
lcPipewireCameraRegistry
) <<
"Failed to connect to pipewire instance directly:"
63
<<
connection
.
error
().
message
();
64
return
false
;
65
}
66
m_coreConnection
=
std
::
move
(*
connection
);
67
startDeviceMonitor
();
68
return
true
;
69
});
70
}
71
72
#
if
QT_CONFIG
(
dbus
)
73
bool
QCameraContextManager
::
connectViaPortal
()
74
{
75
using
namespace
std
::
chrono_literals
;
76
77
if
(!
portalInterfaceVersion
(
QLatin1StringView
(
"org.freedesktop.portal.Camera"
))) {
78
qCWarning
(
lcPipewireCameraRegistry
) <<
"No camera portal available either"
;
79
return
false
;
80
}
81
82
// brings up a dialog for the user to approve camera access
83
auto
fd
=
requestCameraPortalFd
(20s);
84
if
(!
fd
) {
85
qCWarning
(
lcPipewireCameraRegistry
)
86
<<
"Failed to obtain a camera file descriptor from the portal:"
<<
fd
.
error
();
87
return
false
;
88
}
89
90
return
m_pwInstance
->
runWithEventLoopLock
([&] {
91
auto
connection
=
m_pwInstance
->
connectToPortal
(
fd
->
takeFileDescriptor
());
92
if
(!
connection
) {
93
qCWarning
(
lcPipewireCameraRegistry
)
94
<<
"Failed to connect via the camera portal"
<<
connection
.
error
().
message
();
95
return
false
;
96
}
97
m_coreConnection
=
std
::
move
(*
connection
);
98
startDeviceMonitor
();
99
return
true
;
100
});
101
}
102
#
endif
103
104
QCameraContextManager
::~
QCameraContextManager
()
105
{
106
if
(
isConnected
())
107
stopDeviceMonitor
();
108
109
m_pwInstance
->
runWithEventLoopLock
([&] {
110
m_deviceMonitor
.
reset
();
111
m_registry
.
reset
();
112
m_coreConnection
.
reset
();
113
});
114
}
115
116
QCameraContextManager
*
QCameraContextManager
::
instance
()
117
{
118
return
s_cameraContextInstance
;
119
}
120
121
bool
QCameraContextManager
::
isSupported
()
122
{
123
if
(!
QPipeWireInstance
::
isLoaded
())
124
return
false
;
125
126
if
(!
pw_check_library_version
(0, 3, 44))
127
return
false
;
128
129
QCameraContextManager
*
self
=
instance
();
130
return
self
&&
self
->
isConnected
();
131
}
132
133
QCameraDeviceMonitor
&
QCameraContextManager
::
deviceMonitor
()
134
{
135
return
*
instance
()->
m_deviceMonitor
;
136
}
137
138
bool
QCameraContextManager
::
isInPwThreadLoop
()
139
{
140
return
instance
()->
m_pwInstance
->
isInPwThreadLoop
();
141
}
142
143
PwNodeHandle
QCameraContextManager
::
bindNode
(
ObjectId
id
)
144
{
145
return
PwNodeHandle
{
146
reinterpret_cast
<
pw_node
*>(
pw_registry_bind
(
m_registry
.
get
(),
id
.
value
,
147
PW_TYPE_INTERFACE_Node
, PW_VERSION_NODE,
148
sizeof
(
void
*))),
149
};
150
}
151
152
void
QCameraContextManager
::
syncRegistry
()
153
{
154
using
namespace
std
::
chrono_literals
;
155
CoreEventSyncHelper
syncHelper
;
156
157
auto
syncOrErr
=
syncHelper
.
sync
(
m_coreConnection
.
get
(), 3s);
158
if
(
syncOrErr
==
true
)
159
return
;
160
if
(
syncOrErr
==
false
)
161
qWarning
() <<
"pw_core_sync timed out"
;
162
else
if
(
syncOrErr
.
error
()) {
163
int
err
=
syncOrErr
.
error
();
164
qWarning
() <<
"CoreEventSyncHelper::sync failed:"
<<
make_error_code
(
err
).
message
();
165
}
166
}
167
168
void
QCameraContextManager
::
objectAddedCb
(
void
*
data
,
uint32_t
id
,
uint32_t
permissions
,
169
const
char
*
type
,
uint32_t
version
,
const
spa_dict
*
props
)
170
{
171
Q_ASSERT
(
isInPwThreadLoop
());
172
173
std
::
optional
<
PipewireRegistryType
>
objectType
=
parsePipewireRegistryType
(
type
);
174
if
(!
objectType
) {
175
qCritical
() <<
"object type cannot be parsed:"
<<
type
;
176
return
;
177
}
178
179
if
(!
props
) {
180
qCritical
() <<
"null property received"
;
181
return
;
182
}
183
184
reinterpret_cast
<
QCameraContextManager
*>(
data
)->
objectAdded
(
ObjectId
{
id
},
permissions
,
185
*
objectType
,
version
, *
props
);
186
}
187
188
void
QCameraContextManager
::
objectRemovedCb
(
void
*
data
,
uint32_t
id
)
189
{
190
Q_ASSERT
(
isInPwThreadLoop
());
191
192
auto
*
self
=
reinterpret_cast
<
QCameraContextManager
*>(
data
);
193
self
->
objectRemoved
(
ObjectId
{
id
});
194
}
195
196
void
QCameraContextManager
::
objectAdded
(
ObjectId
id
,
uint32_t
/*permissions*/
,
197
PipewireRegistryType
type
,
uint32_t
/*version*/
,
198
const
spa_dict
&
props
)
199
{
200
m_deviceMonitor
->
objectAdded
(
id
,
type
,
props
);
201
}
202
203
void
QCameraContextManager
::
objectRemoved
(
ObjectId
id
)
204
{
205
m_deviceMonitor
->
objectRemoved
(
id
);
206
}
207
208
void
QCameraContextManager
::
startDeviceMonitor
()
209
{
210
m_registry
=
PwRegistryHandle
{
211
pw_core_get_registry
(
m_coreConnection
.
get
(), PW_VERSION_REGISTRY,
212
/*user_data_size=*/
sizeof
(
QCameraContextManager
*)),
213
};
214
if
(!
m_registry
) {
215
qFatal
() <<
"Failed to create pipewire registry"
<<
make_error_code
().
message
();
216
return
;
217
}
218
219
spa_zero(
m_registryListener
);
220
221
static
constexpr
struct
pw_registry_events
registry_events
= {
222
.
version
=
PW_VERSION_REGISTRY_EVENTS
,
223
.
global
=
QCameraContextManager
::
objectAddedCb
,
224
.
global_remove
=
QCameraContextManager
::
objectRemovedCb
,
225
};
226
int
status
=
227
pw_registry_add_listener
(
m_registry
.
get
(), &
m_registryListener
, &
registry_events
,
this
);
228
if
(
status
< 0)
229
qFatal
() <<
"Failed to add listener"
<<
make_error_code
(-
status
).
message
();
230
}
231
232
void
QCameraContextManager
::
stopDeviceMonitor
()
233
{
234
if
(!
m_registry
)
235
return
;
236
237
m_pwInstance
->
runWithEventLoopLock
([&] {
238
spa_hook_remove
(&
m_registryListener
);
239
m_registry
.
reset
();
240
});
241
}
242
243
}
// namespace QtPipeWire
244
245
QT_END_NAMESPACE
QtPipeWire
Definition
qpipewire_async_support.cpp:10
qtmultimedia
src
multimedia
pipewire
qpipewire_cameracontextmanager.cpp
Generated on
for Qt by
1.16.1