Fix node details
This commit is contained in:
parent
21cd3bd3f9
commit
9ac56d0d0b
2 changed files with 146 additions and 20 deletions
145
src/warppipe.cpp
145
src/warppipe.cpp
|
|
@ -10,6 +10,7 @@
|
|||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <pipewire/impl-module.h>
|
||||
#include <pipewire/keys.h>
|
||||
#include <pipewire/link.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
|
|
@ -104,6 +105,7 @@ bool MatchesRule(const NodeInfo& node, const RuleMatch& match) {
|
|||
|
||||
struct StreamData {
|
||||
pw_stream* stream = nullptr;
|
||||
pw_impl_module* module = nullptr;
|
||||
spa_hook listener{};
|
||||
pw_thread_loop* loop = nullptr;
|
||||
bool is_source = false;
|
||||
|
|
@ -349,6 +351,8 @@ struct Client::Impl {
|
|||
std::unordered_map<uint32_t, std::unique_ptr<NodeProxyData>> node_proxies;
|
||||
std::unordered_map<uint32_t, uint32_t> node_channel_counts;
|
||||
|
||||
std::unordered_set<uint32_t> loopback_internal_nodes;
|
||||
|
||||
std::unordered_map<uint32_t, MeterState> meter_states;
|
||||
std::unordered_set<uint32_t> metered_nodes;
|
||||
MeterState master_meter;
|
||||
|
|
@ -789,6 +793,7 @@ void Client::Impl::ClearCache() {
|
|||
nodes.clear();
|
||||
ports.clear();
|
||||
links.clear();
|
||||
loopback_internal_nodes.clear();
|
||||
pending_auto_links.clear();
|
||||
auto_link_claimed_pairs.clear();
|
||||
policy_sync_pending = false;
|
||||
|
|
@ -873,6 +878,103 @@ Result<uint32_t> Client::Impl::CreateVirtualStreamLocked(std::string_view name,
|
|||
}
|
||||
}
|
||||
|
||||
if (options.behavior == VirtualBehavior::kLoopback && options.target_node) {
|
||||
std::string args;
|
||||
args += "{ node.description = \"" + display_name + "\"";
|
||||
args += " node.name = \"" + stream_name + "\"";
|
||||
args += " audio.channels = " + std::to_string(options.format.channels);
|
||||
args += " audio.rate = " + std::to_string(options.format.rate);
|
||||
args += " capture.props = {";
|
||||
args += " target.object = \"" + *options.target_node + "\"";
|
||||
args += " stream.capture.sink = true";
|
||||
args += " node.passive = true";
|
||||
args += " node.dont-reconnect = true";
|
||||
args += " }";
|
||||
args += " playback.props = {";
|
||||
args += " node.name = \"" + stream_name + "\"";
|
||||
args += " node.description = \"" + display_name + "\"";
|
||||
args += " media.class = \"" + media_class_value + "\"";
|
||||
args += " node.virtual = true";
|
||||
args += " }";
|
||||
args += " }";
|
||||
|
||||
pw_impl_module* mod = pw_context_load_module(context,
|
||||
"libpipewire-module-loopback", args.c_str(), nullptr);
|
||||
if (!mod) {
|
||||
return {Status::Error(StatusCode::kUnavailable, "failed to load loopback module"), 0};
|
||||
}
|
||||
|
||||
auto sync_status = SyncLocked();
|
||||
if (!sync_status.ok()) {
|
||||
pw_impl_module_destroy(mod);
|
||||
return {sync_status, 0};
|
||||
}
|
||||
|
||||
uint32_t node_id = SPA_ID_INVALID;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex);
|
||||
for (const auto& entry : nodes) {
|
||||
if (entry.second.name == stream_name) {
|
||||
node_id = entry.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node_id == SPA_ID_INVALID) {
|
||||
int wait_attempts = 0;
|
||||
while (node_id == SPA_ID_INVALID && wait_attempts < 3) {
|
||||
int wait_res = pw_thread_loop_timed_wait(thread_loop, kSyncWaitSeconds);
|
||||
if (wait_res == -ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
auto sync2 = SyncLocked();
|
||||
(void)sync2;
|
||||
std::lock_guard<std::mutex> lock(cache_mutex);
|
||||
for (const auto& entry : nodes) {
|
||||
if (entry.second.name == stream_name) {
|
||||
node_id = entry.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++wait_attempts;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_id == SPA_ID_INVALID) {
|
||||
pw_impl_module_destroy(mod);
|
||||
return {Status::Error(StatusCode::kTimeout, "loopback node did not appear in registry"), 0};
|
||||
}
|
||||
|
||||
std::string capture_name = "input." + stream_name;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex);
|
||||
for (const auto& entry : nodes) {
|
||||
if (entry.second.name == capture_name) {
|
||||
loopback_internal_nodes.insert(entry.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto stream_data = std::make_unique<StreamData>();
|
||||
stream_data->module = mod;
|
||||
stream_data->loop = thread_loop;
|
||||
stream_data->is_source = is_source;
|
||||
stream_data->loopback = true;
|
||||
stream_data->target_node = *options.target_node;
|
||||
stream_data->name = stream_name;
|
||||
stream_data->rate = options.format.rate;
|
||||
stream_data->channels = options.format.channels;
|
||||
stream_data->node_id = node_id;
|
||||
stream_data->ready = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex);
|
||||
virtual_streams.emplace(node_id, std::move(stream_data));
|
||||
}
|
||||
return {Status::Ok(), node_id};
|
||||
}
|
||||
|
||||
pw_properties* props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, media_category,
|
||||
PW_KEY_MEDIA_ROLE, "Music",
|
||||
|
|
@ -888,9 +990,6 @@ Result<uint32_t> Client::Impl::CreateVirtualStreamLocked(std::string_view name,
|
|||
if (node_group) {
|
||||
pw_properties_set(props, PW_KEY_NODE_GROUP, node_group);
|
||||
}
|
||||
if (options.behavior == VirtualBehavior::kLoopback && options.target_node) {
|
||||
pw_properties_set(props, PW_KEY_TARGET_OBJECT, options.target_node->c_str());
|
||||
}
|
||||
|
||||
pw_stream* stream = pw_stream_new(core, stream_name.c_str(), props);
|
||||
if (!stream) {
|
||||
|
|
@ -901,7 +1000,7 @@ Result<uint32_t> Client::Impl::CreateVirtualStreamLocked(std::string_view name,
|
|||
stream_data->stream = stream;
|
||||
stream_data->loop = thread_loop;
|
||||
stream_data->is_source = is_source;
|
||||
stream_data->loopback = options.behavior == VirtualBehavior::kLoopback;
|
||||
stream_data->loopback = false;
|
||||
if (options.target_node) {
|
||||
stream_data->target_node = *options.target_node;
|
||||
}
|
||||
|
|
@ -926,9 +1025,6 @@ Result<uint32_t> Client::Impl::CreateVirtualStreamLocked(std::string_view name,
|
|||
|
||||
enum pw_direction direction = is_source ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
|
||||
enum pw_stream_flags flags = PW_STREAM_FLAG_MAP_BUFFERS;
|
||||
if (options.behavior == VirtualBehavior::kLoopback && options.target_node) {
|
||||
flags = static_cast<pw_stream_flags>(flags | PW_STREAM_FLAG_AUTOCONNECT);
|
||||
}
|
||||
int res = pw_stream_connect(stream, direction, PW_ID_ANY, flags, params, 1);
|
||||
if (res < 0) {
|
||||
pw_stream_destroy(stream);
|
||||
|
|
@ -1042,7 +1138,11 @@ void Client::Impl::DisconnectLocked() {
|
|||
}
|
||||
for (auto& entry : streams) {
|
||||
StreamData* stream_data = entry.second.get();
|
||||
if (stream_data && stream_data->stream) {
|
||||
if (!stream_data) continue;
|
||||
if (stream_data->module) {
|
||||
pw_impl_module_destroy(stream_data->module);
|
||||
stream_data->module = nullptr;
|
||||
} else if (stream_data->stream) {
|
||||
pw_stream_disconnect(stream_data->stream);
|
||||
pw_stream_destroy(stream_data->stream);
|
||||
stream_data->stream = nullptr;
|
||||
|
|
@ -1827,6 +1927,9 @@ Result<std::vector<NodeInfo>> Client::ListNodes() {
|
|||
std::vector<NodeInfo> items;
|
||||
items.reserve(impl_->nodes.size());
|
||||
for (const auto& entry : impl_->nodes) {
|
||||
if (impl_->loopback_internal_nodes.count(entry.first)) {
|
||||
continue;
|
||||
}
|
||||
NodeInfo info = entry.second;
|
||||
if (!info.is_virtual &&
|
||||
impl_->virtual_streams.find(entry.first) !=
|
||||
|
|
@ -1933,10 +2036,28 @@ Status Client::RemoveNode(NodeId node) {
|
|||
owned_stream = std::move(it->second);
|
||||
impl_->virtual_streams.erase(it);
|
||||
}
|
||||
if (owned_stream && owned_stream->stream) {
|
||||
pw_stream_disconnect(owned_stream->stream);
|
||||
pw_stream_destroy(owned_stream->stream);
|
||||
owned_stream->stream = nullptr;
|
||||
if (owned_stream) {
|
||||
if (owned_stream->module) {
|
||||
std::string capture_name = "input." + owned_stream->name;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->cache_mutex);
|
||||
for (auto it = impl_->loopback_internal_nodes.begin();
|
||||
it != impl_->loopback_internal_nodes.end(); ++it) {
|
||||
auto node_it = impl_->nodes.find(*it);
|
||||
if (node_it != impl_->nodes.end() &&
|
||||
node_it->second.name == capture_name) {
|
||||
impl_->loopback_internal_nodes.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pw_impl_module_destroy(owned_stream->module);
|
||||
owned_stream->module = nullptr;
|
||||
} else if (owned_stream->stream) {
|
||||
pw_stream_disconnect(owned_stream->stream);
|
||||
pw_stream_destroy(owned_stream->stream);
|
||||
owned_stream->stream = nullptr;
|
||||
}
|
||||
}
|
||||
pw_thread_loop_unlock(impl_->thread_loop);
|
||||
impl_->AutoSave();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue