Skip to content

Commit ee570ec

Browse files
nydragonoutfoxxed
authored andcommitted
services/pipewire: expose node type
1 parent 6b3d64e commit ee570ec

File tree

5 files changed

+100
-23
lines changed

5 files changed

+100
-23
lines changed

src/services/pipewire/defaults.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ void PwDefaultTracker::onNodeDestroyed(QObject* node) {
146146

147147
void PwDefaultTracker::changeConfiguredSink(PwNode* node) {
148148
if (node != nullptr) {
149-
if (!node->isSink) {
149+
if (!node->type.testFlags(PwNodeType::AudioSink)) {
150150
qCCritical(logDefaults) << "Cannot change default sink to a node that is not a sink.";
151151
return;
152152
}
@@ -168,7 +168,7 @@ void PwDefaultTracker::changeConfiguredSinkName(const QString& sink) {
168168

169169
void PwDefaultTracker::changeConfiguredSource(PwNode* node) {
170170
if (node != nullptr) {
171-
if (node->isSink) {
171+
if (!node->type.testFlags(PwNodeType::AudioSource)) {
172172
qCCritical(logDefaults) << "Cannot change default source to a node that is not a source.";
173173
return;
174174
}

src/services/pipewire/node.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <qlogging.h>
1212
#include <qloggingcategory.h>
1313
#include <qobject.h>
14+
#include <qstringliteral.h>
1415
#include <qtmetamacros.h>
1516
#include <qtypes.h>
1617
#include <spa/node/keys.h>
@@ -85,6 +86,20 @@ QString PwAudioChannel::toString(Enum value) {
8586
}
8687
}
8788

89+
QString PwNodeType::toString(PwNodeType::Flags type) {
90+
switch (type) {
91+
case PwNodeType::VideoSource: return QStringLiteral("VideoSource");
92+
case PwNodeType::VideoSink: return QStringLiteral("VideoSink");
93+
case PwNodeType::AudioSource: return QStringLiteral("AudioSource");
94+
case PwNodeType::AudioSink: return QStringLiteral("AudioSink");
95+
case PwNodeType::AudioDuplex: return QStringLiteral("AudioDuplex");
96+
case PwNodeType::AudioOutStream: return QStringLiteral("AudioOutStream");
97+
case PwNodeType::AudioInStream: return QStringLiteral("AudioInStream");
98+
case PwNodeType::Untracked: return QStringLiteral("Untracked");
99+
default: return QStringLiteral("Invalid");
100+
}
101+
}
102+
88103
void PwNode::bindHooks() {
89104
// Bind the device first as pw is in order, meaning the device should be bound before
90105
// we want to do anything with it.
@@ -116,21 +131,19 @@ void PwNode::unbindHooks() {
116131
void PwNode::initProps(const spa_dict* props) {
117132
if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) {
118133
if (strcmp(mediaClass, "Audio/Sink") == 0) {
119-
this->type = PwNodeType::Audio;
120-
this->isSink = true;
121-
this->isStream = false;
134+
this->type = PwNodeType::AudioSink;
122135
} else if (strcmp(mediaClass, "Audio/Source") == 0) {
123-
this->type = PwNodeType::Audio;
124-
this->isSink = false;
125-
this->isStream = false;
136+
this->type = PwNodeType::AudioSource;
137+
} else if (strcmp(mediaClass, "Audio/Duplex") == 0) {
138+
this->type = PwNodeType::AudioDuplex;
126139
} else if (strcmp(mediaClass, "Stream/Output/Audio") == 0) {
127-
this->type = PwNodeType::Audio;
128-
this->isSink = false;
129-
this->isStream = true;
140+
this->type = PwNodeType::AudioOutStream;
130141
} else if (strcmp(mediaClass, "Stream/Input/Audio") == 0) {
131-
this->type = PwNodeType::Audio;
132-
this->isSink = true;
133-
this->isStream = true;
142+
this->type = PwNodeType::AudioInStream;
143+
} else if (strcmp(mediaClass, "Video/Sink") == 0) {
144+
this->type = PwNodeType::VideoSink;
145+
} else if (strcmp(mediaClass, "Video/Source") == 0) {
146+
this->type = PwNodeType::VideoSource;
134147
}
135148
}
136149

@@ -164,7 +177,7 @@ void PwNode::initProps(const spa_dict* props) {
164177
}
165178
}
166179

167-
if (this->type == PwNodeType::Audio) {
180+
if (this->type.testFlags(PwNodeType::Audio)) {
168181
this->boundData = new PwNodeBoundAudio(this);
169182
}
170183
}

src/services/pipewire/node.hpp

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <pipewire/node.h>
55
#include <pipewire/type.h>
66
#include <qcontainerfwd.h>
7+
#include <qflags.h>
78
#include <qmap.h>
89
#include <qobject.h>
910
#include <qqmlintegration.h>
@@ -86,12 +87,71 @@ class PwAudioChannel: public QObject {
8687
/// including aux and custom channel ranges.
8788
Q_INVOKABLE static QString toString(qs::service::pipewire::PwAudioChannel::Enum value);
8889
};
90+
///! The type of a pipewire node.
91+
/// Use bitwise comparisons to filter for audio, video, sink, source or stream nodes
92+
class PwNodeType: public QObject {
93+
Q_OBJECT;
94+
QML_ELEMENT;
95+
QML_SINGLETON;
8996

90-
enum class PwNodeType : quint8 {
91-
Untracked,
92-
Audio,
97+
public:
98+
enum Flag : quint8 {
99+
// A Pipewire node which is not being managed.
100+
Untracked = 0b0,
101+
// This flag is set when this node is an Audio node.
102+
Audio = 0b1,
103+
// This flag is set when this node is an Video node.
104+
Video = 0b10,
105+
// This flag is set when this node is a stream node.
106+
Stream = 0b100,
107+
// This flag is set when this node is producing some form of data,
108+
// such as a microphone, screenshare or webcam.
109+
Source = 0b1000,
110+
// This flag is set when this node is receiving data.
111+
Sink = 0b10000,
112+
// A sink for audio samples, like an audio card.
113+
//
114+
// This is equivalent to the media class `Video/Source` and is
115+
// composed of the @@PwNodeType.Audio and @@PwNodeType.Sink flags.
116+
AudioSink = Audio | Sink,
117+
// A source of audio samples like a microphone.
118+
//
119+
// This is quivalent to the media class `Video/Sink` and is composed
120+
// of the @@PwNodeType.Audio and @@PwNodeType.Source flags.
121+
AudioSource = Audio | Source,
122+
// A node that is both a sink and a source.
123+
//
124+
// This is equivalent to the media class `Audio/Duplex` and is composed of the
125+
// @@PwNodeType.Audio, @@PwNodeType.Source and @@PwNodeType.Sink flags.
126+
AudioDuplex = Audio | Sink | Source,
127+
// A playback stream.
128+
//
129+
// This is equivalent to the media class `Stream/Output/Audio` and is composed
130+
// of the @@PwNodeType.Audio, @@PwNodeType.Sink and @@PwNodeType.Stream flags.
131+
AudioOutStream = Audio | Sink | Stream,
132+
// A capture stream.
133+
//
134+
// This is equivalent to the media class `Stream/Input/Audio` and is composed
135+
// of the @@PwNodeType.Audio, @@PwNodeType.Source and @@PwNodeType.Stream flags.
136+
AudioInStream = Audio | Source | Stream,
137+
// A producer of video, like a webcam or a screenshare.
138+
//
139+
// This is equivalent to the media class `Video/Source` and is composed
140+
// of the @@PwNodeType.Video and @@PwNodeType.Source flags.
141+
VideoSource = Video | Source,
142+
// A consumer of video, such as a program that is recieving a video stream.
143+
//
144+
// This is equivalent to the media class `Video/Sink` and is composed of the
145+
// @@PwNodeType.Video and @@PwNodeType.Sink flags.
146+
VideoSink = Video | Sink,
147+
};
148+
Q_ENUM(Flag)
149+
Q_DECLARE_FLAGS(Flags, Flag)
150+
Q_INVOKABLE static QString toString(qs::service::pipewire::PwNodeType::Flags type);
93151
};
94152

153+
Q_DECLARE_OPERATORS_FOR_FLAGS(PwNodeType::Flags)
154+
95155
class PwNode;
96156

97157
struct PwVolumeProps {
@@ -169,9 +229,8 @@ class PwNode: public PwBindable<pw_node, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE
169229
QString nick;
170230
QMap<QString, QString> properties;
171231

172-
PwNodeType type = PwNodeType::Untracked;
173-
bool isSink = false;
174-
bool isStream = false;
232+
PwNodeType::Flags type = PwNodeType::Untracked;
233+
175234
bool ready = false;
176235

177236
PwNodeBoundData* boundData = nullptr;

src/services/pipewire/qml.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,14 @@ QString PwNodeIface::description() const { return this->mNode->description; }
328328

329329
QString PwNodeIface::nickname() const { return this->mNode->nick; }
330330

331-
bool PwNodeIface::isSink() const { return this->mNode->isSink; }
331+
bool PwNodeIface::isSink() const { return this->mNode->type.testFlags(PwNodeType::Sink); }
332332

333-
bool PwNodeIface::isStream() const { return this->mNode->isStream; }
333+
bool PwNodeIface::isStream() const { return this->mNode->type.testFlags(PwNodeType::Stream); }
334334

335335
bool PwNodeIface::isReady() const { return this->mNode->ready; }
336336

337+
PwNodeType::Flags PwNodeIface::type() const { return this->mNode->type; };
338+
337339
QVariantMap PwNodeIface::properties() const {
338340
auto map = QVariantMap();
339341
for (auto [k, v]: this->mNode->properties.asKeyValueRange()) {

src/services/pipewire/qml.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ class PwNodeIface: public PwObjectIface {
287287
/// If `true` then the node is likely to be a program, if `false` it is likely to be
288288
/// a hardware device.
289289
Q_PROPERTY(bool isStream READ isStream CONSTANT);
290+
/// The type of this node. Reflects Pipewire's [media.class](https://docs.pipewire.org/page_man_pipewire-props_7.html).
291+
Q_PROPERTY(qs::service::pipewire::PwNodeType::Flags type READ type CONSTANT);
290292
/// The property set present on the node, as an object containing key-value pairs.
291293
/// You can inspect this directly with `pw-cli i <id>`.
292294
///
@@ -324,6 +326,7 @@ class PwNodeIface: public PwObjectIface {
324326
[[nodiscard]] bool isSink() const;
325327
[[nodiscard]] bool isStream() const;
326328
[[nodiscard]] bool isReady() const;
329+
[[nodiscard]] PwNodeType::Flags type() const;
327330
[[nodiscard]] QVariantMap properties() const;
328331
[[nodiscard]] PwNodeAudioIface* audio() const;
329332

0 commit comments

Comments
 (0)