node: add port_id

Decouple the SPA port ids from the pinos port ids, this allows us to
more easily link and relink things and do dynamic connection later.
Implement multiple output ports on a pinos node.
This commit is contained in:
Wim Taymans 2016-09-12 18:29:59 +02:00
parent 36bcdaa4bc
commit e34ef88dac
5 changed files with 213 additions and 89 deletions

View file

@ -214,12 +214,16 @@ on_port_added (PinosNode *node, PinosDirection direction, guint port_id, PinosCl
return; return;
} }
new_port = pinos_node_get_free_port_id (target, pinos_direction_reverse (direction)); new_port = pinos_node_get_free_port (target, pinos_direction_reverse (direction));
if (new_port == SPA_ID_INVALID) { if (new_port == SPA_ID_INVALID) {
g_debug ("daemon %p: can't create free port", this); g_debug ("daemon %p: can't create free port", this);
return; return;
} }
if (direction == PINOS_DIRECTION_OUTPUT)
link = pinos_node_link (node, port_id, target, new_port, NULL, NULL); link = pinos_node_link (node, port_id, target, new_port, NULL, NULL);
else
link = pinos_node_link (target, new_port, node, port_id, NULL, NULL);
pinos_client_add_object (client, G_OBJECT (link)); pinos_client_add_object (client, G_OBJECT (link));
g_object_unref (link); g_object_unref (link);
} }

View file

@ -57,8 +57,10 @@ enum
PROP_DAEMON, PROP_DAEMON,
PROP_OBJECT_PATH, PROP_OBJECT_PATH,
PROP_OUTPUT_NODE, PROP_OUTPUT_NODE,
PROP_OUTPUT_ID,
PROP_OUTPUT_PORT, PROP_OUTPUT_PORT,
PROP_INPUT_NODE, PROP_INPUT_NODE,
PROP_INPUT_ID,
PROP_INPUT_PORT, PROP_INPUT_PORT,
PROP_FORMAT_FILTER, PROP_FORMAT_FILTER,
PROP_PROPERTIES, PROP_PROPERTIES,
@ -94,6 +96,10 @@ pinos_link_get_property (GObject *_object,
g_value_set_object (value, this->output_node); g_value_set_object (value, this->output_node);
break; break;
case PROP_OUTPUT_ID:
g_value_set_uint (value, this->output_id);
break;
case PROP_OUTPUT_PORT: case PROP_OUTPUT_PORT:
g_value_set_uint (value, this->output_port); g_value_set_uint (value, this->output_port);
break; break;
@ -102,6 +108,10 @@ pinos_link_get_property (GObject *_object,
g_value_set_object (value, this->input_node); g_value_set_object (value, this->input_node);
break; break;
case PROP_INPUT_ID:
g_value_set_uint (value, this->input_id);
break;
case PROP_INPUT_PORT: case PROP_INPUT_PORT:
g_value_set_uint (value, this->input_port); g_value_set_uint (value, this->input_port);
break; break;
@ -142,6 +152,10 @@ pinos_link_set_property (GObject *_object,
this->output_node = g_value_dup_object (value); this->output_node = g_value_dup_object (value);
break; break;
case PROP_OUTPUT_ID:
this->output_id = g_value_get_uint (value);
break;
case PROP_OUTPUT_PORT: case PROP_OUTPUT_PORT:
this->output_port = g_value_get_uint (value); this->output_port = g_value_get_uint (value);
break; break;
@ -150,6 +164,10 @@ pinos_link_set_property (GObject *_object,
this->input_node = g_value_dup_object (value); this->input_node = g_value_dup_object (value);
break; break;
case PROP_INPUT_ID:
this->input_id = g_value_get_uint (value);
break;
case PROP_INPUT_PORT: case PROP_INPUT_PORT:
this->input_port = g_value_get_uint (value); this->input_port = g_value_get_uint (value);
break; break;
@ -279,7 +297,7 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state)
const SpaPortInfo *iinfo, *oinfo; const SpaPortInfo *iinfo, *oinfo;
SpaPortInfoFlags in_flags, out_flags; SpaPortInfoFlags in_flags, out_flags;
if (in_state != SPA_NODE_STATE_READY || out_state != SPA_NODE_STATE_READY) if (in_state < SPA_NODE_STATE_READY || out_state < SPA_NODE_STATE_READY)
return SPA_RESULT_OK; return SPA_RESULT_OK;
g_debug ("link %p: doing alloc buffers %p %p", this, this->output_node, this->input_node); g_debug ("link %p: doing alloc buffers %p %p", this, this->output_node, this->input_node);
@ -296,9 +314,6 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state)
spa_debug_port_info (oinfo); spa_debug_port_info (oinfo);
spa_debug_port_info (iinfo); spa_debug_port_info (iinfo);
priv->n_in_buffers = 16;
priv->n_out_buffers = 16;
if ((oinfo->flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) && if ((oinfo->flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) &&
(iinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS)) { (iinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS)) {
out_flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS; out_flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS;
@ -309,6 +324,7 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state)
in_flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS; in_flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS;
} else if ((oinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS) && } else if ((oinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS) &&
(iinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS)) { (iinfo->flags & SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS)) {
priv->n_in_buffers = 16;
out_flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; out_flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
in_flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; in_flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
@ -330,7 +346,13 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state)
goto error; goto error;
} }
if (in_state > SPA_NODE_STATE_READY)
in_flags &= ~SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
if (out_state > SPA_NODE_STATE_READY)
out_flags &= ~SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
if (in_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) { if (in_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) {
priv->n_in_buffers = 16;
if ((res = spa_node_port_alloc_buffers (this->input_node->node, this->input_port, if ((res = spa_node_port_alloc_buffers (this->input_node->node, this->input_port,
oinfo->params, oinfo->n_params, oinfo->params, oinfo->n_params,
priv->in_buffers, &priv->n_in_buffers)) < 0) { priv->in_buffers, &priv->n_in_buffers)) < 0) {
@ -339,6 +361,7 @@ do_allocation (PinosLink *this, SpaNodeState in_state, SpaNodeState out_state)
} }
} }
if (out_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) { if (out_flags & SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS) {
priv->n_out_buffers = 16;
if ((res = spa_node_port_alloc_buffers (this->output_node->node, this->output_port, if ((res = spa_node_port_alloc_buffers (this->output_node->node, this->output_port,
iinfo->params, iinfo->n_params, iinfo->params, iinfo->n_params,
priv->out_buffers, &priv->n_out_buffers)) < 0) { priv->out_buffers, &priv->n_out_buffers)) < 0) {
@ -460,8 +483,9 @@ pinos_link_constructed (GObject * object)
G_OBJECT_CLASS (pinos_link_parent_class)->constructed (object); G_OBJECT_CLASS (pinos_link_parent_class)->constructed (object);
on_property_notify (G_OBJECT (this), NULL, this); on_property_notify (G_OBJECT (this), NULL, this);
g_debug ("link %p: constructed %p:%d -> %p:%d", this, this->output_node, this->output_port, g_debug ("link %p: constructed %p:%d:%d -> %p:%d:%d", this,
this->input_node, this->input_port); this->output_node, this->output_id, this->output_port,
this->input_node, this->input_id, this->input_port);
link_register_object (this); link_register_object (this);
check_states (this); check_states (this);
@ -535,6 +559,18 @@ pinos_link_class_init (PinosLinkClass * klass)
G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_OUTPUT_ID,
g_param_spec_uint ("output-id",
"Output Id",
"The output id",
0,
G_MAXUINT,
-1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_OUTPUT_PORT, PROP_OUTPUT_PORT,
g_param_spec_uint ("output-port", g_param_spec_uint ("output-port",
@ -557,6 +593,18 @@ pinos_link_class_init (PinosLinkClass * klass)
G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_INPUT_ID,
g_param_spec_uint ("input-id",
"Input Id",
"The input id",
0,
G_MAXUINT,
-1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_INPUT_PORT, PROP_INPUT_PORT,
g_param_spec_uint ("input-port", g_param_spec_uint ("input-port",

View file

@ -48,9 +48,11 @@ struct _PinosLink {
GObject object; GObject object;
PinosNode *output_node; PinosNode *output_node;
guint output_port; guint output_id;
uint32_t output_port;
PinosNode *input_node; PinosNode *input_node;
guint input_port; guint input_id;
uint32_t input_port;
PinosLinkPrivate *priv; PinosLinkPrivate *priv;
}; };

View file

@ -37,6 +37,10 @@
#define PINOS_NODE_GET_PRIVATE(node) \ #define PINOS_NODE_GET_PRIVATE(node) \
(G_TYPE_INSTANCE_GET_PRIVATE ((node), PINOS_TYPE_NODE, PinosNodePrivate)) (G_TYPE_INSTANCE_GET_PRIVATE ((node), PINOS_TYPE_NODE, PinosNodePrivate))
typedef struct {
PinosLink *link;
} NodeLink;
struct _PinosNodePrivate struct _PinosNodePrivate
{ {
PinosDaemon *daemon; PinosDaemon *daemon;
@ -69,7 +73,10 @@ struct _PinosNodePrivate
gboolean running; gboolean running;
pthread_t thread; pthread_t thread;
GHashTable *links; GArray *output_links;
guint n_used_output_links;
GArray *input_links;
guint n_used_input_links;
SpaClock *clock; SpaClock *clock;
}; };
@ -469,38 +476,47 @@ on_node_event (SpaNode *node, SpaNodeEvent *event, void *user_data)
} }
case SPA_NODE_EVENT_TYPE_HAVE_OUTPUT: case SPA_NODE_EVENT_TYPE_HAVE_OUTPUT:
{ {
PinosLink *link;
SpaPortOutputInfo oinfo[1] = { 0, }; SpaPortOutputInfo oinfo[1] = { 0, };
SpaResult res; SpaResult res;
guint i;
if ((res = spa_node_port_pull_output (node, 1, oinfo)) < 0) { if ((res = spa_node_port_pull_output (node, 1, oinfo)) < 0) {
g_warning ("node %p: got pull error %d, %d", this, res, oinfo[0].status); g_warning ("node %p: got pull error %d, %d", this, res, oinfo[0].status);
break; break;
} }
link = g_hash_table_lookup (priv->links, GUINT_TO_POINTER (oinfo[0].port_id)); for (i = 0; i < priv->output_links->len; i++) {
if (link) { NodeLink *link = &g_array_index (priv->output_links, NodeLink, i);
PinosLink *pl = link->link;
SpaPortInputInfo iinfo[1]; SpaPortInputInfo iinfo[1];
iinfo[0].port_id = link->input_port; if (pl == NULL || pl->output_node->node != node || pl->output_port != oinfo[0].port_id)
continue;
iinfo[0].port_id = pl->input_port;
iinfo[0].buffer_id = oinfo[0].buffer_id; iinfo[0].buffer_id = oinfo[0].buffer_id;
iinfo[0].flags = SPA_PORT_INPUT_FLAG_NONE; iinfo[0].flags = SPA_PORT_INPUT_FLAG_NONE;
if ((res = spa_node_port_push_input (link->input_node->node, 1, iinfo)) < 0) if ((res = spa_node_port_push_input (pl->input_node->node, 1, iinfo)) < 0)
g_warning ("node %p: error pushing buffer: %d, %d", this, res, iinfo[0].status); g_warning ("node %p: error pushing buffer: %d, %d", this, res, iinfo[0].status);
} }
break; break;
} }
case SPA_NODE_EVENT_TYPE_REUSE_BUFFER: case SPA_NODE_EVENT_TYPE_REUSE_BUFFER:
{ {
PinosLink *link;
SpaResult res; SpaResult res;
SpaNodeEventReuseBuffer *rb = event->data; SpaNodeEventReuseBuffer *rb = event->data;
guint i;
link = g_hash_table_lookup (priv->links, GUINT_TO_POINTER (rb->port_id)); for (i = 0; i < priv->input_links->len; i++) {
if (link) { NodeLink *link = &g_array_index (priv->input_links, NodeLink, i);
if ((res = spa_node_port_reuse_buffer (link->output_node->node, PinosLink *pl = link->link;
link->output_port,
if (pl == NULL || pl->input_node->node != node || pl->input_port != rb->port_id)
continue;
if ((res = spa_node_port_reuse_buffer (pl->output_node->node,
pl->output_port,
rb->buffer_id)) < 0) rb->buffer_id)) < 0)
g_warning ("node %p: error reuse buffer: %d", node, res); g_warning ("node %p: error reuse buffer: %d", node, res);
} }
@ -715,7 +731,8 @@ pinos_node_dispose (GObject * obj)
node_unregister_object (node); node_unregister_object (node);
g_hash_table_unref (priv->links); g_array_free (priv->input_links, TRUE);
g_array_free (priv->output_links, TRUE);
G_OBJECT_CLASS (pinos_node_parent_class)->dispose (obj); G_OBJECT_CLASS (pinos_node_parent_class)->dispose (obj);
} }
@ -881,7 +898,8 @@ pinos_node_init (PinosNode * node)
priv->state = PINOS_NODE_STATE_SUSPENDED; priv->state = PINOS_NODE_STATE_SUSPENDED;
pinos_node1_set_state (priv->iface, PINOS_NODE_STATE_SUSPENDED); pinos_node1_set_state (priv->iface, PINOS_NODE_STATE_SUSPENDED);
priv->links = g_hash_table_new (g_direct_hash, g_direct_equal); priv->input_links = g_array_new (FALSE, TRUE, sizeof (NodeLink));
priv->output_links = g_array_new (FALSE, TRUE, sizeof (NodeLink));
} }
/** /**
@ -1042,63 +1060,102 @@ pinos_node_remove (PinosNode *node)
g_signal_emit (node, signals[SIGNAL_REMOVE], 0, NULL); g_signal_emit (node, signals[SIGNAL_REMOVE], 0, NULL);
} }
/** static uint32_t
* pinos_node_get_free_port_id: get_free_node_port (PinosNode *node,
* @node: a #PinosNode
* @direction: a #PinosDirection
*
* Find a new unused port id in @node with @direction
*
* Returns: the new port id of %SPA_INVALID_ID on error
*/
guint
pinos_node_get_free_port_id (PinosNode *node,
PinosDirection direction) PinosDirection direction)
{ {
PinosNodePrivate *priv; PinosNodePrivate *priv = node->priv;
guint i, free_port = 0, n_ports, max_ports; guint i, free_port, n_ports, max_ports;
uint32_t *ports; uint32_t *ports;
g_return_val_if_fail (PINOS_IS_NODE (node), -1);
priv = node->priv;
if (direction == PINOS_DIRECTION_INPUT) { if (direction == PINOS_DIRECTION_INPUT) {
max_ports = priv->max_input_ports; max_ports = priv->max_input_ports;
n_ports = priv->n_input_ports; n_ports = priv->n_input_ports;
ports = priv->input_port_ids; ports = priv->input_port_ids;
free_port = 0;
} else { } else {
max_ports = priv->max_output_ports; max_ports = priv->max_output_ports;
n_ports = priv->n_output_ports; n_ports = priv->n_output_ports;
ports = priv->output_port_ids; ports = priv->output_port_ids;
free_port = priv->max_input_ports;
} }
if (max_ports == n_ports)
return SPA_ID_INVALID;
g_debug ("node %p: direction %d max %u, n %u", node, direction, max_ports, n_ports); g_debug ("node %p: direction %d max %u, n %u", node, direction, max_ports, n_ports);
for (i = 0; i < n_ports; i++) { for (i = 0; i < n_ports; i++) {
if (free_port < ports[i]) if (free_port < ports[i])
break; break;
if (g_hash_table_lookup (priv->links, GUINT_TO_POINTER (free_port)) == NULL && free_port < max_ports)
return free_port;
free_port = ports[i] + 1; free_port = ports[i] + 1;
} }
if (free_port >= max_ports) if (free_port >= max_ports)
return -1; return SPA_ID_INVALID;
return free_port; return free_port;
} }
/**
* pinos_node_get_free_port:
* @node: a #PinosNode
* @direction: a #PinosDirection
*
* Find a new unused port id in @node with @direction
*
* Returns: the new port id or %SPA_ID_INVALID on error
*/
guint
pinos_node_get_free_port (PinosNode *node,
PinosDirection direction)
{
PinosNodePrivate *priv;
guint i, n_ports;
NodeLink *links;
g_return_val_if_fail (PINOS_IS_NODE (node), SPA_ID_INVALID);
priv = node->priv;
if (direction == PINOS_DIRECTION_INPUT) {
n_ports = priv->input_links->len;
links = (NodeLink *)priv->input_links->data;
} else {
n_ports = priv->output_links->len;
links = (NodeLink *)priv->output_links->data;
}
for (i = 0; i < n_ports; i++) {
if (!links[i].link)
return i;
}
return n_ports;
}
static void static void
do_remove_link (PinosLink *link, PinosNode *node) do_remove_link (PinosLink *link, PinosNode *node)
{ {
g_hash_table_remove (link->output_node->priv->links, GUINT_TO_POINTER (link->output_port)); guint i, n_links;
if (g_hash_table_size (link->output_node->priv->links) == 0) GArray *links;
pinos_node_report_idle (link->output_node);
g_hash_table_remove (link->input_node->priv->links, GUINT_TO_POINTER (link->input_port)); links = link->output_node->priv->output_links;
if (g_hash_table_size (link->input_node->priv->links) == 0) n_links = links->len;
for (i = 0; i < n_links; i++) {
NodeLink *l = &g_array_index (links, NodeLink, i);
if (l->link == link) {
l->link = NULL;
if (--link->output_node->priv->n_used_output_links == 0)
pinos_node_report_idle (link->output_node);
}
}
links = link->input_node->priv->input_links;
n_links = links->len;
for (i = 0; i < n_links; i++) {
NodeLink *l = &g_array_index (links, NodeLink, i);
if (l->link == link) {
l->link = NULL;
if (--link->input_node->priv->n_used_input_links == 0)
pinos_node_report_idle (link->input_node); pinos_node_report_idle (link->input_node);
}
}
} }
/** /**
@ -1121,61 +1178,74 @@ do_remove_link (PinosLink *link, PinosNode *node)
*/ */
PinosLink * PinosLink *
pinos_node_link (PinosNode *output_node, pinos_node_link (PinosNode *output_node,
guint output_port, guint output_id,
PinosNode *input_node, PinosNode *input_node,
guint input_port, guint input_id,
GPtrArray *format_filter, GPtrArray *format_filter,
PinosProperties *properties) PinosProperties *properties)
{ {
PinosNodePrivate *priv; PinosNodePrivate *priv;
PinosLink *link; NodeLink *olink, *ilink;
PinosLink *pl;
g_return_val_if_fail (PINOS_IS_NODE (output_node), NULL); g_return_val_if_fail (PINOS_IS_NODE (output_node), NULL);
g_return_val_if_fail (PINOS_IS_NODE (input_node), NULL); g_return_val_if_fail (PINOS_IS_NODE (input_node), NULL);
if (get_port_direction (output_node, output_port) != PINOS_DIRECTION_OUTPUT) {
PinosNode *tmp;
guint tmp_port;
tmp = output_node;
output_node = input_node;
input_node = tmp;
tmp_port = output_port;
output_port = input_port;
input_port = tmp_port;
}
priv = output_node->priv; priv = output_node->priv;
link = g_hash_table_lookup (priv->links, GUINT_TO_POINTER (output_port)); g_debug ("node %p: link %u %p:%u", output_node, output_id, input_node, input_id);
if (link) {
link->input_node = input_node; if (output_id >= priv->output_links->len)
link->input_port = input_port; g_array_set_size (priv->output_links, output_id + 1);
g_object_ref (link); if (input_id >= input_node->priv->input_links->len)
g_array_set_size (input_node->priv->input_links, input_id + 1);
olink = &g_array_index (priv->output_links, NodeLink, output_id);
ilink = &g_array_index (input_node->priv->input_links, NodeLink, input_id);
pl = olink->link;
if (pl) {
/* FIXME */
pl->input_node = input_node;
pl->input_id = input_id;
g_object_ref (pl);
} else { } else {
uint32_t input_port, output_port;
if (output_node->priv->clock) if (output_node->priv->clock)
input_node->priv->clock = output_node->priv->clock; input_node->priv->clock = output_node->priv->clock;
link = g_object_new (PINOS_TYPE_LINK, output_port = get_free_node_port (output_node, PINOS_DIRECTION_OUTPUT);
if (output_port == SPA_ID_INVALID)
output_port = output_node->priv->output_port_ids[0];
input_port = get_free_node_port (input_node, PINOS_DIRECTION_INPUT);
if (input_port == SPA_ID_INVALID)
input_port = input_node->priv->input_port_ids[0];
pl = g_object_new (PINOS_TYPE_LINK,
"daemon", priv->daemon, "daemon", priv->daemon,
"output-node", output_node, "output-node", output_node,
"output-id", output_id,
"output-port", output_port, "output-port", output_port,
"input-node", input_node, "input-node", input_node,
"input-id", input_id,
"input-port", input_port, "input-port", input_port,
"format-filter", format_filter, "format-filter", format_filter,
"properties", properties, "properties", properties,
NULL); NULL);
g_signal_connect (link, g_signal_connect (pl,
"remove", "remove",
(GCallback) do_remove_link, (GCallback) do_remove_link,
output_node); output_node);
g_hash_table_insert (priv->links, GUINT_TO_POINTER (output_port), link); output_node->priv->n_used_output_links++;
g_hash_table_insert (input_node->priv->links, GUINT_TO_POINTER (input_port), link); input_node->priv->n_used_input_links++;
olink->link = pl;
ilink->link = pl;
} }
return link; return pl;
} }
/** /**
@ -1194,7 +1264,7 @@ pinos_node_get_links (PinosNode *node)
g_return_val_if_fail (PINOS_IS_NODE (node), NULL); g_return_val_if_fail (PINOS_IS_NODE (node), NULL);
priv = node->priv; priv = node->priv;
return g_hash_table_get_values (priv->links); return NULL;
} }
static void static void

View file

@ -86,13 +86,13 @@ PinosDaemon * pinos_node_get_daemon (PinosNode *node);
const gchar * pinos_node_get_sender (PinosNode *node); const gchar * pinos_node_get_sender (PinosNode *node);
const gchar * pinos_node_get_object_path (PinosNode *node); const gchar * pinos_node_get_object_path (PinosNode *node);
guint pinos_node_get_free_port_id (PinosNode *node, guint pinos_node_get_free_port (PinosNode *node,
PinosDirection direction); PinosDirection direction);
PinosLink * pinos_node_link (PinosNode *output_node, PinosLink * pinos_node_link (PinosNode *output_node,
guint output_port, guint output_id,
PinosNode *input_node, PinosNode *input_node,
guint input_port, guint input_id,
GPtrArray *format_filter, GPtrArray *format_filter,
PinosProperties *properties); PinosProperties *properties);
GList * pinos_node_get_links (PinosNode *node); GList * pinos_node_get_links (PinosNode *node);