diff --git a/src/backends/PipeWire/CMakeLists.txt b/src/backends/PipeWire/CMakeLists.txt index e3451cc..a59ff31 100644 --- a/src/backends/PipeWire/CMakeLists.txt +++ b/src/backends/PipeWire/CMakeLists.txt @@ -37,6 +37,8 @@ target_include_directories(be_pipewire target_sources(be_pipewire PRIVATE + "EventManager.cpp" + "EventManager.hpp" "Library.cpp" "Library.hpp" "PipeWire.cpp" diff --git a/src/backends/PipeWire/EventManager.cpp b/src/backends/PipeWire/EventManager.cpp new file mode 100644 index 0000000..3aa20d2 --- /dev/null +++ b/src/backends/PipeWire/EventManager.cpp @@ -0,0 +1,72 @@ +// Copyright 2024 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#include "EventManager.hpp" + +#include "Library.hpp" + +#include +#include + +using namespace pipewire; + +static constexpr auto NODE_TYPE_ID = "PipeWire:Interface:Node"; + +static constexpr pw_registry_events eventsRegistry = { PW_VERSION_REGISTRY_EVENTS, + [](void *userData, const uint32_t id, + const uint32_t /*permissions*/, const char *type, + const uint32_t /*version*/, const spa_dict * /*props*/) { + if (spa_streq(type, NODE_TYPE_ID)) { + auto &manager = *static_cast< EventManager * >(userData); + + new NodeInfoData(manager, id); + } + }, + [](void *userData, const uint32_t id) { + auto &manager = *static_cast< EventManager* >(userData); + + manager.feedback().nodeRemoved(id); + } }; + +static constexpr pw_node_events eventsNode = { PW_VERSION_NODE_EVENTS, + [](void *userData, const pw_node_info *info) { + auto data = static_cast< NodeInfoData* >(userData); + + data->manager().feedback().nodeAdded(info); + + delete data; + }, + nullptr }; + +EventManager::EventManager(pw_core *core, const Feedback &feedback) + : m_feedback(feedback), m_registry(pw_core_get_registry(core, PW_VERSION_REGISTRY, 0)) { + if (m_registry) { + pw_registry_add_listener(m_registry, &m_registryListener, &eventsRegistry, this); + } +} + +EventManager::~EventManager() { + if (m_registry) { + spa_hook_remove(&m_registryListener); + lib().proxy_destroy(reinterpret_cast< pw_proxy * >(m_registry)); + } +} + +NodeInfoData::NodeInfoData(EventManager &manager, const uint32_t id) + : m_manager(manager), + m_proxy(static_cast< pw_proxy * >(pw_registry_bind(manager.registry(), id, NODE_TYPE_ID, PW_VERSION_NODE, 0))), + m_listener() { + if (m_proxy) { + lib().proxy_add_object_listener(m_proxy, &m_listener, &eventsNode, this); + } +} + +NodeInfoData::~NodeInfoData() { + spa_hook_remove(&m_listener); + + if (m_proxy) { + lib().proxy_destroy(m_proxy); + } +} diff --git a/src/backends/PipeWire/EventManager.hpp b/src/backends/PipeWire/EventManager.hpp new file mode 100644 index 0000000..465d839 --- /dev/null +++ b/src/backends/PipeWire/EventManager.hpp @@ -0,0 +1,56 @@ +// Copyright 2024 The Mumble Developers. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file at the root of the +// Mumble source tree or at . + +#ifndef CROSSAUDIO_SRC_BACKENDS_PIPEWIRE_EVENTMANAGER_HPP +#define CROSSAUDIO_SRC_BACKENDS_PIPEWIRE_EVENTMANAGER_HPP + +#include +#include + +#include + +struct pw_core; +struct pw_node_info; +struct pw_proxy; +struct pw_registry; + +namespace pipewire { +class EventManager { +public: + struct Feedback { + std::function< void(const pw_node_info *info) > nodeAdded; + std::function< void(uint32_t id) > nodeRemoved; + }; + + EventManager(pw_core *core, const Feedback &feedback); + ~EventManager(); + + constexpr auto &feedback() { return m_feedback; } + constexpr auto registry() { return m_registry; } + +private: + EventManager(const EventManager &) = delete; + EventManager &operator=(const EventManager &) = delete; + + Feedback m_feedback; + pw_registry *m_registry; + spa_hook m_registryListener; +}; + +class NodeInfoData { +public: + NodeInfoData(EventManager &manager, const uint32_t id); + ~NodeInfoData(); + + constexpr auto &manager() { return m_manager; } + +private: + EventManager &m_manager; + pw_proxy *m_proxy; + spa_hook m_listener; +}; +} // namespace pipewire + +#endif diff --git a/src/backends/PipeWire/PipeWire.cpp b/src/backends/PipeWire/PipeWire.cpp index f371370..c25acda 100644 --- a/src/backends/PipeWire/PipeWire.cpp +++ b/src/backends/PipeWire/PipeWire.cpp @@ -5,6 +5,9 @@ #include "PipeWire.hpp" +#include "EventManager.hpp" +#include "Library.hpp" + #include "Node.h" #include "crossaudio/Macros.h" @@ -177,31 +180,6 @@ constexpr BE_Impl PipeWire_Impl = { }; // clang-format on -static constexpr auto NODE_TYPE_ID = "PipeWire:Interface:Node"; - -static constexpr pw_registry_events eventsRegistry = { - PW_VERSION_REGISTRY_EVENTS, - [](void *userData, const uint32_t id, const uint32_t /*permissions*/, const char *type, const uint32_t /*version*/, - const spa_dict * /*props*/) { - if (spa_streq(type, NODE_TYPE_ID)) { - auto &engine = *static_cast< Engine * >(userData); - - new NodeInfoData(engine, id); - } - }, - [](void *userData, const uint32_t id) { static_cast< Engine * >(userData)->removeNode(id); } -}; - -static constexpr pw_node_events eventsNode = { PW_VERSION_NODE_EVENTS, - [](void *userData, const pw_node_info *info) { - auto data = static_cast< NodeInfoData* >(userData); - - data->engine().addNode(info); - - delete data; - }, - nullptr }; - Engine::Engine() : m_threadLoop(nullptr), m_context(nullptr), m_core(nullptr) { if ((m_threadLoop = lib().thread_loop_new(nullptr, nullptr))) { m_context = lib().context_new(lib().thread_loop_get_loop(m_threadLoop), nullptr, 0); @@ -234,6 +212,9 @@ void Engine::unlock() { } ErrorCode Engine::start() { + const EventManager::Feedback eventManagerFeedback{ .nodeAdded = [this](const pw_node_info *info) { addNode(info); }, + .nodeRemoved = [this](const uint32_t id) { removeNode(id); } }; + if (m_core) { return CROSSAUDIO_EC_INIT; } @@ -242,8 +223,7 @@ ErrorCode Engine::start() { return CROSSAUDIO_EC_CONNECT; } - m_registry = pw_core_get_registry(m_core, PW_VERSION_REGISTRY, 0); - pw_registry_add_listener(m_registry, &m_registryListener, &eventsRegistry, this); + m_eventManager = std::make_unique< EventManager >(m_core, eventManagerFeedback); if (lib().thread_loop_start(m_threadLoop) < 0) { stop(); @@ -256,14 +236,10 @@ ErrorCode Engine::start() { ErrorCode Engine::stop() { lock(); - spa_hook_remove(&m_registryListener); + m_eventManager.reset(); m_nodes.clear(); - if (m_registry) { - lib().proxy_destroy(reinterpret_cast< pw_proxy * >(m_registry)); - } - if (m_core) { lib().core_disconnect(m_core); m_core = nullptr; @@ -472,23 +448,6 @@ ErrorCode Flux::nameSet(const char *name) { return lib().stream_update_properties(m_stream, &dict) >= 1 ? CROSSAUDIO_EC_OK : CROSSAUDIO_EC_GENERIC; } -NodeInfoData::NodeInfoData(Engine &engine, const uint32_t id) - : m_engine(engine), - m_proxy(static_cast< pw_proxy * >(pw_registry_bind(engine.m_registry, id, NODE_TYPE_ID, PW_VERSION_NODE, 0))), - m_listener() { - if (m_proxy) { - lib().proxy_add_object_listener(m_proxy, &m_listener, &eventsNode, this); - } -} - -NodeInfoData::~NodeInfoData() { - spa_hook_remove(&m_listener); - - if (m_proxy) { - lib().proxy_destroy(m_proxy); - } -} - static void processInput(void *userData) { auto &flux = *static_cast< Flux * >(userData); diff --git a/src/backends/PipeWire/PipeWire.hpp b/src/backends/PipeWire/PipeWire.hpp index 6fcb20d..a7ee3c2 100644 --- a/src/backends/PipeWire/PipeWire.hpp +++ b/src/backends/PipeWire/PipeWire.hpp @@ -6,8 +6,6 @@ #ifndef CROSSAUDIO_SRC_BACKENDS_PIPEWIRE_PIPEWIRE_HPP #define CROSSAUDIO_SRC_BACKENDS_PIPEWIRE_PIPEWIRE_HPP -#include "Library.hpp" - #include "Backend.h" #include "crossaudio/Direction.h" @@ -16,6 +14,7 @@ #include "crossaudio/Node.h" #include +#include #include #include @@ -27,10 +26,15 @@ using FluxFeedback = CrossAudio_FluxFeedback; using Direction = CrossAudio_Direction; using Nodes = CrossAudio_Nodes; +struct pw_context; +struct pw_core; struct pw_node_info; -struct spa_audio_info_raw; +struct pw_stream; +struct pw_thread_loop; namespace pipewire { +class EventManager; + class Engine { public: class Locker { @@ -66,19 +70,19 @@ class Engine { ErrorCode start(); ErrorCode stop(); - void addNode(const pw_node_info *info); - void removeNode(const uint32_t id); - pw_thread_loop *m_threadLoop; pw_context *m_context; pw_core *m_core; - pw_registry *m_registry; - spa_hook m_registryListener; + + std::unique_ptr< EventManager > m_eventManager; private: Engine(const Engine &) = delete; Engine &operator=(const Engine &) = delete; + void addNode(const pw_node_info *info); + void removeNode(uint32_t id); + std::map< uint32_t, Node > m_nodes; }; @@ -106,21 +110,6 @@ class Flux { Flux(const Flux &) = delete; Flux &operator=(const Flux &) = delete; }; - -class NodeInfoData { -public: - NodeInfoData(Engine &engine, const uint32_t id); - ~NodeInfoData(); - - constexpr explicit operator bool() { return m_proxy; } - - constexpr Engine &engine() { return m_engine; } - -private: - Engine &m_engine; - pw_proxy *m_proxy; - spa_hook m_listener; -}; } // namespace pipewire // Backend API