diff --git a/pinos/Makefile.am b/pinos/Makefile.am index a96e8e799..0eb9abb99 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -245,6 +245,7 @@ if BUILD_MODULE_SPA module_LTLIBRARIES += module-spa.la module_spa_la_SOURCES = \ + modules/spa/spa-audiotestsrc.c \ modules/spa/spa-alsa-sink.c \ modules/spa/spa-v4l2-source.c \ modules/spa/module.c diff --git a/pinos/client/stream.c b/pinos/client/stream.c index 0f333e2e5..c2de0aa39 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -833,7 +833,7 @@ parse_control (PinosStream *stream, bid.buf = p.buffers[i]; bid.cleanup = false; bid.id = bid.buf->id; - g_debug ("add buffer %d, %d", bid.id, bid.buf->mem.mem.id); + g_debug ("add buffer %d, %d, %zd, %zd", bid.id, bid.buf->mem.mem.id, bid.buf->mem.offset, bid.buf->mem.size); if (bid.id != priv->buffer_ids->len) { g_warning ("unexpected id %u found, expected %u", bid.id, priv->buffer_ids->len); diff --git a/pinos/modules/spa/module.c b/pinos/modules/spa/module.c index 01506376f..0ccc1929f 100644 --- a/pinos/modules/spa/module.c +++ b/pinos/modules/spa/module.c @@ -23,6 +23,7 @@ #include "spa-alsa-sink.h" #include "spa-v4l2-source.h" +#include "spa-audiotestsrc.h" gboolean pinos__module_init (PinosModule *module, const gchar * args); @@ -31,6 +32,7 @@ pinos__module_init (PinosModule * module, G_GNUC_UNUSED const gchar * args) { pinos_spa_alsa_sink_new (module->daemon, "alsa-sink", NULL); pinos_spa_v4l2_source_new (module->daemon, "v4l2-source", NULL); + pinos_spa_audiotestsrc_new (module->daemon, "audiotestsrc", NULL); return TRUE; } diff --git a/pinos/modules/spa/spa-audiotestsrc.c b/pinos/modules/spa/spa-audiotestsrc.c new file mode 100644 index 000000000..da3cc3580 --- /dev/null +++ b/pinos/modules/spa/spa-audiotestsrc.c @@ -0,0 +1,198 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include + +#include "spa-audiotestsrc.h" + +#define PINOS_SPA_AUDIOTESTSRC_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_AUDIOTESTSRC, PinosSpaAudioTestSrcPrivate)) + +struct _PinosSpaAudioTestSrcPrivate +{ + PinosRingbuffer *ringbuffer; +}; + +enum { + PROP_0, +}; + +G_DEFINE_TYPE (PinosSpaAudioTestSrc, pinos_spa_audiotestsrc, PINOS_TYPE_NODE); + +static SpaResult +make_node (SpaNode **node, const char *lib, const char *name) +{ + SpaHandle *handle; + SpaResult res; + void *hnd, *state = NULL; + SpaEnumHandleFactoryFunc enum_func; + + if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { + g_error ("can't load %s: %s", lib, dlerror()); + return SPA_RESULT_ERROR; + } + if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) { + g_error ("can't find enum function"); + return SPA_RESULT_ERROR; + } + + while (true) { + const SpaHandleFactory *factory; + void *iface; + + if ((res = enum_func (&factory, &state)) < 0) { + if (res != SPA_RESULT_ENUM_END) + g_error ("can't enumerate factories: %d", res); + break; + } + if (strcmp (factory->name, name)) + continue; + + handle = calloc (1, factory->size); + if ((res = factory->init (factory, handle)) < 0) { + g_error ("can't make factory instance: %d", res); + return res; + } + if ((res = handle->get_interface (handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) { + g_error ("can't get interface %d", res); + return res; + } + *node = iface; + return SPA_RESULT_OK; + } + return SPA_RESULT_ERROR; +} + +static void +setup_node (PinosSpaAudioTestSrc *this) +{ + PinosNode *node = PINOS_NODE (this); + SpaResult res; + SpaProps *props; + SpaPropValue value; + + if ((res = spa_node_get_props (node->node, &props)) < 0) + g_debug ("got get_props error %d", res); + + value.type = SPA_PROP_TYPE_STRING; + value.value = "hw:1"; + value.size = strlen (value.value)+1; + spa_props_set_prop (props, spa_props_index_for_name (props, "device"), &value); + + if ((res = spa_node_set_props (node->node, props)) < 0) + g_debug ("got set_props error %d", res); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +src_constructed (GObject * object) +{ + PinosSpaAudioTestSrc *src = PINOS_SPA_AUDIOTESTSRC (object); + + setup_node (src); + + G_OBJECT_CLASS (pinos_spa_audiotestsrc_parent_class)->constructed (object); +} + +static void +src_finalize (GObject * object) +{ + PinosNode *node = PINOS_NODE (object); + PinosSpaAudioTestSrc *src = PINOS_SPA_AUDIOTESTSRC (object); + + g_debug ("audiotestsrc %p: dispose", src); + spa_handle_clear (node->node->handle); + g_free (node->node->handle); + + G_OBJECT_CLASS (pinos_spa_audiotestsrc_parent_class)->finalize (object); +} + +static void +pinos_spa_audiotestsrc_class_init (PinosSpaAudioTestSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PinosSpaAudioTestSrcPrivate)); + + gobject_class->constructed = src_constructed; + gobject_class->finalize = src_finalize; + gobject_class->get_property = get_property; + gobject_class->set_property = set_property; +} + +static void +pinos_spa_audiotestsrc_init (PinosSpaAudioTestSrc * this) +{ + this->priv = PINOS_SPA_AUDIOTESTSRC_GET_PRIVATE (this); +} + +PinosNode * +pinos_spa_audiotestsrc_new (PinosDaemon *daemon, + const gchar *name, + PinosProperties *properties) +{ + PinosNode *node; + SpaNode *n; + SpaResult res; + + if ((res = make_node (&n, + "spa/build/plugins/audiotestsrc/libspa-audiotestsrc.so", + "audiotestsrc")) < 0) { + g_error ("can't create audiotestsrc: %d", res); + return NULL; + } + + node = g_object_new (PINOS_TYPE_SPA_AUDIOTESTSRC, + "daemon", daemon, + "name", name, + "properties", properties, + "node", n, + NULL); + + return node; +} diff --git a/pinos/modules/spa/spa-audiotestsrc.h b/pinos/modules/spa/spa-audiotestsrc.h new file mode 100644 index 000000000..4f2f993e2 --- /dev/null +++ b/pinos/modules/spa/spa-audiotestsrc.h @@ -0,0 +1,61 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PINOS_SPA_AUDIOTESTSRC_H__ +#define __PINOS_SPA_AUDIOTESTSRC_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +#define PINOS_TYPE_SPA_AUDIOTESTSRC (pinos_spa_audiotestsrc_get_type ()) +#define PINOS_IS_SPA_AUDIOTESTSRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_AUDIOTESTSRC)) +#define PINOS_IS_SPA_AUDIOTESTSRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_AUDIOTESTSRC)) +#define PINOS_SPA_AUDIOTESTSRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_AUDIOTESTSRC, PinosSpaAudioTestSrcClass)) +#define PINOS_SPA_AUDIOTESTSRC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_AUDIOTESTSRC, PinosSpaAudioTestSrc)) +#define PINOS_SPA_AUDIOTESTSRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_AUDIOTESTSRC, PinosSpaAudioTestSrcClass)) +#define PINOS_SPA_AUDIOTESTSRC_CAST(obj) ((PinosSpaAudioTestSrc*)(obj)) +#define PINOS_SPA_AUDIOTESTSRC_CLASS_CAST(klass) ((PinosSpaAudioTestSrcClass*)(klass)) + +typedef struct _PinosSpaAudioTestSrc PinosSpaAudioTestSrc; +typedef struct _PinosSpaAudioTestSrcClass PinosSpaAudioTestSrcClass; +typedef struct _PinosSpaAudioTestSrcPrivate PinosSpaAudioTestSrcPrivate; + +struct _PinosSpaAudioTestSrc { + PinosNode object; + + PinosSpaAudioTestSrcPrivate *priv; +}; + +struct _PinosSpaAudioTestSrcClass { + PinosNodeClass parent_class; +}; + +GType pinos_spa_audiotestsrc_get_type (void); + +PinosNode * pinos_spa_audiotestsrc_new (PinosDaemon *daemon, + const gchar *name, + PinosProperties *properties); + +G_END_DECLS + +#endif /* __PINOS_SPA_AUDIOTESTSRC_H__ */ diff --git a/pinos/server/link.c b/pinos/server/link.c index 8a32e93f7..f975685b8 100644 --- a/pinos/server/link.c +++ b/pinos/server/link.c @@ -410,8 +410,8 @@ check_states (PinosLink *this) SpaResult res; SpaNodeState in_state, out_state; - in_state = this->input_node->node_state; - out_state = this->output_node->node_state; + in_state = this->input_node->node->state; + out_state = this->output_node->node->state; g_debug ("link %p: input %d, output %d", this, in_state, out_state); diff --git a/pinos/server/node.c b/pinos/server/node.c index d8532f7c1..b45c2674d 100644 --- a/pinos/server/node.c +++ b/pinos/server/node.c @@ -59,9 +59,12 @@ struct _PinosNodePrivate PinosProperties *properties; + unsigned int n_poll; + SpaPollItem poll[16]; + + bool rebuild_fds; SpaPollFd fds[16]; unsigned int n_fds; - SpaPollItem poll; gboolean running; pthread_t thread; @@ -119,6 +122,8 @@ update_port_ids (PinosNode *node, gboolean create) &priv->n_output_ports, &priv->max_output_ports); + g_debug ("node %p: update_port ids %u, %u", node, priv->max_input_ports, priv->max_output_ports); + priv->input_port_ids = g_realloc_n (priv->input_port_ids, priv->max_input_ports, sizeof (uint32_t)); priv->output_port_ids = g_realloc_n (priv->output_port_ids, priv->max_output_ports, sizeof (uint32_t)); @@ -134,11 +139,58 @@ loop (void *user_data) { PinosNode *this = user_data; PinosNodePrivate *priv = this->priv; - int r; + unsigned int i, j; g_debug ("node %p: enter thread", this); while (priv->running) { SpaPollNotifyData ndata; + unsigned int n_idle = 0; + int r; + + /* prepare */ + for (i = 0; i < priv->n_poll; i++) { + SpaPollItem *p = &priv->poll[i]; + + if (p->enabled && p->idle_cb) { + ndata.fds = NULL; + ndata.n_fds = 0; + ndata.user_data = p->user_data; + p->idle_cb (&ndata); + n_idle++; + } + } + if (n_idle > 0) + continue; + + /* rebuild */ + if (priv->rebuild_fds) { + g_debug ("node %p: rebuild fds", this); + priv->n_fds = 1; + for (i = 0; i < priv->n_poll; i++) { + SpaPollItem *p = &priv->poll[i]; + + if (!p->enabled) + continue; + + for (j = 0; j < p->n_fds; j++) + priv->fds[priv->n_fds + j] = p->fds[j]; + p->fds = &priv->fds[priv->n_fds]; + priv->n_fds += p->n_fds; + } + priv->rebuild_fds = false; + } + + /* before */ + for (i = 0; i < priv->n_poll; i++) { + SpaPollItem *p = &priv->poll[i]; + + if (p->enabled && p->before_cb) { + ndata.fds = p->fds; + ndata.n_fds = p->n_fds; + ndata.user_data = p->user_data; + p->before_cb (&ndata); + } + } r = poll ((struct pollfd *) priv->fds, priv->n_fds, -1); if (r < 0) { @@ -150,19 +202,25 @@ loop (void *user_data) g_debug ("node %p: select timeout", this); break; } + + /* check wakeup */ if (priv->fds[0].revents & POLLIN) { uint64_t u; if (read (priv->fds[0].fd, &u, sizeof(uint64_t)) != sizeof(uint64_t)) g_warning ("node %p: failed to read fd", strerror (errno)); - g_debug ("node %p: event signaled", this); - break; + continue; } - if (priv->poll.after_cb) { - ndata.fds = priv->poll.fds; - ndata.n_fds = priv->poll.n_fds; - ndata.user_data = priv->poll.user_data; - priv->poll.after_cb (&ndata); + /* after */ + for (i = 0; i < priv->n_poll; i++) { + SpaPollItem *p = &priv->poll[i]; + + if (p->enabled && p->after_cb) { + ndata.fds = p->fds; + ndata.n_fds = p->n_fds; + ndata.user_data = p->user_data; + p->after_cb (&ndata); + } } } g_debug ("node %p: leave thread", this); @@ -170,6 +228,15 @@ loop (void *user_data) return NULL; } +static void +wakeup_thread (PinosNode *this) +{ + PinosNodePrivate *priv = this->priv; + uint64_t u = 1; + + if (write (priv->fds[0].fd, &u, sizeof(uint64_t)) != sizeof(uint64_t)) + g_warning ("node %p: failed to write fd", strerror (errno)); +} static void start_thread (PinosNode *this) @@ -192,12 +259,8 @@ stop_thread (PinosNode *this) PinosNodePrivate *priv = this->priv; if (priv->running) { - uint64_t u = 1; - - if (write (priv->fds[0].fd, &u, sizeof(uint64_t)) != sizeof(uint64_t)) - g_warning ("node %p: failed to write fd", strerror (errno)); - priv->running = false; + wakeup_thread (this); pthread_join (priv->thread, NULL); } } @@ -286,14 +349,11 @@ on_node_event (SpaNode *node, SpaEvent *event, void *user_data) { SpaEventStateChange *sc = event->data; - if (this->node_state != sc->state) { - g_debug ("node %p: update SPA state to %d", this, sc->state); - this->node_state = sc->state; - g_object_notify (G_OBJECT (this), "node-state"); + g_debug ("node %p: update SPA state to %d", this, sc->state); + g_object_notify (G_OBJECT (this), "node-state"); - if (sc->state == SPA_NODE_STATE_CONFIGURE) { - update_port_ids (this, FALSE); - } + if (sc->state == SPA_NODE_STATE_CONFIGURE) { + update_port_ids (this, FALSE); } switch (sc->state) { case SPA_NODE_STATE_CONFIGURE: @@ -315,23 +375,38 @@ on_node_event (SpaNode *node, SpaEvent *event, void *user_data) case SPA_EVENT_TYPE_ADD_POLL: { SpaPollItem *poll = event->data; - unsigned int i; g_debug ("node %p: add poll %d", this, poll->n_fds); - priv->poll = *poll; - priv->poll.fds = &priv->fds[priv->n_fds]; - for (i = 0; i < poll->n_fds; i++) - priv->fds[priv->n_fds++] = poll->fds[i]; + priv->poll[priv->n_poll] = *poll; + priv->n_poll++; + if (poll->n_fds) + priv->rebuild_fds = true; + wakeup_thread (this); start_thread (this); break; } + case SPA_EVENT_TYPE_UPDATE_POLL: + { + unsigned int i; + SpaPollItem *poll = event->data; + + for (i = 0; i < priv->n_poll; i++) { + if (priv->poll[i].id == poll->id) + priv->poll[i] = *poll; + } + if (poll->n_fds) + priv->rebuild_fds = true; + wakeup_thread (this); + break; + } case SPA_EVENT_TYPE_REMOVE_POLL: { SpaPollItem *poll = event->data; g_debug ("node %p: remove poll %d", this, poll->n_fds); - priv->n_fds -= poll->n_fds; + priv->rebuild_fds = true; + wakeup_thread (this); stop_thread (this); break; @@ -433,7 +508,7 @@ pinos_node_get_property (GObject *_object, break; case PROP_NODE_STATE: - g_value_set_uint (value, node->node_state); + g_value_set_uint (value, node->node->state); break; default: diff --git a/pinos/server/node.h b/pinos/server/node.h index 40e3a0a29..3f19f5f60 100644 --- a/pinos/server/node.h +++ b/pinos/server/node.h @@ -52,7 +52,6 @@ struct _PinosNode { GObject object; SpaNode *node; - SpaNodeState node_state; PinosNodePrivate *priv; }; diff --git a/spa/include/spa/event.h b/spa/include/spa/event.h index 86357359d..eeccb3e6a 100644 --- a/spa/include/spa/event.h +++ b/spa/include/spa/event.h @@ -40,6 +40,7 @@ typedef struct _SpaEvent SpaEvent; * @SPA_EVENT_TYPE_NEED_INPUT: emited when more data can be pushed to an async node * @SPA_EVENT_TYPE_REUSE_BUFFER: emited when a buffer can be reused * @SPA_EVENT_TYPE_ADD_POLL: emited when a pollfd should be added. data points to #SpaPollItem + * @SPA_EVENT_TYPE_UPDATE_POLL: update the pollfd item * @SPA_EVENT_TYPE_REMOVE_POLL: emited when a pollfd should be removed. data points to #SpaPollItem * @SPA_EVENT_TYPE_DRAINED: emited when DRAIN command completed * @SPA_EVENT_TYPE_MARKER: emited when MARK command completed @@ -56,6 +57,7 @@ typedef enum { SPA_EVENT_TYPE_NEED_INPUT, SPA_EVENT_TYPE_REUSE_BUFFER, SPA_EVENT_TYPE_ADD_POLL, + SPA_EVENT_TYPE_UPDATE_POLL, SPA_EVENT_TYPE_REMOVE_POLL, SPA_EVENT_TYPE_DRAINED, SPA_EVENT_TYPE_MARKER, diff --git a/spa/include/spa/node.h b/spa/include/spa/node.h index b43881584..26d168f06 100644 --- a/spa/include/spa/node.h +++ b/spa/include/spa/node.h @@ -154,6 +154,12 @@ struct _SpaNode { /* the total size of this node. This can be used to expand this * structure in the future */ size_t size; + /** + * SpaNode::state: + * + * The current state of the node + */ + SpaNodeState state; /** * SpaNode::get_props: * @node: a #SpaNode diff --git a/spa/include/spa/poll.h b/spa/include/spa/poll.h index ccc61bdc1..f7e314c73 100644 --- a/spa/include/spa/poll.h +++ b/spa/include/spa/poll.h @@ -24,8 +24,6 @@ extern "C" { #endif -typedef struct _SpaEvent SpaEvent; - #include /** @@ -63,6 +61,7 @@ typedef int (*SpaPollNotify) (SpaPollNotifyData *data); /** * SpaPollItem: * @id: id of the poll item + * @enabled: if the item is enabled * @fds: array of file descriptors to watch * @n_fds: number of elements in @fds * @idle_cb: callback called when there is no other work @@ -72,6 +71,7 @@ typedef int (*SpaPollNotify) (SpaPollNotifyData *data); */ typedef struct { uint32_t id; + bool enabled; SpaPollFd *fds; unsigned int n_fds; SpaPollNotify idle_cb; diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index 45ea35d07..6480b91ba 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -305,7 +305,7 @@ spa_alsa_sink_node_get_port_ids (SpaNode *node, if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_input_ports > 0) + if (n_input_ports > 0 && input_ids != NULL) input_ids[0] = 0; return SPA_RESULT_OK; @@ -634,6 +634,7 @@ spa_alsa_sink_node_port_pull_output (SpaNode *node, static const SpaNode alsasink_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_alsa_sink_node_get_props, spa_alsa_sink_node_set_props, spa_alsa_sink_node_send_command, diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 7cd86db78..2fbdbb8bb 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -348,6 +348,8 @@ spa_alsa_start (SpaALSASink *this) event.data = &state->poll; event.size = sizeof (state->poll); + state->poll.id = 0; + state->poll.enabled = true; state->poll.fds = state->fds; state->poll.idle_cb = NULL; state->poll.before_cb = NULL; diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index af11bb8e3..b5076d51b 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -725,6 +725,7 @@ spa_audiomixer_node_port_push_event (SpaNode *node, static const SpaNode audiomixer_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_audiomixer_node_get_props, spa_audiomixer_node_set_props, spa_audiomixer_node_send_command, diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 4ecc79d82..feb35d6cb 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -32,6 +33,20 @@ typedef struct { double volume; } SpaAudioTestSrcProps; +typedef struct _ATSBuffer ATSBuffer; + +struct _ATSBuffer { + SpaBuffer buffer; + SpaMeta metas[1]; + SpaMetaHeader header; + SpaData datas[1]; + SpaBuffer *outbuf; + bool outstanding; + ATSBuffer *next; + void *ptr; + size_t size; +}; + struct _SpaAudioTestSrc { SpaHandle handle; SpaNode node; @@ -41,12 +56,27 @@ struct _SpaAudioTestSrc { SpaEventCallback event_cb; void *user_data; + SpaPollItem idle; + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; SpaPortStatus status; bool have_format; SpaFormatAudio query_format; SpaFormatAudio current_format; + + SpaMemory *alloc_mem; + ATSBuffer *alloc_buffers; + + bool have_buffers; + SpaBuffer **buffers; + unsigned int n_buffers; + + ATSBuffer *empty; + ATSBuffer *ready; + unsigned int ready_count; }; static const uint32_t default_wave = 1.0; @@ -152,6 +182,58 @@ spa_audiotestsrc_node_set_props (SpaNode *node, return res; } +static SpaResult +send_have_output (SpaAudioTestSrc *this) +{ + SpaEvent event; + SpaEventHaveOutput ho; + + if (this->event_cb) { + event.type = SPA_EVENT_TYPE_HAVE_OUTPUT; + event.size = sizeof (ho); + event.data = &ho; + ho.port_id = 0; + this->event_cb (&this->node, &event, this->user_data); + } + + return SPA_RESULT_OK; +} + +static void +update_state (SpaAudioTestSrc *this, SpaNodeState state) +{ + SpaEvent event; + SpaEventStateChange sc; + + if (this->node.state == state) + return; + + this->node.state = state; + + if (this->event_cb) { + event.type = SPA_EVENT_TYPE_STATE_CHANGE; + event.data = ≻ + event.size = sizeof (sc); + sc.state = state; + this->event_cb (&this->node, &event, this->user_data); + } +} + +static SpaResult +update_idle_enabled (SpaAudioTestSrc *this, bool enabled) +{ + SpaEvent event; + + if (this->event_cb) { + this->idle.enabled = enabled; + event.type = SPA_EVENT_TYPE_UPDATE_POLL; + event.data = &this->idle; + event.size = sizeof (this->idle); + this->event_cb (&this->node, &event, this->user_data); + } + return SPA_RESULT_OK; +} + static SpaResult spa_audiotestsrc_node_send_command (SpaNode *node, SpaCommand *command) @@ -168,33 +250,31 @@ spa_audiotestsrc_node_send_command (SpaNode *node, return SPA_RESULT_INVALID_COMMAND; case SPA_COMMAND_START: - if (this->event_cb) { - SpaEvent event; - SpaEventStateChange sc; + { + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; - event.type = SPA_EVENT_TYPE_STATE_CHANGE; - event.data = ≻ - event.size = sizeof (sc); - sc.state = SPA_NODE_STATE_STREAMING; + if (!this->have_buffers) + return SPA_RESULT_NO_BUFFERS; - this->event_cb (node, &event, this->user_data); - } + update_idle_enabled (this, true); + + update_state (this, SPA_NODE_STATE_STREAMING); break; - + } case SPA_COMMAND_PAUSE: - if (this->event_cb) { - SpaEvent event; - SpaEventStateChange sc; + { + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; - event.type = SPA_EVENT_TYPE_STATE_CHANGE; - event.data = ≻ - event.size = sizeof (sc); - sc.state = SPA_NODE_STATE_PAUSED; + if (!this->have_buffers) + return SPA_RESULT_NO_BUFFERS; - this->event_cb (node, &event, this->user_data); - } + update_idle_enabled (this, false); + + update_state (this, SPA_NODE_STATE_PAUSED); break; - + } case SPA_COMMAND_FLUSH: case SPA_COMMAND_DRAIN: case SPA_COMMAND_MARKER: @@ -204,20 +284,34 @@ spa_audiotestsrc_node_send_command (SpaNode *node, } static SpaResult -spa_audiotestsrc_node_set_event_callback (SpaNode *node, - SpaEventCallback event, - void *user_data) +spa_audiotestsrc_node_set_event_callback (SpaNode *node, + SpaEventCallback event_cb, + void *user_data) { SpaAudioTestSrc *this; + SpaEvent event; if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; this = (SpaAudioTestSrc *) node->handle; - this->event_cb = event; + if (event_cb == NULL && this->event_cb) { + event.type = SPA_EVENT_TYPE_REMOVE_POLL; + event.data = &this->idle; + event.size = sizeof (this->idle); + this->event_cb (&this->node, &event, this->user_data); + } + + this->event_cb = event_cb; this->user_data = user_data; + if (this->event_cb) { + event.type = SPA_EVENT_TYPE_ADD_POLL; + event.data = &this->idle; + event.size = sizeof (this->idle); + this->event_cb (&this->node, &event, this->user_data); + } return SPA_RESULT_OK; } @@ -250,10 +344,10 @@ spa_audiotestsrc_node_get_port_ids (SpaNode *node, unsigned int n_output_ports, uint32_t *output_ids) { - if (node == NULL || node->handle == NULL || input_ids == NULL || output_ids == NULL) + if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 0; return SPA_RESULT_OK; @@ -295,9 +389,12 @@ spa_audiotestsrc_node_port_enum_formats (SpaNode *node, switch (index) { case 0: - spa_format_audio_init (SPA_MEDIA_TYPE_AUDIO, - SPA_MEDIA_SUBTYPE_RAW, - &this->query_format); + if (filter) + spa_format_audio_parse (filter, &this->query_format); + else + spa_format_audio_init (SPA_MEDIA_TYPE_AUDIO, + SPA_MEDIA_SUBTYPE_RAW, + &this->query_format); break; default: return SPA_RESULT_ENUM_END; @@ -327,13 +424,34 @@ spa_audiotestsrc_node_port_set_format (SpaNode *node, if (format == NULL) { this->have_format = false; - return SPA_RESULT_OK; + this->have_buffers = false; + } else { + if ((res = spa_format_audio_parse (format, &this->current_format)) < 0) + return res; + + this->have_format = true; } - if ((res = spa_format_audio_parse (format, &this->current_format)) < 0) - return res; + if (this->have_format) { + this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + this->info.maxbuffering = -1; + this->info.latency = -1; - this->have_format = true; + this->info.n_params = 1; + this->info.params = this->params; + this->params[0] = &this->param_buffers.param; + this->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS; + this->param_buffers.param.size = sizeof (this->param_buffers); + this->param_buffers.minsize = 1024; + this->param_buffers.stride = 1024; + this->param_buffers.min_buffers = 2; + this->param_buffers.max_buffers = 32; + this->param_buffers.align = 16; + this->info.features = NULL; + update_state (this, SPA_NODE_STATE_READY); + } + else + update_state (this, SPA_NODE_STATE_CONFIGURE); return SPA_RESULT_OK; } @@ -403,7 +521,74 @@ spa_audiotestsrc_node_port_use_buffers (SpaNode *node, SpaBuffer **buffers, uint32_t n_buffers) { - return SPA_RESULT_NOT_IMPLEMENTED; + SpaAudioTestSrc *this; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaAudioTestSrc *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; + + if (this->have_buffers) { + if (this->alloc_mem) + spa_memory_unref (&this->alloc_mem->mem); + this->alloc_mem = NULL; + this->buffers = NULL; + this->n_buffers = 0; + this->have_buffers = false; + } + if (buffers != NULL && n_buffers != 0) { + unsigned int i; + + this->alloc_mem = spa_memory_alloc_size (SPA_MEMORY_POOL_LOCAL, + NULL, + sizeof (ATSBuffer) * n_buffers); + this->alloc_buffers = spa_memory_ensure_ptr (this->alloc_mem); + + for (i = 0; i < n_buffers; i++) { + ATSBuffer *b; + SpaMemoryRef *mem_ref; + SpaMemory *mem; + SpaData *d = SPA_BUFFER_DATAS (buffers[i]); + + b = &this->alloc_buffers[i]; + if (i + 1 == n_buffers) + b->next = NULL; + else + b->next = &this->alloc_buffers[i + 1]; + + b->buffer.mem.mem = this->alloc_mem->mem; + b->buffer.mem.offset = sizeof (ATSBuffer) * i; + b->buffer.mem.size = sizeof (ATSBuffer); + b->buffer.id = SPA_ID_INVALID; + b->outbuf = buffers[i]; + b->outstanding = true; + + mem_ref = &d[0].mem.mem; + if (!(mem = spa_memory_find (mem_ref))) { + fprintf (stderr, "invalid memory on buffer %p\n", buffers[i]); + continue; + } + b->ptr = SPA_MEMBER (spa_memory_ensure_ptr (mem), d[0].mem.offset, void); + b->size = d[0].mem.size; + } + this->buffers = buffers; + this->n_buffers = n_buffers; + this->have_buffers = true; + this->empty = this->alloc_buffers; + } + + if (this->have_buffers) + update_state (this, SPA_NODE_STATE_PAUSED); + else + update_state (this, SPA_NODE_STATE_READY); + + return SPA_RESULT_OK; } static SpaResult @@ -422,7 +607,35 @@ spa_audiotestsrc_node_port_reuse_buffer (SpaNode *node, uint32_t port_id, uint32_t buffer_id) { - return SPA_RESULT_NOT_IMPLEMENTED; + SpaAudioTestSrc *this; + ATSBuffer *b; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaAudioTestSrc *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (!this->have_buffers) + return SPA_RESULT_NO_BUFFERS; + + if (buffer_id >= this->n_buffers) + return SPA_RESULT_INVALID_BUFFER_ID; + + b = &this->alloc_buffers[buffer_id]; + if (!b->outstanding) + return SPA_RESULT_OK; + + b->outstanding = false; + b->next = this->empty; + this->empty = b; + + if (b->next == NULL) + update_idle_enabled (this, true); + + return SPA_RESULT_OK; } static SpaResult @@ -457,14 +670,48 @@ spa_audiotestsrc_node_port_push_input (SpaNode *node, return SPA_RESULT_INVALID_PORT; } +static SpaResult +fill_buffer (SpaAudioTestSrc *this, ATSBuffer *b) +{ + uint8_t *p = b->ptr; + size_t i; + + for (i = 0; i < b->size; i++) { + p[i] = rand(); + } + + return SPA_RESULT_OK; +} + +static int +audiotestsrc_idle (SpaPollNotifyData *data) +{ + SpaAudioTestSrc *this = data->user_data; + ATSBuffer *empty; + + empty = this->empty; + if (!empty) { + update_idle_enabled (this, false); + return 0; + } + + fill_buffer (this, empty); + + this->empty = empty->next; + empty->next = this->ready; + this->ready = empty; + this->ready_count++; + send_have_output (this); + + return 0; +} + static SpaResult spa_audiotestsrc_node_port_pull_output (SpaNode *node, unsigned int n_info, SpaOutputInfo *info) { SpaAudioTestSrc *this; - size_t j, size; - uint8_t *ptr = NULL; unsigned int i; bool have_error = false; @@ -474,6 +721,8 @@ spa_audiotestsrc_node_port_pull_output (SpaNode *node, this = (SpaAudioTestSrc *) node->handle; for (i = 0; i < n_info; i++) { + ATSBuffer *b; + if (info[i].port_id != 0) { info[i].status = SPA_RESULT_INVALID_PORT; have_error = true; @@ -485,12 +734,18 @@ spa_audiotestsrc_node_port_pull_output (SpaNode *node, have_error = true; continue; } + if (this->ready_count == 0) { + info[i].status = SPA_RESULT_UNEXPECTED; + have_error = true; + continue; + } - size = 0; - - for (j = 0; j < size; j++) - ptr[j] = rand(); + b = this->ready; + this->ready = b->next; + this->ready_count--; + b->outstanding = true; + info[i].buffer_id = b->outbuf->id; info[i].status = SPA_RESULT_OK; } if (have_error) @@ -510,6 +765,7 @@ spa_audiotestsrc_node_port_push_event (SpaNode *node, static const SpaNode audiotestsrc_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_audiotestsrc_node_get_props, spa_audiotestsrc_node_set_props, spa_audiotestsrc_node_send_command, @@ -580,9 +836,20 @@ audiotestsrc_init (const SpaHandleFactory *factory, this->props[1].props.prop_info = prop_info; reset_audiotestsrc_props (&this->props[1]); + this->idle.id = 0; + this->idle.enabled = false; + this->idle.fds = NULL; + this->idle.n_fds = 0; + this->idle.idle_cb = audiotestsrc_idle; + this->idle.before_cb = NULL; + this->idle.after_cb = NULL; + this->idle.user_data = this; + this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_NO_REF; - this->status.flags = SPA_PORT_STATUS_FLAG_HAVE_OUTPUT; + this->status.flags = SPA_PORT_STATUS_FLAG_NONE; + + this->node.state = SPA_NODE_STATE_CONFIGURE; return SPA_RESULT_OK; } diff --git a/spa/plugins/ffmpeg/ffmpeg-dec.c b/spa/plugins/ffmpeg/ffmpeg-dec.c index 2d583c91f..c25a605a6 100644 --- a/spa/plugins/ffmpeg/ffmpeg-dec.c +++ b/spa/plugins/ffmpeg/ffmpeg-dec.c @@ -227,9 +227,9 @@ spa_ffmpeg_dec_node_get_port_ids (SpaNode *node, if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_input_ports > 0) + if (n_input_ports > 0 && input_ids != NULL) input_ids[0] = 0; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 1; return SPA_RESULT_OK; @@ -502,6 +502,7 @@ spa_ffmpeg_dec_node_port_push_event (SpaNode *node, static const SpaNode ffmpeg_dec_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_ffmpeg_dec_node_get_props, spa_ffmpeg_dec_node_set_props, spa_ffmpeg_dec_node_send_command, diff --git a/spa/plugins/ffmpeg/ffmpeg-enc.c b/spa/plugins/ffmpeg/ffmpeg-enc.c index 2ce4033fc..3fc0e551f 100644 --- a/spa/plugins/ffmpeg/ffmpeg-enc.c +++ b/spa/plugins/ffmpeg/ffmpeg-enc.c @@ -227,9 +227,9 @@ spa_ffmpeg_enc_node_get_port_ids (SpaNode *node, if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_input_ports > 0) + if (n_input_ports > 0 && input_ids != NULL) input_ids[0] = 0; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 1; return SPA_RESULT_OK; @@ -505,6 +505,7 @@ spa_ffmpeg_enc_node_port_push_event (SpaNode *node, static const SpaNode ffmpeg_enc_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_ffmpeg_enc_node_get_props, spa_ffmpeg_enc_node_set_props, spa_ffmpeg_enc_node_send_command, @@ -570,5 +571,7 @@ spa_ffmpeg_enc_init (SpaHandle *handle) this->state[OUTPUT_PORT_ID].info.flags = SPA_PORT_INFO_FLAG_NONE; this->state[OUTPUT_PORT_ID].status.flags = SPA_PORT_STATUS_FLAG_NONE; + this->node.state = SPA_NODE_STATE_CONFIGURE; + return SPA_RESULT_OK; } diff --git a/spa/plugins/libva/libva-dec.c b/spa/plugins/libva/libva-dec.c index 182ad2446..6ab3e69e1 100644 --- a/spa/plugins/libva/libva-dec.c +++ b/spa/plugins/libva/libva-dec.c @@ -219,9 +219,9 @@ spa_libva_dec_node_get_port_ids (SpaHandle *handle, if (handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_input_ports > 0) + if (n_input_ports > 0 && input_ids != NULL) input_ids[0] = 0; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 1; return SPA_RESULT_OK; diff --git a/spa/plugins/libva/libva-enc.c b/spa/plugins/libva/libva-enc.c index 593d4119c..3e4493798 100644 --- a/spa/plugins/libva/libva-enc.c +++ b/spa/plugins/libva/libva-enc.c @@ -219,9 +219,9 @@ spa_libva_enc_node_get_port_ids (SpaHandle *handle, if (handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_input_ports > 0) + if (n_input_ports > 0 && input_ids != NULL) input_ids[0] = 0; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 1; return SPA_RESULT_OK; diff --git a/spa/plugins/remote/proxy.c b/spa/plugins/remote/proxy.c index c992da1a2..ef781fed3 100644 --- a/spa/plugins/remote/proxy.c +++ b/spa/plugins/remote/proxy.c @@ -64,7 +64,6 @@ typedef struct { struct _SpaProxy { SpaHandle handle; SpaNode node; - SpaNodeState state; SpaProxyProps props[2]; @@ -130,11 +129,11 @@ update_poll (SpaProxy *this, int socketfd) static SpaResult update_state (SpaProxy *this, SpaNodeState state) { - if (this->state != state) { + if (this->node.state != state) { SpaEvent event; SpaEventStateChange sc; - this->state = state; + this->node.state = state; event.type = SPA_EVENT_TYPE_STATE_CHANGE; event.data = ≻ @@ -1077,6 +1076,7 @@ proxy_on_fd_events (SpaPollNotifyData *data) static const SpaNode proxy_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_proxy_node_get_props, spa_proxy_node_set_props, spa_proxy_node_send_command, @@ -1151,6 +1151,7 @@ proxy_init (const SpaHandleFactory *factory, this->fds[0].events = POLLIN | POLLPRI | POLLERR; this->fds[0].revents = 0; this->poll.id = 0; + this->poll.enabled = true; this->poll.fds = this->fds; this->poll.n_fds = 1; this->poll.idle_cb = NULL; diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index e6d2c14af..0bee4adc1 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -120,7 +120,6 @@ typedef struct { struct _SpaV4l2Source { SpaHandle handle; SpaNode node; - SpaNodeState node_state; SpaV4l2SourceProps props[2]; @@ -208,10 +207,10 @@ update_state (SpaV4l2Source *this, SpaNodeState state) SpaEvent event; SpaEventStateChange sc; - if (this->node_state == state) + if (this->node.state == state) return; - this->node_state = state; + this->node.state = state; event.type = SPA_EVENT_TYPE_STATE_CHANGE; event.data = ≻ @@ -329,7 +328,7 @@ spa_v4l2_source_node_get_port_ids (SpaNode *node, if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 0; return SPA_RESULT_OK; @@ -718,6 +717,7 @@ spa_v4l2_source_node_port_push_event (SpaNode *node, static const SpaNode v4l2source_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_v4l2_source_node_get_props, spa_v4l2_source_node_set_props, spa_v4l2_source_node_send_command, @@ -793,8 +793,6 @@ v4l2_source_init (const SpaHandleFactory *factory, this->state[0].export_buf = true; - this->node_state = SPA_NODE_STATE_INIT; - return SPA_RESULT_OK; } diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index d6945e0ec..6ddede5e7 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -625,9 +625,9 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe if (state->alloc_mem) spa_memory_unref (&state->alloc_mem->mem); - state->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, - NULL, - sizeof (V4l2Buffer) * reqbuf.count); + state->alloc_mem = spa_memory_alloc_size (SPA_MEMORY_POOL_LOCAL, + NULL, + sizeof (V4l2Buffer) * reqbuf.count); state->alloc_buffers = spa_memory_ensure_ptr (state->alloc_mem); for (i = 0; i < reqbuf.count; i++) { @@ -646,7 +646,7 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe fprintf (stderr, "import buffer %p\n", buffers[i]); - mem_ref = &SPA_BUFFER_DATAS (buffers[i])[0].mem.mem; + mem_ref = &d[0].mem.mem; if (!(mem = spa_memory_find (mem_ref))) { fprintf (stderr, "invalid memory on buffer %p\n", buffers[i]); continue; @@ -656,7 +656,7 @@ spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffe b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; b->v4l2_buffer.memory = state->memtype; b->v4l2_buffer.index = i; - b->v4l2_buffer.m.userptr = (unsigned long) ((uint8_t*)mem->ptr + d[0].mem.offset); + b->v4l2_buffer.m.userptr = (unsigned long) SPA_MEMBER (mem->ptr, d[0].mem.offset, void *); b->v4l2_buffer.length = d[0].mem.size; spa_v4l2_buffer_recycle (this, buffers[i]->id); @@ -869,6 +869,7 @@ spa_v4l2_start (SpaV4l2Source *this) state->fds[0].revents = 0; state->poll.id = 0; + state->poll.enabled = true; state->poll.fds = state->fds; state->poll.n_fds = 1; state->poll.idle_cb = NULL; diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 620828489..1728c53a8 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -620,6 +620,7 @@ spa_volume_node_port_push_event (SpaNode *node, static const SpaNode volume_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_volume_node_get_props, spa_volume_node_set_props, spa_volume_node_send_command, diff --git a/spa/plugins/xv/xv-sink.c b/spa/plugins/xv/xv-sink.c index 6b40c83b0..8305dd564 100644 --- a/spa/plugins/xv/xv-sink.c +++ b/spa/plugins/xv/xv-sink.c @@ -262,7 +262,7 @@ spa_xv_sink_node_get_port_ids (SpaNode *node, if (node == NULL || node->handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; - if (n_output_ports > 0) + if (n_output_ports > 0 && output_ids != NULL) output_ids[0] = 0; return SPA_RESULT_OK; @@ -500,6 +500,7 @@ spa_xv_sink_node_port_push_event (SpaNode *node, static const SpaNode xvsink_node = { NULL, sizeof (SpaNode), + SPA_NODE_STATE_INIT, spa_xv_sink_node_get_props, spa_xv_sink_node_set_props, spa_xv_sink_node_send_command,