From 584ae678c69b1808e23602d55a7785724f1235dc Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 Sep 2020 17:18:47 +0200 Subject: [PATCH] audioadapter: improve the processing loop Handle the case where the converter in a source needs more output buffers (-EPIPE). Schedule the follower in that case and try to run the converter again until we have output or we can't make progress anymore. If the converter needs more data, schedule the follower before we exit the loop with the data. This gives the follower a chance to get more data asynchronously for the next iteration. --- spa/plugins/audioconvert/audioadapter.c | 63 +++++++++++++++++++------ 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 115781e79..8f4cc922e 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -889,31 +889,66 @@ impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) static int impl_node_process(void *object) { struct impl *this = object; - int status = 0; + int status = 0, fstatus; spa_log_trace_fp(this->log, "%p: process convert:%p driver:%d", this, this->convert, this->driver); if (this->direction == SPA_DIRECTION_INPUT) { - if (this->convert) - status = spa_node_process(this->convert); + /* an input node (sink). + * First we run the converter to process the input for the follower + * then if it produced data, we run the follower. */ + status = SPA_STATUS_HAVE_DATA; + do { + if (this->convert) { + status = spa_node_process(this->convert); + if (status <= 0) + status = SPA_STATUS_HAVE_DATA; + } + if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) { + /* as long as the converter produced something or + * is drained, process the follower. Also schedule + * the follower when the converter was in error + * because the follower might first need to recycle a + * buffer to the converter */ + fstatus = spa_node_process(this->follower); + /* if the follower doesn't need more data or is + * drained we can stop */ + if ((fstatus & SPA_STATUS_NEED_DATA) == 0 || + (fstatus & SPA_STATUS_DRAINED)) + break; + } + /* the converter needs more data */ + if ((status & SPA_STATUS_NEED_DATA)) + break; + } while (status > 0); } - if (status >= 0) - status = spa_node_process(this->follower); - if (this->direction == SPA_DIRECTION_OUTPUT && !this->driver && this->convert) { - while (status > 0) { - status = spa_node_process(this->convert); - if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) - break; - if (status & SPA_STATUS_NEED_DATA) { - status = spa_node_process(this->follower); - if (status & SPA_STATUS_NEED_DATA) + status = SPA_STATUS_NEED_DATA; + do { + /* output node (source). First run the converter to make + * sure we push out any queued data. Then when it needs + * more data, schedule the follower. */ + if (this->convert) { + status = spa_node_process(this->convert); + if (status <= 0) + status = SPA_STATUS_NEED_DATA; + } + if ((status & SPA_STATUS_NEED_DATA)) { + /* the converter needs more data, schedule the + * follower */ + fstatus = spa_node_process(this->follower); + /* if the follower didn't produce more data + * we can stop now */ + if ((fstatus & SPA_STATUS_HAVE_DATA) == 0) break; } - } + /* converter produced something or is drained */ + if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) + break; + } while (status > 0); } spa_log_trace_fp(this->log, "%p: process status:%d", this, status);