From 5c32690cc8b2f3f77fb4a3e838710eaf2f1bb4af Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 3 Apr 2017 16:54:44 +0200 Subject: [PATCH] Add sync and async support in *testsrc Make minumum alsa latency configurable. --- pinos/client/stream.c | 2 - pinos/modules/spa/spa-node.c | 7 +- pinos/server/client-node.c | 3 - spa/include/spa/props.h | 1 + spa/lib/debug.c | 2 +- spa/plugins/alsa/alsa-sink.c | 16 +--- spa/plugins/alsa/alsa-source.c | 16 +--- spa/plugins/alsa/alsa-utils.c | 5 +- spa/plugins/alsa/alsa-utils.h | 12 +-- spa/plugins/audiomixer/audiomixer.c | 33 +++++-- spa/plugins/audiotestsrc/audiotestsrc.c | 85 +++++++++-------- spa/plugins/videotestsrc/videotestsrc.c | 116 +++++++++++++----------- spa/tests/test-mixer.c | 41 +++++++-- 13 files changed, 186 insertions(+), 153 deletions(-) diff --git a/pinos/client/stream.c b/pinos/client/stream.c index 891c3ae59..21140c3a9 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -500,7 +500,6 @@ handle_rtnode_event (PinosStream *stream, } else if (SPA_EVENT_TYPE (event) == context->type.event_node.NeedInput) { int i; - BufferId *bid; for (i = 0; i < impl->trans->area->n_outputs; i++) { SpaPortIO *output = &impl->trans->outputs[i]; @@ -516,7 +515,6 @@ handle_rtnode_event (PinosStream *stream, } else if (SPA_EVENT_TYPE (event) == context->type.event_node.ReuseBuffer) { SpaEventNodeReuseBuffer *p = (SpaEventNodeReuseBuffer *) event; - BufferId *bid; if (p->body.port_id.value != impl->port_id) return; diff --git a/pinos/modules/spa/spa-node.c b/pinos/modules/spa/spa-node.c index 0eb3a4fbb..0102ebb19 100644 --- a/pinos/modules/spa/spa-node.c +++ b/pinos/modules/spa/spa-node.c @@ -51,6 +51,8 @@ pinos_spa_node_load (PinosCore *core, SpaEnumHandleFactoryFunc enum_func; const SpaHandleFactory *factory; void *iface; + SpaDictItem items[1]; + SpaDict dict = SPA_DICT_INIT (1, items); if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { pinos_log_error ("can't load %s: %s", lib, dlerror()); @@ -71,10 +73,13 @@ pinos_spa_node_load (PinosCore *core, break; } + items[0].key = "asynchronous"; + items[0].value = "1"; + handle = calloc (1, factory->size); if ((res = spa_handle_factory_init (factory, handle, - NULL, + &dict, core->support, core->n_support)) < 0) { pinos_log_error ("can't make factory instance: %d", res); diff --git a/pinos/server/client-node.c b/pinos/server/client-node.c index 32b1384f8..8e263bc09 100644 --- a/pinos/server/client-node.c +++ b/pinos/server/client-node.c @@ -798,14 +798,11 @@ static SpaResult spa_proxy_node_process_output (SpaNode *node) { SpaProxy *this; - PinosNode *pnode; - int i; if (node == NULL) return SPA_RESULT_INVALID_ARGUMENTS; this = SPA_CONTAINER_OF (node, SpaProxy, node); - pnode = this->pnode; send_need_input (this); diff --git a/spa/include/spa/props.h b/spa/include/spa/props.h index 1bc84877b..713de5da8 100644 --- a/spa/include/spa/props.h +++ b/spa/include/spa/props.h @@ -37,6 +37,7 @@ typedef SpaPODObject SpaProps; #define SPA_TYPE_PROPS__deviceFd SPA_TYPE_PROPS_BASE "deviceFd" #define SPA_TYPE_PROPS__card SPA_TYPE_PROPS_BASE "card" #define SPA_TYPE_PROPS__cardName SPA_TYPE_PROPS_BASE "cardName" +#define SPA_TYPE_PROPS__minLatency SPA_TYPE_PROPS_BASE "minLatency" #define SPA_TYPE_PROPS__periods SPA_TYPE_PROPS_BASE "periods" #define SPA_TYPE_PROPS__periodSize SPA_TYPE_PROPS_BASE "periodSize" #define SPA_TYPE_PROPS__periodEvent SPA_TYPE_PROPS_BASE "periodEvent" diff --git a/spa/lib/debug.c b/spa/lib/debug.c index 47171c924..c2c15b886 100644 --- a/spa/lib/debug.c +++ b/spa/lib/debug.c @@ -474,7 +474,7 @@ spa_debug_dict (const SpaDict *dict) } -#define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_TRACE +#define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_DEBUG static void do_logv (SpaLog *log, diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index 75fdf38e6..54715bbb7 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -32,17 +32,13 @@ typedef struct _SpaALSAState SpaALSASink; static const char default_device[] = "default"; -static const uint32_t default_period_size = 128; -static const uint32_t default_periods = 2; -static const bool default_period_event = 0; +static const uint32_t default_min_latency = 1024; static void reset_alsa_sink_props (SpaALSAProps *props) { strncpy (props->device, default_device, 64); - props->period_size = default_period_size; - props->periods = default_periods; - props->period_event = default_period_event; + props->min_latency = default_min_latency; } static void @@ -83,9 +79,7 @@ spa_alsa_sink_node_get_props (SpaNode *node, PROP (&f[1], this->type.prop_device, -SPA_POD_TYPE_STRING, this->props.device, sizeof (this->props.device)), PROP (&f[1], this->type.prop_device_name, -SPA_POD_TYPE_STRING, this->props.device_name, sizeof (this->props.device_name)), PROP (&f[1], this->type.prop_card_name, -SPA_POD_TYPE_STRING, this->props.card_name, sizeof (this->props.card_name)), - PROP_MM (&f[1], this->type.prop_period_size, SPA_POD_TYPE_INT, this->props.period_size, 1, INT32_MAX), - PROP_MM (&f[1], this->type.prop_periods, SPA_POD_TYPE_INT, this->props.periods, 1, INT32_MAX), - PROP (&f[1], this->type.prop_period_event, SPA_POD_TYPE_BOOL, this->props.period_event)); + PROP_MM (&f[1], this->type.prop_min_latency, SPA_POD_TYPE_INT, this->props.min_latency, 1, INT32_MAX)); *props = SPA_POD_BUILDER_DEREF (&b, f[0].ref, SpaProps); @@ -109,9 +103,7 @@ spa_alsa_sink_node_set_props (SpaNode *node, } else { spa_props_query (props, this->type.prop_device, -SPA_POD_TYPE_STRING, this->props.device, sizeof (this->props.device), - this->type.prop_period_size, SPA_POD_TYPE_INT, &this->props.period_size, - this->type.prop_periods, SPA_POD_TYPE_INT, &this->props.periods, - this->type.prop_period_event, SPA_POD_TYPE_BOOL, &this->props.period_event, + this->type.prop_min_latency, SPA_POD_TYPE_INT, &this->props.min_latency, 0); } return SPA_RESULT_OK; diff --git a/spa/plugins/alsa/alsa-source.c b/spa/plugins/alsa/alsa-source.c index db3759c0c..32c66e1de 100644 --- a/spa/plugins/alsa/alsa-source.c +++ b/spa/plugins/alsa/alsa-source.c @@ -39,17 +39,13 @@ update_state (SpaALSASource *this, SpaNodeState state) } static const char default_device[] = "hw:0"; -static const uint32_t default_period_size = 32; -static const uint32_t default_periods = 2; -static const bool default_period_event = 0; +static const uint32_t default_min_latency = 1024; static void reset_alsa_props (SpaALSAProps *props) { strncpy (props->device, default_device, 64); - props->period_size = default_period_size; - props->periods = default_periods; - props->period_event = default_period_event; + props->min_latency = default_min_latency; } #define PROP(f,key,type,...) \ @@ -84,9 +80,7 @@ spa_alsa_source_node_get_props (SpaNode *node, PROP (&f[1], this->type.prop_device, -SPA_POD_TYPE_STRING, this->props.device, sizeof (this->props.device)), PROP (&f[1], this->type.prop_device_name, -SPA_POD_TYPE_STRING, this->props.device_name, sizeof (this->props.device_name)), PROP (&f[1], this->type.prop_card_name, -SPA_POD_TYPE_STRING, this->props.card_name, sizeof (this->props.card_name)), - PROP_MM (&f[1], this->type.prop_period_size, SPA_POD_TYPE_INT, this->props.period_size, 1, INT32_MAX), - PROP_MM (&f[1], this->type.prop_periods, SPA_POD_TYPE_INT, this->props.periods, 1, INT32_MAX), - PROP (&f[1], this->type.prop_period_event, SPA_POD_TYPE_BOOL, this->props.period_event)); + PROP_MM (&f[1], this->type.prop_min_latency, SPA_POD_TYPE_INT, this->props.min_latency, 1, INT32_MAX)); *props = SPA_POD_BUILDER_DEREF (&b, f[0].ref, SpaProps); @@ -110,9 +104,7 @@ spa_alsa_source_node_set_props (SpaNode *node, } else { spa_props_query (props, this->type.prop_device, -SPA_POD_TYPE_STRING, this->props.device, sizeof (this->props.device), - this->type.prop_period_size, SPA_POD_TYPE_INT, &this->props.period_size, - this->type.prop_periods, SPA_POD_TYPE_INT, &this->props.periods, - this->type.prop_period_event, SPA_POD_TYPE_BOOL, &this->props.period_event, + this->type.prop_min_latency, SPA_POD_TYPE_INT, &this->props.min_latency, 0); } diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 157af2722..07f554095 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -117,7 +117,6 @@ spa_alsa_set_format (SpaALSAState *state, SpaAudioInfo *fmt, SpaPortFormatFlags SpaAudioInfoRaw *info = &fmt->info.raw; snd_pcm_t *hndl; unsigned int periods; - SpaALSAProps *props = &state->props; if ((err = spa_alsa_open (state)) < 0) return err; @@ -195,8 +194,6 @@ set_swparams (SpaALSAState *state) snd_pcm_sw_params_t *params; snd_pcm_uframes_t boundary; - SpaALSAProps *props = &state->props; - snd_pcm_sw_params_alloca (¶ms); /* get the current params */ @@ -618,7 +615,7 @@ spa_alsa_start (SpaALSAState *state, bool xrun_recover) state->source.rmask = 0; spa_loop_add_source (state->data_loop, &state->source); - state->threshold = 1024; + state->threshold = state->props.min_latency; if (state->stream == SND_PCM_STREAM_PLAYBACK) { state->alsa_started = false; diff --git a/spa/plugins/alsa/alsa-utils.h b/spa/plugins/alsa/alsa-utils.h index c086b7d2c..0afe77a19 100644 --- a/spa/plugins/alsa/alsa-utils.h +++ b/spa/plugins/alsa/alsa-utils.h @@ -45,9 +45,7 @@ typedef struct { char device[64]; char device_name[128]; char card_name[128]; - uint32_t period_size; - uint32_t periods; - bool period_event; + uint32_t min_latency; } SpaALSAProps; #define MAX_BUFFERS 64 @@ -68,9 +66,7 @@ typedef struct { uint32_t prop_device; uint32_t prop_device_name; uint32_t prop_card_name; - uint32_t prop_period_size; - uint32_t prop_periods; - uint32_t prop_period_event; + uint32_t prop_min_latency; SpaTypeMediaType media_type; SpaTypeMediaSubtype media_subtype; SpaTypeMediaSubtypeAudio media_subtype_audio; @@ -92,9 +88,7 @@ init_type (Type *type, SpaTypeMap *map) type->prop_device = spa_type_map_get_id (map, SPA_TYPE_PROPS__device); type->prop_device_name = spa_type_map_get_id (map, SPA_TYPE_PROPS__deviceName); type->prop_card_name = spa_type_map_get_id (map, SPA_TYPE_PROPS__cardName); - type->prop_period_size = spa_type_map_get_id (map, SPA_TYPE_PROPS__periodSize); - type->prop_periods = spa_type_map_get_id (map, SPA_TYPE_PROPS__periods); - type->prop_period_event = spa_type_map_get_id (map, SPA_TYPE_PROPS__periodEvent); + type->prop_min_latency = spa_type_map_get_id (map, SPA_TYPE_PROPS__minLatency); spa_type_media_type_map (map, &type->media_type); spa_type_media_subtype_map (map, &type->media_subtype); diff --git a/spa/plugins/audiomixer/audiomixer.c b/spa/plugins/audiomixer/audiomixer.c index a8287c72e..c5691ec72 100644 --- a/spa/plugins/audiomixer/audiomixer.c +++ b/spa/plugins/audiomixer/audiomixer.c @@ -452,7 +452,7 @@ spa_audiomixer_node_port_use_buffers (SpaNode *node, b = &port->buffers[i]; b->outbuf = buffers[i]; - b->outstanding = true; + b->outstanding = direction == SPA_DIRECTION_INPUT ? true : false; b->h = spa_buffer_find_meta (buffers[i], SPA_META_TYPE_HEADER); switch (d[0].type) { @@ -516,11 +516,34 @@ spa_audiomixer_node_port_set_io (SpaNode *node, return SPA_RESULT_OK; } +static void +recycle_buffer (SpaAudioMixer *this, uint32_t id) +{ + SpaAudioMixerPort *port = &this->out_ports[0]; + + MixerBuffer *b = &port->buffers[id]; + if (b->outstanding) { + spa_list_insert (port->queue.prev, &b->link); + b->outstanding = false; + spa_log_trace (this->log, "audiomixer %p: recycle buffer %d", this, id); + } +} + static SpaResult spa_audiomixer_node_port_reuse_buffer (SpaNode *node, uint32_t port_id, uint32_t buffer_id) { + SpaAudioMixer *this; + + spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); + + this = SPA_CONTAINER_OF (node, SpaAudioMixer, node); + + spa_return_val_if_fail (CHECK_PORT (this, SPA_DIRECTION_OUTPUT, port_id), SPA_RESULT_INVALID_PORT); + + recycle_buffer (this, buffer_id); + return SPA_RESULT_NOT_IMPLEMENTED; } @@ -664,15 +687,11 @@ spa_audiomixer_node_process_output (SpaNode *node) input->range = output->range; } if (output->buffer_id != SPA_ID_INVALID) { - MixerBuffer *b = &port->buffers[output->buffer_id]; - if (b->outstanding) { - spa_list_insert (port->queue.prev, &b->link); - b->outstanding = false; - spa_log_trace (this->log, "audiomixer %p: recycle buffer %d", this, b->outbuf->id); - } + recycle_buffer (this, output->buffer_id); output->buffer_id = SPA_ID_INVALID; } this->state = STATE_IN; + return SPA_RESULT_NEED_INPUT; } diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 609c2393b..1298d71ef 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -112,6 +112,7 @@ struct _SpaAudioTestSrc { SpaTypeMap *map; SpaLog *log; SpaLoop *data_loop; + bool async; uint8_t props_buffer[512]; SpaAudioTestSrcProps props; @@ -239,7 +240,6 @@ send_have_output (SpaAudioTestSrc *this) SpaEvent event = SPA_EVENT_INIT (this->type.event_node.HaveOutput); this->event_cb (&this->node, &event, this->user_data); } - return SPA_RESULT_OK; } @@ -280,6 +280,10 @@ audiotestsrc_make_buffer (SpaAudioTestSrc *this) { ATSBuffer *b; SpaPortIO *io; + uint64_t expirations; + + if (read (this->timer_source.fd, &expirations, sizeof (uint64_t)) < sizeof (uint64_t)) + perror ("read timerfd"); if (spa_list_is_empty (&this->empty)) { set_timer (this, false); @@ -306,21 +310,19 @@ audiotestsrc_make_buffer (SpaAudioTestSrc *this) io->buffer_id = b->outbuf->id; io->status = SPA_RESULT_OK; } - return SPA_RESULT_OK; + return SPA_RESULT_HAVE_OUTPUT; } static void audiotestsrc_on_output (SpaSource *source) { SpaAudioTestSrc *this = source->data; - uint64_t expirations; + SpaResult res; - if (read (this->timer_source.fd, &expirations, sizeof (uint64_t)) < sizeof (uint64_t)) - perror ("read timerfd"); + res = audiotestsrc_make_buffer (this); - audiotestsrc_make_buffer (this); - - send_have_output (this); + if (res == SPA_RESULT_HAVE_OUTPUT) + send_have_output (this); } static void @@ -396,15 +398,9 @@ spa_audiotestsrc_node_set_event_callback (SpaNode *node, this = SPA_CONTAINER_OF (node, SpaAudioTestSrc, node); - if (event_cb == NULL && this->event_cb) - spa_loop_remove_source (this->data_loop, &this->timer_source); - this->event_cb = event_cb; this->user_data = user_data; - if (this->event_cb) { - spa_loop_add_source (this->data_loop, &this->timer_source); - } return SPA_RESULT_OK; } @@ -768,13 +764,27 @@ spa_audiotestsrc_node_port_set_io (SpaNode *node, return SPA_RESULT_OK; } +static inline void +reuse_buffer (SpaAudioTestSrc *this, uint32_t id) +{ + ATSBuffer *b = &this->buffers[id]; + spa_return_if_fail (b->outstanding); + + spa_log_trace (this->log, "audiotestsrc %p: reuse buffer %d", this, id); + + b->outstanding = false; + spa_list_insert (this->empty.prev, &b->link); + + if (!this->props.live) + set_timer (this, true); +} + static SpaResult spa_audiotestsrc_node_port_reuse_buffer (SpaNode *node, uint32_t port_id, uint32_t buffer_id) { SpaAudioTestSrc *this; - ATSBuffer *b; spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); @@ -784,16 +794,7 @@ spa_audiotestsrc_node_port_reuse_buffer (SpaNode *node, spa_return_val_if_fail (this->n_buffers > 0, SPA_RESULT_NO_BUFFERS); spa_return_val_if_fail (buffer_id < this->n_buffers, SPA_RESULT_INVALID_BUFFER_ID); - b = &this->buffers[buffer_id]; - spa_return_val_if_fail (b->outstanding, SPA_RESULT_INVALID_BUFFER_ID); - - spa_log_trace (this->log, "audiotestsrc %p: reuse buffer %d", this, b->outbuf->id); - - b->outstanding = false; - spa_list_insert (this->empty.prev, &b->link); - - if (!this->props.live) - set_timer (this, true); + reuse_buffer (this, buffer_id); return SPA_RESULT_OK; } @@ -816,29 +817,21 @@ spa_audiotestsrc_node_process_input (SpaNode *node) static SpaResult spa_audiotestsrc_node_process_output (SpaNode *node) { - SpaResult res; SpaAudioTestSrc *this; - ATSBuffer *b; spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); this = SPA_CONTAINER_OF (node, SpaAudioTestSrc, node); if (this->io && this->io->buffer_id != SPA_ID_INVALID) { - b = &this->buffers[this->io->buffer_id]; - if (b->outstanding) { - b->outstanding = false; - spa_log_trace (this->log, "audiotestsrc %p: recycle buffer %d", this, b->outbuf->id); - spa_list_insert (this->empty.prev, &b->link); - if (!this->props.live) - set_timer (this, true); - } + reuse_buffer (this, this->io->buffer_id); this->io->buffer_id = SPA_ID_INVALID; } - if ((res = audiotestsrc_make_buffer (this)) < 0) - return res; - return SPA_RESULT_HAVE_OUTPUT; + if (!this->async) + return audiotestsrc_make_buffer (this); + else + return SPA_RESULT_OK; } static const SpaNode audiotestsrc_node = { @@ -949,6 +942,7 @@ audiotestsrc_clear (SpaHandle *handle) this = (SpaAudioTestSrc *) handle; + spa_loop_remove_source (this->data_loop, &this->timer_source); close (this->timer_source.fd); return SPA_RESULT_OK; @@ -963,6 +957,7 @@ audiotestsrc_init (const SpaHandleFactory *factory, { SpaAudioTestSrc *this; uint32_t i; + const char *str; if (factory == NULL || handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; @@ -972,6 +967,11 @@ audiotestsrc_init (const SpaHandleFactory *factory, this = (SpaAudioTestSrc *) handle; + if (info && (str = spa_dict_lookup (info, "asynchronous"))) + this->async = atoi (str) == 1; + else + this->async = false; + for (i = 0; i < n_support; i++) { if (strcmp (support[i].type, SPA_TYPE__TypeMap) == 0) this->map = support[i].data; @@ -984,7 +984,7 @@ audiotestsrc_init (const SpaHandleFactory *factory, spa_log_error (this->log, "a type-map is needed"); return SPA_RESULT_ERROR; } - if (this->data_loop == NULL) { + if (this->data_loop == NULL && this->async) { spa_log_error (this->log, "a data_loop is needed"); return SPA_RESULT_ERROR; } @@ -998,7 +998,7 @@ audiotestsrc_init (const SpaHandleFactory *factory, this->timer_source.func = audiotestsrc_on_output; this->timer_source.data = this; - this->timer_source.fd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + this->timer_source.fd = timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC); this->timer_source.mask = SPA_IO_IN; this->timer_source.rmask = 0; this->timerspec.it_value.tv_sec = 0; @@ -1006,6 +1006,9 @@ audiotestsrc_init (const SpaHandleFactory *factory, this->timerspec.it_interval.tv_sec = 0; this->timerspec.it_interval.tv_nsec = 0; + if (this->data_loop && this->async) + spa_loop_add_source (this->data_loop, &this->timer_source); + this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_NO_REF; if (this->props.live) @@ -1013,6 +1016,8 @@ audiotestsrc_init (const SpaHandleFactory *factory, this->node.state = SPA_NODE_STATE_CONFIGURE; + spa_log_info (this->log, "audiotestsrc %p: initialized, async=%d", this, this->async); + return SPA_RESULT_OK; } diff --git a/spa/plugins/videotestsrc/videotestsrc.c b/spa/plugins/videotestsrc/videotestsrc.c index d4cf51fa9..548527190 100644 --- a/spa/plugins/videotestsrc/videotestsrc.c +++ b/spa/plugins/videotestsrc/videotestsrc.c @@ -106,6 +106,7 @@ struct _SpaVideoTestSrc { SpaTypeMap *map; SpaLog *log; SpaLoop *data_loop; + bool async; uint8_t props_buffer[512]; SpaVideoTestSrcProps props; @@ -177,7 +178,6 @@ spa_videotestsrc_node_get_props (SpaNode *node, this = SPA_CONTAINER_OF (node, SpaVideoTestSrc, node); spa_pod_builder_init (&b, this->props_buffer, sizeof (this->props_buffer)); - spa_pod_builder_props (&b, &f[0], this->type.props, PROP (&f[1], this->type.prop_live, SPA_POD_TYPE_BOOL, this->props.live), PROP_EN (&f[1], this->type.prop_pattern, SPA_POD_TYPE_ID, 3, @@ -256,10 +256,9 @@ set_timer (SpaVideoTestSrc *this, bool enabled) timerfd_settime (this->timer_source.fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL); } -static void -videotestsrc_on_output (SpaSource *source) +static SpaResult +videotestsrc_make_buffer (SpaVideoTestSrc *this) { - SpaVideoTestSrc *this = source->data; VTSBuffer *b; SpaPortIO *io; uint64_t expirations; @@ -269,10 +268,12 @@ videotestsrc_on_output (SpaSource *source) if (spa_list_is_empty (&this->empty)) { set_timer (this, false); - return; + return SPA_RESULT_OUT_OF_BUFFERS; } b = spa_list_first (&this->empty, VTSBuffer, link); spa_list_remove (&b->link); + b->outstanding = true; + spa_log_trace (this->log, "videotestsrc %p: dequeue buffer %d", this, b->outbuf->id); fill_buffer (this, b); @@ -287,11 +288,22 @@ videotestsrc_on_output (SpaSource *source) set_timer (this, true); if ((io = this->io)) { - b->outstanding = true; io->buffer_id = b->outbuf->id; io->status = SPA_RESULT_OK; - send_have_output (this); } + return SPA_RESULT_HAVE_OUTPUT; +} + +static void +videotestsrc_on_output (SpaSource *source) +{ + SpaVideoTestSrc *this = source->data; + SpaResult res; + + res = videotestsrc_make_buffer (this); + + if (res == SPA_RESULT_HAVE_OUTPUT) + send_have_output (this); } static void @@ -367,15 +379,9 @@ spa_videotestsrc_node_set_event_callback (SpaNode *node, this = SPA_CONTAINER_OF (node, SpaVideoTestSrc, node); - if (event_cb == NULL && this->event_cb) - spa_loop_remove_source (this->data_loop, &this->timer_source); - this->event_cb = event_cb; this->user_data = user_data; - if (this->event_cb) { - spa_loop_add_source (this->data_loop, &this->timer_source); - } return SPA_RESULT_OK; } @@ -479,7 +485,6 @@ next: default: return SPA_RESULT_ENUM_END; } - fmt = SPA_POD_BUILDER_DEREF (&b, f[0].ref, SpaFormat); spa_pod_builder_init (&b, this->format_buffer, sizeof (this->format_buffer)); @@ -605,7 +610,6 @@ spa_videotestsrc_node_port_get_format (SpaNode *node, return SPA_RESULT_NO_FORMAT; spa_pod_builder_init (&b, this->format_buffer, sizeof (this->format_buffer)); - spa_pod_builder_format (&b, &f[0], this->type.format, this->type.media_type.video, this->type.media_subtype.raw, PROP (&f[1], this->type.format_video.format, SPA_POD_TYPE_ID, this->current_format.info.raw.format), @@ -747,50 +751,48 @@ spa_videotestsrc_node_port_set_io (SpaNode *node, { SpaVideoTestSrc *this; - if (node == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; + spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); this = SPA_CONTAINER_OF (node, SpaVideoTestSrc, node); - if (!CHECK_PORT (this, direction, port_id)) - return SPA_RESULT_INVALID_PORT; + spa_return_val_if_fail (CHECK_PORT (this, direction, port_id), SPA_RESULT_INVALID_PORT); this->io = io; return SPA_RESULT_OK; } +static inline void +reuse_buffer (SpaVideoTestSrc *this, uint32_t id) +{ + VTSBuffer *b = &this->buffers[id]; + spa_return_if_fail (b->outstanding); + + spa_log_trace (this->log, "videotestsrc %p: reuse buffer %d", this, id); + + b->outstanding = false; + spa_list_insert (this->empty.prev, &b->link); + + if (!this->props.live) + set_timer (this, true); +} + static SpaResult spa_videotestsrc_node_port_reuse_buffer (SpaNode *node, uint32_t port_id, uint32_t buffer_id) { SpaVideoTestSrc *this; - VTSBuffer *b; - if (node == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; + spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); this = SPA_CONTAINER_OF (node, SpaVideoTestSrc, node); - if (port_id != 0) - return SPA_RESULT_INVALID_PORT; + spa_return_val_if_fail (port_id == 0, SPA_RESULT_INVALID_PORT); + spa_return_val_if_fail (this->n_buffers > 0, SPA_RESULT_NO_BUFFERS); + spa_return_val_if_fail (buffer_id < this->n_buffers, SPA_RESULT_INVALID_BUFFER_ID); - if (this->n_buffers == 0) - return SPA_RESULT_NO_BUFFERS; - - if (buffer_id >= this->n_buffers) - return SPA_RESULT_INVALID_BUFFER_ID; - - b = &this->buffers[buffer_id]; - if (!b->outstanding) - return SPA_RESULT_OK; - - b->outstanding = false; - spa_list_insert (this->empty.prev, &b->link); - - if (!this->props.live) - set_timer (this, true); + reuse_buffer (this, buffer_id); return SPA_RESULT_OK; } @@ -814,24 +816,20 @@ static SpaResult spa_videotestsrc_node_process_output (SpaNode *node) { SpaVideoTestSrc *this; - VTSBuffer *b; - if (node == NULL) - return SPA_RESULT_INVALID_ARGUMENTS; + spa_return_val_if_fail (node != NULL, SPA_RESULT_INVALID_ARGUMENTS); this = SPA_CONTAINER_OF (node, SpaVideoTestSrc, node); if (this->io && this->io->buffer_id != SPA_ID_INVALID) { - b = &this->buffers[this->io->buffer_id]; - if (b->outstanding) { - b->outstanding = false; - spa_list_insert (this->empty.prev, &b->link); - if (!this->props.live) - set_timer (this, true); - } + reuse_buffer (this, this->io->buffer_id); this->io->buffer_id = SPA_ID_INVALID; } - return SPA_RESULT_OK; + + if (!this->async) + return videotestsrc_make_buffer (this); + else + return SPA_RESULT_OK; } static const SpaNode videotestsrc_node = { @@ -942,6 +940,7 @@ videotestsrc_clear (SpaHandle *handle) this = (SpaVideoTestSrc *) handle; + spa_loop_remove_source (this->data_loop, &this->timer_source); close (this->timer_source.fd); return SPA_RESULT_OK; @@ -956,6 +955,7 @@ videotestsrc_init (const SpaHandleFactory *factory, { SpaVideoTestSrc *this; uint32_t i; + const char *str; if (factory == NULL || handle == NULL) return SPA_RESULT_INVALID_ARGUMENTS; @@ -965,6 +965,11 @@ videotestsrc_init (const SpaHandleFactory *factory, this = (SpaVideoTestSrc *) handle; + if (info && (str = spa_dict_lookup (info, "asynchronous"))) + this->async = atoi (str) == 1; + else + this->async = false; + for (i = 0; i < n_support; i++) { if (strcmp (support[i].type, SPA_TYPE__TypeMap) == 0) this->map = support[i].data; @@ -974,10 +979,10 @@ videotestsrc_init (const SpaHandleFactory *factory, this->data_loop = support[i].data; } if (this->map == NULL) { - spa_log_error (this->log, "an id-map is needed"); + spa_log_error (this->log, "a type-map is needed"); return SPA_RESULT_ERROR; } - if (this->data_loop == NULL) { + if (this->data_loop == NULL && this->async) { spa_log_error (this->log, "a data_loop is needed"); return SPA_RESULT_ERROR; } @@ -991,7 +996,7 @@ videotestsrc_init (const SpaHandleFactory *factory, this->timer_source.func = videotestsrc_on_output; this->timer_source.data = this; - this->timer_source.fd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + this->timer_source.fd = timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC); this->timer_source.mask = SPA_IO_IN; this->timer_source.rmask = 0; this->timerspec.it_value.tv_sec = 0; @@ -999,6 +1004,9 @@ videotestsrc_init (const SpaHandleFactory *factory, this->timerspec.it_interval.tv_sec = 0; this->timerspec.it_interval.tv_nsec = 0; + if (this->data_loop && this->async) + spa_loop_add_source (this->data_loop, &this->timer_source); + this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_NO_REF; if (this->props.live) @@ -1006,6 +1014,8 @@ videotestsrc_init (const SpaHandleFactory *factory, this->node.state = SPA_NODE_STATE_CONFIGURE; + spa_log_info (this->log, "videotestsrc %p: initialized, async=%d", this, this->async); + return SPA_RESULT_OK; } diff --git a/spa/tests/test-mixer.c b/spa/tests/test-mixer.c index 1965d6694..1e3bcb392 100644 --- a/spa/tests/test-mixer.c +++ b/spa/tests/test-mixer.c @@ -141,7 +141,7 @@ init_buffer (AppData *data, Buffer *b, void *ptr, size_t size) } static SpaResult -make_node (AppData *data, SpaNode **node, const char *lib, const char *name) +make_node (AppData *data, SpaNode **node, const char *lib, const char *name, bool async) { SpaHandle *handle; SpaResult res; @@ -149,6 +149,8 @@ make_node (AppData *data, SpaNode **node, const char *lib, const char *name) SpaEnumHandleFactoryFunc enum_func; unsigned int i; uint32_t state = 0; + SpaDictItem items[1]; + SpaDict dict = SPA_DICT_INIT (1, items); if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { printf ("can't load %s: %s\n", lib, dlerror()); @@ -159,6 +161,9 @@ make_node (AppData *data, SpaNode **node, const char *lib, const char *name) return SPA_RESULT_ERROR; } + items[0].key = "asynchronous"; + items[0].value = async ? "1" : "0"; + for (i = 0; ;i++) { const SpaHandleFactory *factory; void *iface; @@ -172,7 +177,7 @@ make_node (AppData *data, SpaNode **node, const char *lib, const char *name) continue; handle = calloc (1, factory->size); - if ((res = spa_handle_factory_init (factory, handle, NULL, data->support, data->n_support)) < 0) { + if ((res = spa_handle_factory_init (factory, handle, &dict, data->support, data->n_support)) < 0) { printf ("can't make factory instance: %d\n", res); return res; } @@ -194,7 +199,6 @@ on_sink_event (SpaNode *node, SpaEvent *event, void *user_data) if (SPA_EVENT_TYPE (event) == data->type.event_node.NeedInput) { - printf ("need input event\n"); res = spa_node_process_output (data->mix); if (res == SPA_RESULT_NEED_INPUT) { @@ -226,7 +230,6 @@ push: else if (SPA_EVENT_TYPE (event) == data->type.event_node.ReuseBuffer) { SpaEventNodeReuseBuffer *rb = (SpaEventNodeReuseBuffer *) event; - printf ("got recycle event %d\n", rb->body.buffer_id.value); data->mix_sink_io[0].buffer_id = rb->body.buffer_id.value; } else { @@ -278,7 +281,9 @@ make_nodes (AppData *data) SpaPODFrame f[2]; uint8_t buffer[128]; - if ((res = make_node (data, &data->sink, "build/spa/plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) { + if ((res = make_node (data, &data->sink, + "build/spa/plugins/alsa/libspa-alsa.so", + "alsa-sink", true)) < 0) { printf ("can't create alsa-sink: %d\n", res); return res; } @@ -293,15 +298,21 @@ make_nodes (AppData *data) if ((res = spa_node_set_props (data->sink, props)) < 0) printf ("got set_props error %d\n", res); - if ((res = make_node (data, &data->mix, "build/spa/plugins/audiomixer/libspa-audiomixer.so", "audiomixer")) < 0) { + if ((res = make_node (data, &data->mix, + "build/spa/plugins/audiomixer/libspa-audiomixer.so", + "audiomixer", false)) < 0) { printf ("can't create audiomixer: %d\n", res); return res; } - if ((res = make_node (data, &data->source1, "build/spa/plugins/audiotestsrc/libspa-audiotestsrc.so", "audiotestsrc")) < 0) { + if ((res = make_node (data, &data->source1, + "build/spa/plugins/audiotestsrc/libspa-audiotestsrc.so", + "audiotestsrc", false)) < 0) { printf ("can't create audiotestsrc: %d\n", res); return res; } - if ((res = make_node (data, &data->source2, "build/spa/plugins/audiotestsrc/libspa-audiotestsrc.so", "audiotestsrc")) < 0) { + if ((res = make_node (data, &data->source2, + "build/spa/plugins/audiotestsrc/libspa-audiotestsrc.so", + "audiotestsrc", false)) < 0) { printf ("can't create audiotestsrc: %d\n", res); return res; } @@ -462,8 +473,14 @@ run_async_sink (AppData *data) { SpaCommand cmd = SPA_COMMAND_INIT (data->type.command_node.Start); + if ((res = spa_node_send_command (data->source1, &cmd)) < 0) + printf ("got source1 error %d\n", res); + if ((res = spa_node_send_command (data->source2, &cmd)) < 0) + printf ("got source2 error %d\n", res); + if ((res = spa_node_send_command (data->mix, &cmd)) < 0) + printf ("got mix error %d\n", res); if ((res = spa_node_send_command (data->sink, &cmd)) < 0) - printf ("got error %d\n", res); + printf ("got sink error %d\n", res); } data->running = true; @@ -484,6 +501,12 @@ run_async_sink (AppData *data) SpaCommand cmd = SPA_COMMAND_INIT (data->type.command_node.Pause); if ((res = spa_node_send_command (data->sink, &cmd)) < 0) printf ("got error %d\n", res); + if ((res = spa_node_send_command (data->mix, &cmd)) < 0) + printf ("got mix error %d\n", res); + if ((res = spa_node_send_command (data->source1, &cmd)) < 0) + printf ("got source1 error %d\n", res); + if ((res = spa_node_send_command (data->source2, &cmd)) < 0) + printf ("got source2 error %d\n", res); } }