mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
node: keep better track of driver node
When a session disappears, try to move nodes to another session.
This commit is contained in:
parent
1bff5afe05
commit
ef9fcd1093
7 changed files with 144 additions and 72 deletions
|
|
@ -84,7 +84,6 @@ struct spa_graph_callbacks {
|
|||
#define spa_graph_finish(g) ((g)->callbacks->finish((g)->callbacks_data))
|
||||
|
||||
struct spa_graph {
|
||||
#define SPA_GRAPH_FLAG_DRIVER (1 << 0)
|
||||
uint32_t flags; /* flags */
|
||||
struct spa_graph_node *parent; /* parent node or NULL when driver */
|
||||
struct spa_graph_state *state; /* state of graph */
|
||||
|
|
|
|||
|
|
@ -689,11 +689,9 @@ static int impl_node_process(struct spa_node *node)
|
|||
struct impl *impl = this->impl;
|
||||
int status;
|
||||
|
||||
impl->client_node->node->driver_node = impl->this.node->driver_node;
|
||||
impl->ctrl.min_size = impl->ctrl.max_size =
|
||||
impl->this.node->driver_node->rt.quantum->size;
|
||||
|
||||
|
||||
spa_log_trace(this->log, "%p: process %d", this, impl->ctrl.max_size);
|
||||
|
||||
if (impl->use_converter) {
|
||||
|
|
@ -897,8 +895,9 @@ static void client_node_destroy(void *data)
|
|||
struct impl *impl = data;
|
||||
pw_log_debug("client-stream %p: destroy", &impl->this);
|
||||
|
||||
spa_hook_remove(&impl->client_node_listener);
|
||||
pw_node_set_driver(impl->client_node->node, NULL);
|
||||
|
||||
spa_hook_remove(&impl->client_node_listener);
|
||||
spa_hook_remove(&impl->node_listener);
|
||||
pw_node_destroy(impl->this.node);
|
||||
cleanup(impl);
|
||||
|
|
@ -935,14 +934,22 @@ static void node_destroy(void *data)
|
|||
|
||||
pw_log_debug("client-stream %p: destroy", &impl->this);
|
||||
|
||||
spa_hook_remove(&impl->node_listener);
|
||||
spa_hook_remove(&impl->client_node_listener);
|
||||
pw_client_node_destroy(impl->client_node);
|
||||
cleanup(impl);
|
||||
}
|
||||
|
||||
static void node_driver_changed(void *data, struct pw_node *driver)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
impl->client_node->node->driver_node = driver;
|
||||
}
|
||||
|
||||
static const struct pw_node_events node_events = {
|
||||
PW_VERSION_NODE_EVENTS,
|
||||
.destroy = node_destroy,
|
||||
.driver_changed = node_driver_changed,
|
||||
};
|
||||
|
||||
/** Create a new client stream
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ struct link_data {
|
|||
};
|
||||
|
||||
|
||||
static int handle_autoconnect(struct impl *impl, struct pw_node *node,
|
||||
const struct pw_properties *props);
|
||||
|
||||
/** \endcond */
|
||||
|
||||
static void link_data_remove(struct link_data *data)
|
||||
|
|
@ -151,10 +154,17 @@ static void session_destroy(struct session *sess)
|
|||
|
||||
spa_list_remove(&sess->l);
|
||||
spa_hook_remove(&sess->node_listener);
|
||||
spa_list_for_each_safe(ni, t, &sess->node_list, l)
|
||||
node_info_free(ni);
|
||||
if (sess->dsp)
|
||||
if (sess->dsp) {
|
||||
spa_hook_remove(&sess->dsp_listener);
|
||||
pw_node_destroy(sess->dsp);
|
||||
}
|
||||
spa_list_for_each_safe(ni, t, &sess->node_list, l) {
|
||||
pw_node_set_state(ni->node, PW_NODE_STATE_SUSPENDED);
|
||||
pw_node_set_driver(ni->node, NULL);
|
||||
handle_autoconnect(ni->impl, ni->node,
|
||||
pw_node_get_properties(ni->node));
|
||||
node_info_free(ni);
|
||||
}
|
||||
free(sess);
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +316,7 @@ static void reconfigure_session(struct session *sess)
|
|||
|
||||
sess->node->rt.quantum->rate.num = 1;
|
||||
sess->node->rt.quantum->rate.denom = sess->sample_rate;
|
||||
sess->node->rt.quantum->size = buffer_size;
|
||||
sess->node->rt.quantum->size = sess->buffer_size;
|
||||
|
||||
pw_log_info("module %p: driver node:%p quantum:%d/%d",
|
||||
impl, sess->node, sess->sample_rate, buffer_size);
|
||||
|
|
@ -358,6 +368,8 @@ static int link_session_dsp(struct session *session)
|
|||
}
|
||||
pw_link_register(session->link, NULL, pw_module_get_global(impl->module), NULL);
|
||||
|
||||
reconfigure_session(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +424,7 @@ static int find_session(void *data, struct session *sess)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
||||
static int handle_autoconnect(struct impl *impl, struct pw_node *node,
|
||||
const struct pw_properties *props)
|
||||
{
|
||||
struct pw_node *peer;
|
||||
|
|
@ -423,6 +435,11 @@ static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
|||
struct session *session;
|
||||
struct node_info *info;
|
||||
uint32_t sample_rate, buffer_size;
|
||||
int res;
|
||||
|
||||
str = pw_properties_get(props, PW_NODE_PROP_AUTOCONNECT);
|
||||
if (str == NULL || !pw_properties_parse_bool(str))
|
||||
return 0;
|
||||
|
||||
sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
|
||||
|
|
@ -455,16 +472,16 @@ static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
|||
else if (strcmp(category, "Capture") == 0)
|
||||
find.media_class = "Audio/Source";
|
||||
else
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
else if (strcmp(media, "Video") == 0) {
|
||||
if (strcmp(category, "Capture") == 0)
|
||||
find.media_class = "Video/Source";
|
||||
else
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
else
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
str = pw_properties_get(props, PW_NODE_PROP_TARGET_NODE);
|
||||
if (str != NULL)
|
||||
|
|
@ -479,7 +496,7 @@ static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
|||
spa_list_for_each(session, &impl->session_list, l)
|
||||
find_session(&find, session);
|
||||
if (find.sess == NULL)
|
||||
return;
|
||||
return -ENOENT;
|
||||
|
||||
session = find.sess;
|
||||
|
||||
|
|
@ -488,24 +505,24 @@ static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
|||
else if (strcmp(category, "Playback") == 0)
|
||||
direction = PW_DIRECTION_INPUT;
|
||||
else
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
if (exclusive || session->dsp == NULL) {
|
||||
if (exclusive && !spa_list_is_empty(&session->node_list)) {
|
||||
pw_log_warn("session busy, can't get exclusive access");
|
||||
return;
|
||||
return -EBUSY;
|
||||
}
|
||||
if (session->link != NULL) {
|
||||
pw_log_warn("session busy with DSP");
|
||||
return;
|
||||
return -EBUSY;
|
||||
}
|
||||
peer = session->node;
|
||||
session->exclusive = exclusive;
|
||||
}
|
||||
else {
|
||||
if (session->link == NULL) {
|
||||
if (link_session_dsp(session) < 0)
|
||||
return;
|
||||
if ((res = link_session_dsp(session)) < 0)
|
||||
return res;
|
||||
}
|
||||
peer = session->dsp;
|
||||
}
|
||||
|
|
@ -520,13 +537,15 @@ static void handle_autoconnect(struct impl *impl, struct pw_node *node,
|
|||
info->buffer_size = buffer_size;
|
||||
spa_list_init(&info->links);
|
||||
|
||||
spa_list_append(&info->session->node_list, &info->l);
|
||||
spa_list_append(&session->node_list, &info->l);
|
||||
|
||||
pw_node_add_listener(node, &info->node_listener, &node_info_events, info);
|
||||
|
||||
pw_node_for_each_port(peer, direction, on_peer_port, info);
|
||||
|
||||
reconfigure_session(session);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void node_destroy(void *data)
|
||||
|
|
@ -647,9 +666,7 @@ static int on_global(void *data, struct pw_global *global)
|
|||
|
||||
properties = pw_node_get_properties(node);
|
||||
|
||||
str = pw_properties_get(properties, PW_NODE_PROP_AUTOCONNECT);
|
||||
if (str != NULL && pw_properties_parse_bool(str)) {
|
||||
handle_autoconnect(impl, node, properties);
|
||||
if (handle_autoconnect(impl, node, properties) == 1) {
|
||||
return 0;
|
||||
}
|
||||
else if ((str = pw_properties_get(properties, "media.class")) == NULL)
|
||||
|
|
|
|||
|
|
@ -791,9 +791,11 @@ do_activate_link(struct spa_loop *loop,
|
|||
spa_graph_port_add(&this->input->rt.mix_node, &this->rt.mix[SPA_DIRECTION_INPUT].port);
|
||||
spa_graph_port_link(&this->rt.mix[SPA_DIRECTION_OUTPUT].port,
|
||||
&this->rt.mix[SPA_DIRECTION_INPUT].port);
|
||||
this->rt.link.signal_data = &this->input->node->rt.root;
|
||||
spa_graph_link_add(&this->output->node->rt.root,
|
||||
this->input->node->rt.root.state,
|
||||
&this->rt.link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1032,6 +1034,7 @@ do_deactivate_link(struct spa_loop *loop,
|
|||
spa_graph_port_remove(&this->rt.mix[SPA_DIRECTION_OUTPUT].port);
|
||||
spa_graph_port_remove(&this->rt.mix[SPA_DIRECTION_INPUT].port);
|
||||
spa_graph_link_remove(&this->rt.link);
|
||||
this->rt.link.signal_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1169,46 +1172,22 @@ move_graph(struct spa_graph *dst, struct spa_graph *src)
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_join_graphs(struct spa_loop *loop,
|
||||
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||
static int find_driver(struct pw_link *this)
|
||||
{
|
||||
struct pw_link *this = user_data;
|
||||
struct spa_graph *in_graph, *out_graph;
|
||||
struct spa_graph_node *in_root, *out_root;
|
||||
struct pw_node *out_driver, *in_driver;
|
||||
|
||||
in_root = &this->input->node->rt.root;
|
||||
out_root = &this->output->node->rt.root;
|
||||
out_driver = this->output->node->driver_node;
|
||||
in_driver = this->input->node->driver_node;
|
||||
|
||||
in_graph = in_root->graph;
|
||||
out_graph = out_root->graph;
|
||||
pw_log_debug("link %p: drivers %p/%p", this, out_driver, in_driver);
|
||||
|
||||
pw_log_debug("link %p: roots %p/%p graphs %p/%p %d/%d", this,
|
||||
in_root, out_root, in_graph, out_graph,
|
||||
in_graph->flags, out_graph->flags);
|
||||
if (out_driver == in_driver)
|
||||
return 0;
|
||||
|
||||
if (in_graph != out_graph) {
|
||||
struct spa_graph *src, *dst;
|
||||
bool out_driver;
|
||||
|
||||
out_driver = SPA_FLAG_CHECK(out_graph->flags, SPA_GRAPH_FLAG_DRIVER);
|
||||
|
||||
if (out_driver) {
|
||||
src = in_graph;
|
||||
dst = out_graph;
|
||||
this->input->node->driver_node = this->output->node->driver_node;
|
||||
pw_log_debug("link %p: in_graph to out_graph %p", this, this->output->node);
|
||||
}
|
||||
else {
|
||||
src = out_graph;
|
||||
dst = in_graph;
|
||||
this->output->node->driver_node = this->input->node->driver_node;
|
||||
pw_log_debug("link %p: out_graph to in_graph %p", this, this->input->node);
|
||||
}
|
||||
move_graph(dst, src);
|
||||
}
|
||||
this->rt.link.signal = spa_graph_link_signal_node;
|
||||
this->rt.link.signal_data = in_root;
|
||||
if (out_driver->driver)
|
||||
pw_node_set_driver(in_driver, out_driver);
|
||||
else
|
||||
pw_node_set_driver(out_driver, in_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1291,12 +1270,13 @@ struct pw_link *pw_link_new(struct pw_core *core,
|
|||
pw_port_init_mix(output, &this->rt.mix[SPA_DIRECTION_OUTPUT]);
|
||||
pw_port_init_mix(input, &this->rt.mix[SPA_DIRECTION_INPUT]);
|
||||
|
||||
this->rt.link.signal = spa_graph_link_signal_node;
|
||||
|
||||
pw_log_debug("link %p: constructed %p:%d.%d -> %p:%d.%d", impl,
|
||||
output_node, output->port_id, this->rt.mix[SPA_DIRECTION_OUTPUT].port.port_id,
|
||||
input_node, input->port_id, this->rt.mix[SPA_DIRECTION_INPUT].port.port_id);
|
||||
|
||||
pw_loop_invoke(output->node->data_loop,
|
||||
do_join_graphs, SPA_ID_INVALID, NULL, 0, false, this);
|
||||
find_driver(this);
|
||||
|
||||
spa_hook_list_call(&output->listener_list, struct pw_port_events, link_added, this);
|
||||
spa_hook_list_call(&input->listener_list, struct pw_port_events, link_added, this);
|
||||
|
|
|
|||
|
|
@ -403,6 +403,58 @@ int pw_node_initialized(struct pw_node *this)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_move_nodes(struct spa_loop *loop,
|
||||
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||
{
|
||||
struct impl *src = user_data;
|
||||
struct pw_node *this = &src->this;
|
||||
struct impl *dst = *(struct impl **)data;
|
||||
struct spa_graph_node *n, *t;
|
||||
|
||||
spa_graph_node_remove(&this->rt.root);
|
||||
spa_graph_node_add(&src->driver_graph, &this->rt.root);
|
||||
|
||||
spa_list_for_each_safe(n, t, &src->driver_graph.nodes, link) {
|
||||
spa_graph_node_remove(n);
|
||||
spa_graph_node_add(&dst->driver_graph, n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pw_node_set_driver(struct pw_node *node, struct pw_node *driver)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
|
||||
struct pw_node *n, *t;
|
||||
|
||||
pw_log_debug("node %p: driver:%p current:%p", node, driver, node->driver_node);
|
||||
|
||||
if (driver == NULL)
|
||||
driver = node;
|
||||
if (node->driver_node == driver)
|
||||
return 0;
|
||||
|
||||
spa_list_remove(&node->driver_link);
|
||||
spa_list_append(&driver->driver_list, &node->driver_link);
|
||||
node->driver_node = driver;
|
||||
|
||||
spa_list_for_each_safe(n, t, &node->driver_list, driver_link) {
|
||||
spa_list_remove(&n->driver_link);
|
||||
spa_list_append(&driver->driver_list, &n->driver_link);
|
||||
n->driver_node = driver;
|
||||
spa_hook_list_call(&n->listener_list, struct pw_node_events,
|
||||
driver_changed, driver);
|
||||
pw_log_debug("node %p: add %p", driver, n);
|
||||
}
|
||||
pw_loop_invoke(node->data_loop,
|
||||
do_move_nodes, SPA_ID_INVALID, &driver, sizeof(struct pw_node *),
|
||||
true, impl);
|
||||
|
||||
spa_hook_list_call(&node->listener_list, struct pw_node_events, driver_changed, driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_properties(struct pw_node *node)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
|
||||
|
|
@ -418,13 +470,6 @@ static void check_properties(struct pw_node *node)
|
|||
else
|
||||
node->driver = false;
|
||||
|
||||
if (node->driver)
|
||||
SPA_FLAG_SET(impl->driver_graph.flags, SPA_GRAPH_FLAG_DRIVER);
|
||||
else
|
||||
SPA_FLAG_UNSET(impl->driver_graph.flags, SPA_GRAPH_FLAG_DRIVER);
|
||||
|
||||
node->driver_node = node;
|
||||
|
||||
pw_log_debug("node %p: graph %p driver:%d", node, &impl->driver_graph, node->driver);
|
||||
|
||||
}
|
||||
|
|
@ -478,6 +523,7 @@ struct pw_node *pw_node_new(struct pw_core *core,
|
|||
|
||||
this->data_loop = core->data_loop;
|
||||
|
||||
spa_list_init(&this->driver_list);
|
||||
spa_list_init(&this->resource_list);
|
||||
|
||||
spa_hook_list_init(&this->listener_list);
|
||||
|
|
@ -490,6 +536,7 @@ struct pw_node *pw_node_new(struct pw_core *core,
|
|||
spa_list_init(&this->output_ports);
|
||||
pw_map_init(&this->output_port_map, 64, 64);
|
||||
|
||||
|
||||
spa_graph_init(&impl->driver_graph, &impl->driver_state);
|
||||
spa_graph_data_init(&impl->driver_data, &impl->driver_graph);
|
||||
spa_graph_set_callbacks(&impl->driver_graph,
|
||||
|
|
@ -498,7 +545,6 @@ struct pw_node *pw_node_new(struct pw_core *core,
|
|||
this->rt.driver = &impl->driver_graph;
|
||||
this->rt.activation = &impl->root_activation;
|
||||
spa_graph_node_init(&this->rt.root, &this->rt.activation->state);
|
||||
spa_graph_node_add(&impl->driver_graph, &this->rt.root);
|
||||
|
||||
spa_graph_init(&impl->graph, &impl->graph_state);
|
||||
spa_graph_data_init(&impl->graph_data, &impl->graph);
|
||||
|
|
@ -517,6 +563,10 @@ struct pw_node *pw_node_new(struct pw_core *core,
|
|||
|
||||
check_properties(this);
|
||||
|
||||
this->driver_node = this;
|
||||
spa_list_append(&this->driver_list, &this->driver_link);
|
||||
spa_graph_node_add(&impl->driver_graph, &this->rt.root);
|
||||
|
||||
return this;
|
||||
|
||||
no_mem:
|
||||
|
|
@ -682,6 +732,7 @@ void pw_node_destroy(struct pw_node *node)
|
|||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
|
||||
struct pw_resource *resource, *tmp;
|
||||
struct pw_node *n, *t;
|
||||
struct pw_port *port, *tmpp;
|
||||
|
||||
pw_log_debug("node %p: destroy", impl);
|
||||
|
|
@ -689,12 +740,21 @@ void pw_node_destroy(struct pw_node *node)
|
|||
|
||||
pause_node(node);
|
||||
|
||||
pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node);
|
||||
pw_log_debug("node %p: driver node %p", impl, node->driver_node);
|
||||
|
||||
if (node->registered) {
|
||||
spa_list_remove(&node->link);
|
||||
/* move all nodes driven by us to their own driver */
|
||||
spa_list_for_each_safe(n, t, &node->driver_list, driver_link)
|
||||
pw_node_set_driver(n, NULL);
|
||||
|
||||
if (node->driver_node != node) {
|
||||
/* remove ourself from the (other) driver node */
|
||||
spa_list_remove(&node->driver_link);
|
||||
pw_loop_invoke(node->data_loop, do_node_remove, 1, NULL, 0, true, node);
|
||||
}
|
||||
|
||||
if (node->registered)
|
||||
spa_list_remove(&node->link);
|
||||
|
||||
pw_log_debug("node %p: unlink ports", node);
|
||||
spa_list_for_each(port, &node->input_ports, link)
|
||||
pw_port_unlink(port);
|
||||
|
|
@ -703,11 +763,13 @@ void pw_node_destroy(struct pw_node *node)
|
|||
|
||||
pw_log_debug("node %p: destroy ports", node);
|
||||
spa_list_for_each_safe(port, tmpp, &node->input_ports, link) {
|
||||
spa_hook_list_call(&node->listener_list, struct pw_node_events, port_removed, port);
|
||||
spa_hook_list_call(&node->listener_list, struct pw_node_events,
|
||||
port_removed, port);
|
||||
pw_port_destroy(port);
|
||||
}
|
||||
spa_list_for_each_safe(port, tmpp, &node->output_ports, link) {
|
||||
spa_hook_list_call(&node->listener_list, struct pw_node_events, port_removed, port);
|
||||
spa_hook_list_call(&node->listener_list, struct pw_node_events,
|
||||
port_removed, port);
|
||||
pw_port_destroy(port);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ struct pw_node_events {
|
|||
/** an event is emited */
|
||||
void (*event) (void *data, const struct spa_event *event);
|
||||
|
||||
/** the driver of the node changed */
|
||||
void (*driver_changed) (void *data, struct pw_node *driver);
|
||||
|
||||
/** the node wants to process the graph */
|
||||
void (*process) (void *data);
|
||||
/** the node has a buffer to reuse */
|
||||
|
|
|
|||
|
|
@ -265,6 +265,8 @@ struct pw_node {
|
|||
* is selected to drive the graph */
|
||||
|
||||
struct pw_node *driver_node;
|
||||
struct spa_list driver_list;
|
||||
struct spa_list driver_link;
|
||||
|
||||
struct spa_clock *clock; /**< handle to SPA clock if any */
|
||||
struct spa_node *node; /**< SPA node implementation */
|
||||
|
|
@ -615,6 +617,8 @@ int pw_node_update_ports(struct pw_node *node);
|
|||
|
||||
int pw_node_initialized(struct pw_node *node);
|
||||
|
||||
int pw_node_set_driver(struct pw_node *node, struct pw_node *driver);
|
||||
|
||||
/** Activate a link \memberof pw_link
|
||||
* Starts the negotiation of formats and buffers on \a link and then
|
||||
* starts data streaming */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue