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);
 
  123    if (m_reconnectTimer.isActive()) {
 
  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);
 
  218    monitor->beginRemoveRows(QModelIndex(), row, row);
 
  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);
 
  262        const QModelIndex idx = node->monitor->
index(row, 0);
 
  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));
 
  273    QList<int> changedRoles;
 
  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);
 
  292        const QModelIndex idx = node->monitor->
index(row, 0);
 
  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()
 
  342    if (m_reconnectTimer.isActive()) {
 
  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
 
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
 
virtual QModelIndex parent(const QModelIndex &index) const const=0
 
void rowsInserted(const QModelIndex &parent, int first, int last)
 
void rowsRemoved(const QModelIndex &parent, int first, int last)
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
bool disconnect(const QMetaObject::Connection &connection)
 
QString fromUtf8(QByteArrayView str)
 
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)