#ifdef HAVE_CONFIG_H #include "config.h" #include "config_unix.h" #endif #include "debug.h" #include "utils/color_out.h" #include "pipewire_common.hpp" static void on_core_done(void *data, uint32_t /*id*/, int seq) { auto s = static_cast(data); s->pw_last_seq = seq; pw_thread_loop_signal(s->pipewire_loop.get(), false); } static void on_core_error(void * /*data*/, uint32_t id, int seq, int res, const char *message) { log_msg(LOG_LEVEL_ERROR, "Pipewire error: id:%d seq:%x res:%d msg: %s\n", id, seq, res, message); } static const struct pw_core_events core_events = { PW_VERSION_CORE_EVENTS, .info = nullptr, .done = on_core_done, .ping = nullptr, .error = on_core_error, .remove_id = nullptr, .bound_id = nullptr, .add_mem = nullptr, .remove_mem = nullptr, #if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 67) .bound_props = nullptr, #endif }; bool initialize_pw_common(pipewire_state_common& s, int fd){ s.init_guard.init(); s.pipewire_loop.reset(pw_thread_loop_new("Playback", nullptr)); s.pipewire_context.reset(pw_context_new(pw_thread_loop_get_loop(s.pipewire_loop.get()), nullptr, 0)); //TODO check return if(fd != -1) s.pipewire_core.reset(pw_context_connect_fd(s.pipewire_context.get(), fd, nullptr, 0)); else s.pipewire_core.reset(pw_context_connect(s.pipewire_context.get(), nullptr, 0)); pw_core_add_listener(s.pipewire_core.get(), &s.core_listener.get(), &core_events, &s); pw_thread_loop_start(s.pipewire_loop.get()); return true; } static void on_registry_event_global(void *data, uint32_t /*id*/, uint32_t /*permissions*/, const char *type, uint32_t /*version*/, const struct spa_dict *props) { auto result = static_cast *>(data); std::string_view type_sv(type); if(type_sv != "PipeWire:Interface:Node") return; Pipewire_device dev; for(unsigned i = 0; i < props->n_items; i++){ std::string_view key(props->items[i].key); std::string_view val(props->items[i].value); if(key == "node.description") dev.description = val; else if(key == "node.nick") dev.nick = val; else if(key == "media.class") dev.media_class = val; else if(key == "node.name") dev.name = val; else if(key == "object.serial") dev.serial = val; } result->push_back(std::move(dev)); } std::vector get_pw_device_list(){ pipewire_state_common s; initialize_pw_common(s); std::vector result; pipewire_thread_loop_lock_guard lock(s.pipewire_loop.get()); pw_registry_uniq registry(pw_core_get_registry(s.pipewire_core.get(), PW_VERSION_REGISTRY, 0)); const static pw_registry_events registry_events = { PW_VERSION_REGISTRY_EVENTS, .global = on_registry_event_global, .global_remove = nullptr }; spa_hook_uniq registry_listener; pw_registry_add_listener(registry.get(), ®istry_listener.get(), ®istry_events, &result); s.pw_pending_seq = pw_core_sync(s.pipewire_core.get(), PW_ID_CORE, s.pw_pending_seq); int wait_seq = s.pw_pending_seq; do{ pw_thread_loop_wait(s.pipewire_loop.get()); } while(s.pw_last_seq < wait_seq); return result; } pipewire_state_common::~pipewire_state_common(){ if(pipewire_loop) pw_thread_loop_stop(pipewire_loop.get()); } void print_devices(std::string_view media_class){ auto devices = get_pw_device_list(); for(const auto& dev : devices){ if(dev.media_class == media_class){ color_printf("\t%s: %s (%s)\n", dev.serial.c_str(), dev.description.c_str(), dev.name.c_str()); } } }