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_audiodevicemonitor_p.h
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
4#ifndef QPIPEWIRE_AUDIODEVICEMONITOR_P_H
5#define QPIPEWIRE_AUDIODEVICEMONITOR_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtMultimedia/private/qpipewire_async_support_p.h>
19#include <QtMultimedia/private/qpipewire_propertydict_p.h>
20#include <QtMultimedia/private/qpipewire_registry_support_p.h>
21#include <QtMultimedia/private/qpipewire_spa_pod_support_p.h>
22#include <QtMultimedia/qaudiodevice.h>
23#include <QtCore/qchronotimer.h>
24#include <QtCore/qfuture.h>
25#include <QtCore/qreadwritelock.h>
26
27#include <list>
28#include <vector>
29
30#include <pipewire/pipewire.h>
31
32QT_BEGIN_NAMESPACE
33
34namespace QtPipeWire {
35
37{
39public:
41 ObjectSerial serial() const;
42
45
46private:
47 const ObjectSerial m_observedSerial;
48};
49
51
52// TODO: can we make use of COW here?
54{
56
57 enum class Direction : uint8_t
58 {
59 sink,
60 source,
61 };
62
63public:
66
67 void objectAdded(ObjectId, uint32_t permissions, PipewireRegistryType, uint32_t version,
68 const spa_dict &props);
70
72 {
73 };
77
78 std::optional<ObjectSerial> findSinkNodeSerial(std::string_view nodeName) const;
79 std::optional<ObjectSerial> findSourceNodeSerial(std::string_view nodeName) const;
80
81 // ObjectId/ObjectSerial mapping
84
86 void unregisterObserver(const SharedObjectRemoveObserver &);
87 void clearAllObservers();
88
89 // Obtaining device lists
95 DeviceLists getDeviceLists(bool verifyThreading = true);
96
100
101private:
102 struct DeviceRecord
103 {
104 ObjectSerial serial;
105 PwPropertyDict properties;
106 };
107
108 struct PendingNodeRecord
109 {
110 PendingNodeRecord(ObjectId, ObjectSerial serial, std::optional<ObjectSerial> deviceSerial,
111 PwPropertyDict properties);
112
113 ObjectSerial serial;
114 std::optional<ObjectSerial> deviceSerial; // may be nullopt for virtual nodes
115 PwPropertyDict properties;
116 std::unique_ptr<NodeEventListener> enumFormatListener;
117 std::unique_ptr<CoreEventDoneListener> enumFormatDoneListener;
118 QFuture<std::vector<SpaObjectAudioFormat>> formatFuture;
119
120 // owned by he instance, updated in the formatFuture's continuation (we capture a weak reference in the
121 // continuation to avoid lifetime issues
122 const std::shared_ptr<std::optional<std::vector<SpaObjectAudioFormat>>> formatResults;
123 };
124
125 struct NodeRecord
126 {
127 ObjectSerial serial;
128 std::optional<ObjectSerial> deviceSerial; // may be nullopt for virtual nodes
129 PwPropertyDict properties;
130 SpaObjectAudioFormat format;
131 };
132
133 // discovered, but format not resolved
134 struct PendingRecords
135 {
136 std::list<PendingNodeRecord> m_sources;
137 std::list<PendingNodeRecord> m_sinks;
138 std::vector<ObjectSerial> m_removals;
139
140 std::optional<std::variant<QByteArray, NoDefaultDeviceType>> m_defaultSource;
141 std::optional<std::variant<QByteArray, NoDefaultDeviceType>> m_defaultSink;
142
143 void removeRecordsForObject(ObjectSerial);
144 };
145 QMutex m_pendingRecordsMutex;
146 PendingRecords m_pendingRecords QT_MM_GUARDED_BY(m_pendingRecordsMutex);
147
148 // discovered, format resolved. living on application thread
149 mutable QReadWriteLock m_mutex;
150 std::map<ObjectSerial, DeviceRecord> m_devices QT_MM_GUARDED_BY(m_mutex);
151 std::vector<NodeRecord> m_sources QT_MM_GUARDED_BY(m_mutex);
152 std::vector<NodeRecord> m_sinks QT_MM_GUARDED_BY(m_mutex);
153 std::optional<QByteArray> m_defaultSourceName;
154 std::optional<QByteArray> m_defaultSinkName;
155
156 QThread m_compressionTimerThread{ this };
157 QChronoTimer m_compressionTimer;
158 void startCompressionTimer();
159
160 // Device list updates
161 void audioDevicesChanged(bool verifyThreading = true);
162 void updateSources(std::list<PendingNodeRecord> addedNodes,
163 QSpan<const ObjectSerial> removedObjects);
164 void updateSinks(std::list<PendingNodeRecord> addedNodes,
165 QSpan<const ObjectSerial> removedObjects);
166 template <Direction>
167 void updateSourcesOrSinks(std::list<PendingNodeRecord>, QSpan<const ObjectSerial>);
168
169 QList<QAudioDevice> m_sourceDeviceList;
170 QList<QAudioDevice> m_sinkDeviceList;
171
172 std::optional<ObjectSerial> findDeviceSerial(std::string_view deviceName) const;
173
174 template <Direction>
175 std::optional<ObjectSerial> findNodeSerialForNodeName(std::string_view nodeName) const;
176
177 // ObjectId/ObjectSerial mapping
178 // CHECK: can we completely rely on only accessing ObjectId under the pipewire event loop lock?
179 mutable QReadWriteLock m_objectDictMutex;
180 std::map<ObjectId, ObjectSerial> m_objectSerialDict QT_MM_GUARDED_BY(m_objectDictMutex);
181 std::map<ObjectSerial, ObjectId> m_serialObjectDict QT_MM_GUARDED_BY(m_objectDictMutex);
182
183 std::vector<SharedObjectRemoveObserver>
184 m_objectRemoveObserver QT_MM_GUARDED_BY(m_objectDictMutex);
185};
186
187} // namespace QtPipeWire
188
189QT_END_NAMESPACE
190
191#endif // QPIPEWIRE_AUDIODEVICEMONITOR_P_H
void audioSourcesChanged(QList< QAudioDevice >)
std::optional< ObjectSerial > findObjectSerial(ObjectId) const
void unregisterObserver(const SharedObjectRemoveObserver &)
std::optional< ObjectSerial > findSourceNodeSerial(std::string_view nodeName) const
void setDefaultAudioSink(std::variant< QByteArray, NoDefaultDeviceType >)
DeviceLists getDeviceLists(bool verifyThreading=true)
void setDefaultAudioSource(std::variant< QByteArray, NoDefaultDeviceType >)
std::optional< ObjectSerial > findSinkNodeSerial(std::string_view nodeName) const
static constexpr NoDefaultDeviceType NoDefaultDevice
void objectAdded(ObjectId, uint32_t permissions, PipewireRegistryType, uint32_t version, const spa_dict &props)
std::optional< ObjectId > findObjectId(ObjectSerial) const
bool registerObserver(SharedObjectRemoveObserver)
Q_STATIC_LOGGING_CATEGORY(lcPipewireAudioSink, "qt.multimedia.pipewire.audiosink")
StrongIdType< uint64_t, ObjectSerialTag > ObjectSerial