This commit adds very basic node objects to the core. This is just a starting point, the nodes don't do anything useful yet. A node represents a "routing endpoint" - the purpose is to make routing easier. There are input nodes and output nodes, which can be connected together. Generally speaking, sources and sink inputs map to input nodes and sinks and source outputs map to output nodes. The nodes form a new logical routing layer, which is an addition, not replacement, to the current "low level" layer of sinks, sink inputs and so on. One goal is to be able to easily route any input to any output. For example, with the node interface it should be easy to route a source to a sink, without needing to care about the details, such as setting up module-loopback. Routing sink inputs to source outputs should be possible too, perhaps causing a null sink to be created between the streams. Another goal is to support new kinds of routing endpoints that are not well suited to be implemented as sinks, sources or streams. One example would be audio paths that exist in hardware only (like cellular audio in many phone designs) that still have some routing options. Another example would be a "gateway node" that makes streams go to a remote PulseAudio as separate streams. The gateway node implementation could dynamically create private tunnel sinks for each stream. In this first version the nodes have very few attributes, but the intention is to add as much attributes as necessary for routing policy modules to make good automatic routing decisions. This patch is based on work by Janos Kovacs. --- src/Makefile.am | 1 + src/pulsecore/core.c | 1 + src/pulsecore/core.h | 2 +- src/pulsecore/namereg.c | 2 +- src/pulsecore/namereg.h | 3 +- src/pulsecore/node.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/node.h | 89 ++++++++++++++++++++++++++ 7 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 src/pulsecore/node.c create mode 100644 src/pulsecore/node.h diff --git a/src/Makefile.am b/src/Makefile.am index 2521670..f779515 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -882,6 +882,7 @@ libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \ pulsecore/module.c pulsecore/module.h \ pulsecore/msgobject.c pulsecore/msgobject.h \ pulsecore/namereg.c pulsecore/namereg.h \ + pulsecore/node.c pulsecore/node.h \ pulsecore/object.c pulsecore/object.h \ pulsecore/play-memblockq.c pulsecore/play-memblockq.h \ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 2ca50c2..c3e4a2b 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -99,6 +99,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) { c->source_outputs = pa_idxset_new(NULL, NULL); c->modules = pa_idxset_new(NULL, NULL); c->scache = pa_idxset_new(NULL, NULL); + c->nodes = pa_idxset_new(NULL, NULL); c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 261cc99..9dbc4b3 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -139,7 +139,7 @@ struct pa_core { pa_mainloop_api *mainloop; /* idxset of all kinds of entities */ - pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache; + pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *nodes; /* Some hashmaps for all sorts of entities */ pa_hashmap *namereg, *shared; diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 334e00d..e0cd276 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -119,7 +119,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t if (!*name) return NULL; - if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) && + if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD || type == PA_NAMEREG_NODE) && !pa_namereg_is_valid_name(name)) { if (fail) diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index b5a976d..80fd5d6 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -31,7 +31,8 @@ typedef enum pa_namereg_type { PA_NAMEREG_SINK, PA_NAMEREG_SOURCE, PA_NAMEREG_SAMPLE, - PA_NAMEREG_CARD + PA_NAMEREG_CARD, + PA_NAMEREG_NODE } pa_namereg_type_t; const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail); diff --git a/src/pulsecore/node.c b/src/pulsecore/node.c new file mode 100644 index 0000000..3f1ca7c --- /dev/null +++ b/src/pulsecore/node.c @@ -0,0 +1,164 @@ +/*** + This file is part of PulseAudio. + + Copyright (c) 2012 Intel Corporation + Janos Kovacs <jankovac503 at gmail.com> + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulsecore/core-util.h> +#include <pulsecore/namereg.h> +#include <pulsecore/strbuf.h> + +#include "node.h" + +pa_node_new_data *pa_node_new_data_init(pa_node_new_data *data) { + pa_assert(data); + + pa_zero(*data); + data->direction = PA_DIRECTION_OUTPUT; + + return data; +} + +void pa_node_new_data_set_fallback_name_prefix(pa_node_new_data *data, const char* prefix) { + pa_assert(data); + + pa_xfree(data->fallback_name_prefix); + data->fallback_name_prefix = pa_xstrdup(prefix); +} + +void pa_node_new_data_set_description(pa_node_new_data *data, const char *description) { + pa_assert(data); + + pa_xfree(data->description); + data->description = pa_xstrdup(description); +} + +void pa_node_new_data_set_type(pa_node_new_data *data, pa_node_type_t type) { + pa_assert(data); + + data->type = type; +} + +void pa_node_new_data_set_direction(pa_node_new_data *data, pa_direction_t direction) { + pa_assert(data); + + data->direction = direction; +} + +void pa_node_new_data_done(pa_node_new_data *data) { + pa_assert(data); + + pa_xfree(data->description); + pa_xfree(data->fallback_name_prefix); +} + +pa_node *pa_node_new(pa_core *core, pa_node_new_data *data) { + bool use_fallback_name_prefix; + pa_strbuf *name_buf; + char *name = NULL; + pa_node *n = NULL; + const char *registered_name = NULL; + + pa_assert(core); + pa_assert(data); + pa_assert(data->description); + + use_fallback_name_prefix = !!data->fallback_name_prefix; + + name_buf = pa_strbuf_new(); + + /* Automatic name generation code will appear here... */ + + if (use_fallback_name_prefix) + pa_strbuf_printf(name_buf, "%s-", data->fallback_name_prefix); + + pa_strbuf_puts(name_buf, data->direction == PA_DIRECTION_OUTPUT ? "output" : "input"); + + name = pa_strbuf_tostring_free(name_buf); + + n = pa_xnew0(pa_node, 1); + n->core = core; + n->state = PA_NODE_STATE_INIT; + + if (!(registered_name = pa_namereg_register(core, name, PA_NAMEREG_NODE, n, false))) { + pa_log("Failed to register name %s.", name); + goto fail; + } + + pa_xfree(name); + + n->name = pa_xstrdup(registered_name); + n->description = pa_xstrdup(data->description); + n->type = data->type; + n->direction = data->direction; + + return n; + +fail: + pa_xfree(name); + pa_node_free(n); + + return NULL; +} + +void pa_node_free(pa_node *node) { + pa_assert(node); + + if (node->state == PA_NODE_STATE_LINKED) + pa_node_unlink(node); + + pa_xfree(node->description); + + if (node->name) { + pa_namereg_unregister(node->core, node->name); + pa_xfree(node->name); + } + + pa_xfree(node); +} + +void pa_node_put(pa_node *node) { + pa_assert(node); + pa_assert(node->state == PA_NODE_STATE_INIT); + pa_assert(node->owner); + + pa_assert_se(pa_idxset_put(node->core->nodes, node, &node->index) >= 0); + + node->state = PA_NODE_STATE_LINKED; + + pa_log_debug("Created node %s.", node->name); +} + +void pa_node_unlink(pa_node *node) { + pa_assert(node); + pa_assert(node->state != PA_NODE_STATE_INIT); + + if (node->state == PA_NODE_STATE_UNLINKED) + return; + + pa_log_debug("Unlinking node %s.", node->name); + + pa_assert_se(pa_idxset_remove_by_index(node->core->nodes, node->index)); + + node->state = PA_NODE_STATE_UNLINKED; +} diff --git a/src/pulsecore/node.h b/src/pulsecore/node.h new file mode 100644 index 0000000..305853f --- /dev/null +++ b/src/pulsecore/node.h @@ -0,0 +1,89 @@ +#ifndef foonodehfoo +#define foonodehfoo + +/*** + This file is part of PulseAudio. + + Copyright (c) 2012 Intel Corporation + Janos Kovacs <jankovac503 at gmail.com> + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_node_new_data pa_node_new_data; +typedef struct pa_node pa_node; + +#include <pulsecore/core.h> + +/* The node type determines what the owner pointer of pa_node points to. */ +typedef enum { + PA_NODE_TYPE_PORT, /* owner: pa_port */ + PA_NODE_TYPE_SINK, /* owner: pa_sink */ + PA_NODE_TYPE_SOURCE, /* owner: pa_source */ + PA_NODE_TYPE_SINK_INPUT, /* owner: pa_sink_input */ + PA_NODE_TYPE_SOURCE_OUTPUT /* owner: pa_source_output */ +} pa_node_type_t; + +typedef enum { + PA_NODE_STATE_INIT, + PA_NODE_STATE_LINKED, + PA_NODE_STATE_UNLINKED +} pa_node_state_t; + +struct pa_node_new_data { + /* Node names are generated automatically as much as possible, but + * sometimes the available information for automatic generation isn't + * sufficient, in which case the generated node names would be just "input" + * or "output". In such cases the fallback name prefix, if set, is used to + * generate slightly more informative names, such as "jack-output" for JACK + * output nodes (in this example the fallback prefix would be "jack"). */ + char *fallback_name_prefix; + + char *description; + + pa_node_type_t type; + pa_direction_t direction; +}; + +struct pa_node { + pa_core *core; + + uint32_t index; + char *name; + char *description; + + pa_node_type_t type; + pa_direction_t direction; + + pa_node_state_t state; + + void *owner; +}; + +pa_node_new_data *pa_node_new_data_init(pa_node_new_data *data); +void pa_node_new_data_set_fallback_name_prefix(pa_node_new_data *data, const char *prefix); +void pa_node_new_data_set_description(pa_node_new_data *data, const char *description); +void pa_node_new_data_set_type(pa_node_new_data *data, pa_node_type_t type); +void pa_node_new_data_set_direction(pa_node_new_data *data, pa_direction_t direction); +void pa_node_new_data_done(pa_node_new_data *data); + +pa_node *pa_node_new(pa_core *core, pa_node_new_data *data); +void pa_node_free(pa_node *node); + +void pa_node_put(pa_node *node); +void pa_node_unlink(pa_node *node); + +#endif -- 1.8.1.2