8#include "mediamonitor.h"
15#include "pipewirecore_p.h"
20 MediaMonitor *monitor;
23 NodeState::State state = NodeState::Error;
24 spa_hook proxyListener;
25 spa_hook objectListener;
28void updateProp(
const spa_dict *props,
const char *key,
QString &prop,
int role,
QList<int> &changedRoles)
30 const char *new_prop = spa_dict_lookup(props, key);
35 prop = std::move(newProp);
41pw_registry_events MediaMonitor::s_pwRegistryEvents = {
42 .version = PW_VERSION_REGISTRY_EVENTS,
43 .global = &MediaMonitor::onRegistryEventGlobal,
44 .global_remove = &MediaMonitor::onRegistryEventGlobalRemove,
47pw_proxy_events MediaMonitor::s_pwProxyEvents = {
48 .version = PW_VERSION_PROXY_EVENTS,
49 .destroy = &MediaMonitor::onProxyDestroy,
52pw_node_events MediaMonitor::s_pwNodeEvents = {
53 .version = PW_VERSION_NODE_EVENTS,
54 .info = &MediaMonitor::onNodeEventInfo,
57MediaMonitor::MediaMonitor(
QObject *parent)
64 m_reconnectTimer.setSingleShot(
true);
65 m_reconnectTimer.setInterval(5000);
69MediaMonitor::~MediaMonitor()
71 m_inDestructor =
true;
81 pw_proxy *
const proxy = m_nodeList.at(
index.
row()).get();
82 const Node *
const node =
static_cast<Node *
>(pw_proxy_get_user_data(proxy));
86 return node->deviceName;
89 case ObjectSerialRole:
90 return node->objectSerial;
96int MediaMonitor::rowCount(
const QModelIndex &parent)
const
98 return parent.isValid() ? 0 : m_nodeList.size();
105 {StateRole, QByteArrayLiteral(
"state")},
106 {ObjectSerialRole, QByteArrayLiteral(
"objectSerial")},
110MediaRole::Role MediaMonitor::role()
const
115void MediaMonitor::setRole(MediaRole::Role newRole)
117 if (m_role == newRole) {
120 Q_ASSERT(newRole >= MediaRole::Unknown && newRole <= MediaRole::Last);
121 m_role = std::clamp(newRole, MediaRole::Unknown, MediaRole::Last);
128 disconnectFromCore();
134bool MediaMonitor::detectionAvailable()
const
136 return m_detectionAvailable;
139int MediaMonitor::runningCount()
const
141 return m_runningCount;
144int MediaMonitor::idleCount()
const
149void MediaMonitor::connectToCore()
151 Q_ASSERT(!m_registry);
152 if (!m_componentReady || m_role == MediaRole::Unknown) {
157 m_pwCore = PipeWireCore::fetch(0);
159 if (!m_pwCore->error().isEmpty()) {
160 qDebug() <<
"received error while creating the stream" << m_pwCore->error() <<
"Media monitor will not work.";
162 m_reconnectTimer.
start();
166 m_registry = pw_core_get_registry(**m_pwCore.
get(), PW_VERSION_REGISTRY, 0);
167 pw_registry_add_listener(m_registry, &m_registryListener, &s_pwRegistryEvents,
this );
169 m_detectionAvailable =
true;
170 Q_EMIT detectionAvailableChanged();
172 connect(m_pwCore.
get(), &PipeWireCore::pipeBroken,
this, &MediaMonitor::onPipeBroken);
175void MediaMonitor::onPipeBroken()
177 m_registry =
nullptr;
178 disconnectFromCore();
182void MediaMonitor::onRegistryEventGlobal(
void *data, uint32_t
id, uint32_t ,
const char *type, uint32_t ,
const spa_dict *props)
184 auto monitor =
static_cast<MediaMonitor *
>(data);
186 if (!props || !(spa_streq(type, PW_TYPE_INTERFACE_Node))) {
191 if (
const char *prop_str = spa_dict_lookup(props, PW_KEY_MEDIA_ROLE); !prop_str || (strcmp(prop_str, metaEnum.
valueToKey(monitor->m_role)) != 0)) {
195 auto proxy =
static_cast<pw_proxy *
>(pw_registry_bind(monitor->m_registry,
id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE,
sizeof(
Node)));
196 auto node =
static_cast<Node *
>(pw_proxy_get_user_data(proxy));
197 node->monitor = monitor;
198 readProps(props, proxy,
false);
200 monitor->beginInsertRows(
QModelIndex(), monitor->m_nodeList.size(), monitor->m_nodeList.size());
201 monitor->m_nodeList.emplace_back(proxy);
202 monitor->endInsertRows();
204 pw_proxy_add_listener(proxy, &node->proxyListener, &s_pwProxyEvents, node);
205 pw_proxy_add_object_listener(proxy, &node->objectListener, &s_pwNodeEvents, node);
208void MediaMonitor::onRegistryEventGlobalRemove(
void *data, uint32_t
id)
210 auto monitor =
static_cast<MediaMonitor *
>(data);
211 const auto proxyIt = std::find_if(monitor->m_nodeList.cbegin(), monitor->m_nodeList.cend(), [
id](
const auto &proxy) {
212 return pw_proxy_get_bound_id(proxy.get()) == id;
214 if (proxyIt == monitor->m_nodeList.cend()) {
217 const int row = std::distance(monitor->m_nodeList.cbegin(), proxyIt);
219 monitor->m_nodeList.erase(proxyIt);
220 monitor->endRemoveRows();
223void MediaMonitor::onProxyDestroy(
void *data)
225 auto node =
static_cast<Node *
>(data);
226 spa_hook_remove(&node->proxyListener);
227 spa_hook_remove(&node->objectListener);
230void MediaMonitor::onNodeEventInfo(
void *data,
const pw_node_info *info)
232 auto node =
static_cast<Node *
>(data);
234 NodeState::State newState;
235 switch (info->state) {
236 case PW_NODE_STATE_ERROR:
237 newState = NodeState::Error;
239 case PW_NODE_STATE_CREATING:
240 newState = NodeState::Creating;
242 case PW_NODE_STATE_SUSPENDED:
243 newState = NodeState::Suspended;
245 case PW_NODE_STATE_IDLE:
246 newState = NodeState::Idle;
248 case PW_NODE_STATE_RUNNING:
249 newState = NodeState::Running;
252 Q_ASSERT_X(
false,
"MediaMonitor",
"Unknown node state");
256 const auto proxyIt = std::find_if(node->monitor->m_nodeList.cbegin(), node->monitor->m_nodeList.cend(), [data](
const auto &proxy) {
257 return pw_proxy_get_user_data(proxy.get()) == data;
259 if (node->state != newState) {
260 node->state = newState;
261 const int row = std::distance(node->monitor->m_nodeList.cbegin(), proxyIt);
263 node->monitor->dataChanged(idx, idx, {StateRole});
266 readProps(info->props, proxyIt->get(),
true);
267 node->monitor->updateState();
270void MediaMonitor::readProps(
const spa_dict *props, pw_proxy *proxy,
bool emitSignal)
272 auto node =
static_cast<Node *
>(pw_proxy_get_user_data(proxy));
275 updateProp(props, PW_KEY_NODE_NICK, node->deviceName,
Qt::DisplayRole, changedRoles);
276 if (node->deviceName.isEmpty()) {
277 changedRoles.
clear();
278 updateProp(props, PW_KEY_NODE_NAME, node->deviceName,
Qt::DisplayRole, changedRoles);
280 if (node->deviceName.isEmpty()) {
281 changedRoles.
clear();
282 updateProp(props, PW_KEY_NODE_DESCRIPTION, node->deviceName,
Qt::DisplayRole, changedRoles);
285 updateProp(props, PW_KEY_OBJECT_SERIAL, node->objectSerial, ObjectSerialRole, changedRoles);
287 if (emitSignal && !changedRoles.
empty()) {
288 const auto proxyIt = std::find_if(node->monitor->m_nodeList.cbegin(), node->monitor->m_nodeList.cend(), [proxy](
const auto &p) {
289 return p.get() == proxy;
291 const int row = std::distance(node->monitor->m_nodeList.cbegin(), proxyIt);
293 node->monitor->dataChanged(idx, idx, changedRoles);
297void MediaMonitor::classBegin()
301void MediaMonitor::componentComplete()
303 m_componentReady =
true;
307void MediaMonitor::disconnectFromCore()
313 if (m_runningCount) {
315 Q_EMIT runningCountChanged();
320 Q_EMIT idleCountChanged();
323 m_detectionAvailable =
false;
324 Q_EMIT detectionAvailableChanged();
326 if (!m_inDestructor) {
333 pw_proxy_destroy(
reinterpret_cast<struct pw_proxy *
>(m_registry));
334 spa_hook_remove(&m_registryListener);
335 m_registry =
nullptr;
337 disconnect(m_pwCore.
get(), &PipeWireCore::pipeBroken,
this, &MediaMonitor::onPipeBroken);
340void MediaMonitor::reconnectOnIdle()
346 static unsigned retryCount = 0;
347 if (retryCount > 100) {
348 qWarning() <<
"Camera indicator receives too many errors. Aborting...";
352 m_reconnectTimer.
start();
355void MediaMonitor::updateState()
357 int newIdleCount = 0;
358 int newRunningCount = 0;
359 for (
const auto &proxy : m_nodeList) {
360 switch (
static_cast<Node *
>(pw_proxy_get_user_data(proxy.get()))->state) {
361 case NodeState::Idle:
364 case NodeState::Running:
372 const bool idleChanged = m_idleCount != newIdleCount;
373 m_idleCount = newIdleCount;
374 const bool runningChanged = m_runningCount != newRunningCount;
375 m_runningCount = newRunningCount;
378 Q_EMIT idleCountChanged();
380 if (runningChanged) {
381 Q_EMIT runningCountChanged();
385#include "moc_mediamonitor.cpp"
bool checkIndex(const QModelIndex &index, CheckIndexOptions options) const const
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
QString fromUtf8(QByteArrayView str)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isActive() const const