KPipewire

pipewirecore.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
3 SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "pipewirecore_p.h"
9
10#include <KLocalizedString>
11#include <QSocketNotifier>
12#include <QThread>
13#include <QThreadStorage>
14#include <mutex>
15#include <spa/utils/result.h>
16
17#include "logging.h"
18
19using namespace Qt::StringLiterals;
20
21pw_core_events PipeWireCore::s_pwCoreEvents = {
22 .version = PW_VERSION_CORE_EVENTS,
23 .info = &PipeWireCore::onCoreInfo,
24 .done = nullptr,
25 .ping = nullptr,
26 .error = &PipeWireCore::onCoreError,
27 .remove_id = nullptr,
28 .bound_id = nullptr,
29 .add_mem = nullptr,
30 .remove_mem = nullptr,
31};
32
33PipeWireCore::PipeWireCore()
34{
35 static std::once_flag pwInitOnce;
36 std::call_once(pwInitOnce, [] { pw_init(nullptr, nullptr); });
37}
38
39void PipeWireCore::onCoreError(void *data, uint32_t id, int seq, int res, const char *message)
40{
41 Q_UNUSED(seq)
42
43 qCWarning(PIPEWIRE_LOGGING) << "PipeWire remote error: " << res << message;
44 if (id == PW_ID_CORE) {
45 PipeWireCore *pw = static_cast<PipeWireCore *>(data);
46 Q_EMIT pw->pipewireFailed(QString::fromUtf8(message));
47
48 if (res == -EPIPE) {
49 // Broken pipe, need reconnecting
50 if (pw->m_pwCore) {
51 Q_EMIT pw->pipeBroken();
52 spa_hook_remove(&pw->m_coreListener);
53 pw_core_disconnect(pw->m_pwCore);
54 pw->init_core();
55 }
56 }
57 }
58}
59
60void PipeWireCore::onCoreInfo(void *data, const struct pw_core_info *info)
61{
62 PipeWireCore *pw = static_cast<PipeWireCore *>(data);
63 pw->m_serverVersion = QVersionNumber::fromString(QString::fromUtf8(info->version));
64}
65
66PipeWireCore::~PipeWireCore()
67{
68 if (m_pwMainLoop) {
69 pw_loop_leave(m_pwMainLoop);
70 }
71
72 if (m_pwCore) {
73 pw_core_disconnect(m_pwCore);
74 }
75
76 if (m_pwContext) {
77 pw_context_destroy(m_pwContext);
78 }
79
80 if (m_pwMainLoop) {
81 pw_loop_destroy(m_pwMainLoop);
82 }
83}
84
85bool PipeWireCore::init(int fd)
86{
87 m_pwMainLoop = pw_loop_new(nullptr);
88 if (!m_pwMainLoop) {
90 ki18n("Invalid PipeWire installation. See https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3296 for more details.");
91 qCWarning(PIPEWIRE_LOGGING) << error.untranslatedText();
92 m_error = error.toString();
93 return false;
94 }
95 pw_loop_enter(m_pwMainLoop);
96
97 QSocketNotifier *notifier = new QSocketNotifier(pw_loop_get_fd(m_pwMainLoop), QSocketNotifier::Read, this);
98 connect(notifier, &QSocketNotifier::activated, this, [this] {
99 int result = pw_loop_iterate(m_pwMainLoop, 0);
100 if (result < 0)
101 qCWarning(PIPEWIRE_LOGGING) << "pipewire_loop_iterate failed: " << spa_strerror(result);
102 });
103
104 m_pwContext = pw_context_new(m_pwMainLoop, nullptr, 0);
105 if (!m_pwContext) {
106 qCWarning(PIPEWIRE_LOGGING) << "Failed to create PipeWire context";
107 m_error = i18n("Failed to create PipeWire context");
108 return false;
109 }
110
111 m_fd = fd;
112
113 return init_core();
114}
115
116bool PipeWireCore::init_core()
117{
118 if (m_fd > 0) {
119 m_pwCore = pw_context_connect_fd(m_pwContext, m_fd, nullptr, 0);
120 } else {
121 m_pwCore = pw_context_connect(m_pwContext, nullptr, 0);
122 }
123 if (!m_pwCore) {
124 m_error = i18n("Failed to connect to PipeWire");
125 qCWarning(PIPEWIRE_LOGGING) << "error:" << m_error << m_fd;
126 return false;
127 }
128
129 if (pw_loop_iterate(m_pwMainLoop, 0) < 0) {
130 qCWarning(PIPEWIRE_LOGGING) << "Failed to start main PipeWire loop";
131 m_error = i18n("Failed to start main PipeWire loop");
132 return false;
133 }
134
135 pw_core_add_listener(m_pwCore, &m_coreListener, &s_pwCoreEvents, this);
136 return true;
137}
138
139QSharedPointer<PipeWireCore> PipeWireCore::fetch(int fd)
140{
142 QSharedPointer<PipeWireCore> ret = global.localData().value(fd).toStrongRef();
143 if (!ret) {
144 ret.reset(new PipeWireCore);
145 if (ret->init(fd)) {
146 global.localData().insert(fd, ret);
147 }
148 }
149 return ret;
150}
151
152QString PipeWireCore::error() const
153{
154 return m_error;
155}
KLocalizedString KI18N_EXPORT ki18n(const char *text)
QString i18n(const char *text, const TYPE &arg...)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
PHONON_EXPORT Notifier * notifier()
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
QString fromUtf8(QByteArrayView str)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:05:20 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.