File indexing completed on 2024-05-05 05:30:17

0001 /*
0002     SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
0003     SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "pipewirecore_p.h"
0009 
0010 #include <KLocalizedString>
0011 #include <QSocketNotifier>
0012 #include <QThread>
0013 #include <QThreadStorage>
0014 #include <mutex>
0015 #include <spa/utils/result.h>
0016 
0017 #include "logging.h"
0018 
0019 pw_core_events PipeWireCore::s_pwCoreEvents = {
0020     .version = PW_VERSION_CORE_EVENTS,
0021     .info = &PipeWireCore::onCoreInfo,
0022     .done = nullptr,
0023     .ping = nullptr,
0024     .error = &PipeWireCore::onCoreError,
0025     .remove_id = nullptr,
0026     .bound_id = nullptr,
0027     .add_mem = nullptr,
0028     .remove_mem = nullptr,
0029 };
0030 
0031 PipeWireCore::PipeWireCore()
0032 {
0033     static std::once_flag pwInitOnce;
0034     std::call_once(pwInitOnce, [] { pw_init(nullptr, nullptr); });
0035 }
0036 
0037 void PipeWireCore::onCoreError(void *data, uint32_t id, int seq, int res, const char *message)
0038 {
0039     Q_UNUSED(seq)
0040 
0041     qCWarning(PIPEWIRE_LOGGING) << "PipeWire remote error: " << res << message;
0042     if (id == PW_ID_CORE) {
0043         PipeWireCore *pw = static_cast<PipeWireCore *>(data);
0044         Q_EMIT pw->pipewireFailed(QString::fromUtf8(message));
0045 
0046         if (res == -EPIPE) {
0047             // Broken pipe, need reconnecting
0048             if (pw->m_pwCore) {
0049                 Q_EMIT pw->pipeBroken();
0050                 spa_hook_remove(&pw->m_coreListener);
0051                 pw_core_disconnect(pw->m_pwCore);
0052                 pw->init_core();
0053             }
0054         }
0055     }
0056 }
0057 
0058 void PipeWireCore::onCoreInfo(void *data, const struct pw_core_info *info)
0059 {
0060     PipeWireCore *pw = static_cast<PipeWireCore *>(data);
0061     pw->m_serverVersion = QVersionNumber::fromString(QString::fromUtf8(info->version));
0062 }
0063 
0064 PipeWireCore::~PipeWireCore()
0065 {
0066     if (m_pwMainLoop) {
0067         pw_loop_leave(m_pwMainLoop);
0068     }
0069 
0070     if (m_pwCore) {
0071         pw_core_disconnect(m_pwCore);
0072     }
0073 
0074     if (m_pwContext) {
0075         pw_context_destroy(m_pwContext);
0076     }
0077 
0078     if (m_pwMainLoop) {
0079         pw_loop_destroy(m_pwMainLoop);
0080     }
0081 }
0082 
0083 bool PipeWireCore::init(int fd)
0084 {
0085     m_pwMainLoop = pw_loop_new(nullptr);
0086     pw_loop_enter(m_pwMainLoop);
0087 
0088     QSocketNotifier *notifier = new QSocketNotifier(pw_loop_get_fd(m_pwMainLoop), QSocketNotifier::Read, this);
0089     connect(notifier, &QSocketNotifier::activated, this, [this] {
0090         int result = pw_loop_iterate(m_pwMainLoop, 0);
0091         if (result < 0)
0092             qCWarning(PIPEWIRE_LOGGING) << "pipewire_loop_iterate failed: " << spa_strerror(result);
0093     });
0094 
0095     m_pwContext = pw_context_new(m_pwMainLoop, nullptr, 0);
0096     if (!m_pwContext) {
0097         qCWarning(PIPEWIRE_LOGGING) << "Failed to create PipeWire context";
0098         m_error = i18n("Failed to create PipeWire context");
0099         return false;
0100     }
0101 
0102     m_fd = fd;
0103 
0104     return init_core();
0105 }
0106 
0107 bool PipeWireCore::init_core()
0108 {
0109     if (m_fd > 0) {
0110         m_pwCore = pw_context_connect_fd(m_pwContext, m_fd, nullptr, 0);
0111     } else {
0112         m_pwCore = pw_context_connect(m_pwContext, nullptr, 0);
0113     }
0114     if (!m_pwCore) {
0115         m_error = i18n("Failed to connect to PipeWire");
0116         qCWarning(PIPEWIRE_LOGGING) << "error:" << m_error << m_fd;
0117         return false;
0118     }
0119 
0120     if (pw_loop_iterate(m_pwMainLoop, 0) < 0) {
0121         qCWarning(PIPEWIRE_LOGGING) << "Failed to start main PipeWire loop";
0122         m_error = i18n("Failed to start main PipeWire loop");
0123         return false;
0124     }
0125 
0126     pw_core_add_listener(m_pwCore, &m_coreListener, &s_pwCoreEvents, this);
0127     return true;
0128 }
0129 
0130 QSharedPointer<PipeWireCore> PipeWireCore::fetch(int fd)
0131 {
0132     static QThreadStorage<QHash<int, QWeakPointer<PipeWireCore>>> global;
0133     QSharedPointer<PipeWireCore> ret = global.localData().value(fd).toStrongRef();
0134     if (!ret) {
0135         ret.reset(new PipeWireCore);
0136         if (ret->init(fd)) {
0137             global.localData().insert(fd, ret);
0138         }
0139     }
0140     return ret;
0141 }
0142 
0143 QString PipeWireCore::error() const
0144 {
0145     return m_error;
0146 }