File indexing completed on 2024-05-26 05:31:25

0001 /*
0002     SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QAbstractListModel>
0010 #include <QQmlParserStatus>
0011 #include <QSharedPointer>
0012 #include <QTimer>
0013 #include <qqmlregistration.h>
0014 
0015 #include <pipewire/pipewire.h>
0016 
0017 class PipeWireCore;
0018 
0019 namespace MediaRole
0020 {
0021 Q_NAMESPACE
0022 QML_ELEMENT
0023 // Match PW_KEY_MEDIA_ROLE
0024 enum Role : int {
0025     Unknown = -1,
0026     Movie,
0027     Music,
0028     Camera,
0029     Screen,
0030     Communication,
0031     Game,
0032     Notification,
0033     DSP,
0034     Production,
0035     Accessibility,
0036     Test,
0037     Last = Test,
0038 };
0039 Q_ENUM_NS(Role)
0040 }
0041 
0042 namespace NodeState
0043 {
0044 Q_NAMESPACE
0045 QML_ELEMENT
0046 // Match enum pw_node_state
0047 enum State : int {
0048     Error = -1, /**< error state */
0049     Creating = 0, /**< the node is being created */
0050     Suspended = 1, /**< the node is suspended, the device might be closed */
0051     Idle = 2, /**< the node is running but there is no active port */
0052     Running = 3, /**< the node is running */
0053 };
0054 Q_ENUM_NS(State)
0055 }
0056 
0057 class MediaMonitor : public QAbstractListModel, public QQmlParserStatus
0058 {
0059     Q_OBJECT
0060     Q_INTERFACES(QQmlParserStatus)
0061     QML_ELEMENT
0062 
0063     /**
0064      * Role for media streams. Only media streams with their @p PW_KEY_MEDIA_ROLE matching @p role will be monitored.
0065      * @default MediaRole::Unknown
0066      */
0067     Q_PROPERTY(MediaRole::Role role READ role WRITE setRole NOTIFY roleChanged)
0068 
0069     /**
0070      * Whether this monitor is able to detect apps using Pipewire for media access
0071      */
0072     Q_PROPERTY(bool detectionAvailable READ detectionAvailable NOTIFY detectionAvailableChanged)
0073 
0074     /**
0075      * Total count of media streams with the given role on the system
0076      */
0077     Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
0078 
0079     /**
0080      * The number of media streams that are currently used by applications
0081      */
0082     Q_PROPERTY(int runningCount READ runningCount NOTIFY runningCountChanged)
0083 
0084     /**
0085      * The number of media streams that are in idle state.
0086      */
0087     Q_PROPERTY(int idleCount READ idleCount NOTIFY idleCountChanged)
0088 
0089 public:
0090     enum Role {
0091         StateRole = Qt::UserRole + 1,
0092         ObjectSerialRole,
0093     };
0094     Q_ENUM(Role);
0095 
0096     explicit MediaMonitor(QObject *parent = nullptr);
0097     ~MediaMonitor() override;
0098 
0099     QVariant data(const QModelIndex &index, int role) const override;
0100     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
0101     QHash<int, QByteArray> roleNames() const override;
0102 
0103     MediaRole::Role role() const;
0104     void setRole(MediaRole::Role newRole);
0105 
0106     bool detectionAvailable() const;
0107     int runningCount() const;
0108     int idleCount() const;
0109 
0110 Q_SIGNALS:
0111     void roleChanged();
0112     void detectionAvailableChanged();
0113     void countChanged();
0114     void runningCountChanged();
0115     void idleCountChanged();
0116 
0117 private Q_SLOTS:
0118     void connectToCore();
0119     void onPipeBroken();
0120 
0121 private:
0122     struct ProxyDeleter {
0123         void operator()(pw_proxy *proxy) const
0124         {
0125             MediaMonitor::onProxyDestroy(pw_proxy_get_user_data(proxy));
0126             pw_proxy_destroy(proxy);
0127         }
0128     };
0129 
0130     static void onRegistryEventGlobal(void *data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const spa_dict *props);
0131     static void onRegistryEventGlobalRemove(void *data, uint32_t id);
0132     static void onProxyDestroy(void *data);
0133     static void onNodeEventInfo(void *data, const pw_node_info *info);
0134 
0135     static void readProps(const spa_dict *props, pw_proxy *proxy, bool emitSignal);
0136 
0137     void classBegin() override;
0138     void componentComplete() override;
0139 
0140     void disconnectFromCore();
0141     void reconnectOnIdle();
0142     void updateState();
0143 
0144     static pw_registry_events s_pwRegistryEvents;
0145     static pw_proxy_events s_pwProxyEvents;
0146     static pw_node_events s_pwNodeEvents;
0147 
0148     bool m_componentReady = false;
0149     MediaRole::Role m_role = MediaRole::Unknown;
0150     bool m_detectionAvailable = false;
0151     int m_runningCount = 0;
0152     int m_idleCount = 0;
0153 
0154     QSharedPointer<PipeWireCore> m_pwCore;
0155     pw_registry *m_registry = nullptr;
0156     spa_hook m_registryListener;
0157     std::vector<std::unique_ptr<pw_proxy, ProxyDeleter>> m_nodeList;
0158     QTimer m_reconnectTimer;
0159 
0160     bool m_inDestructor = false;
0161 };