mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
node: add an ASYNC flag
The flag means that the process function might not complete synchronously. We can use this knowledge to improve the adapter. In sync mode we can pull before scheduling the converter. In async mode we need to schedule the follower after the converter to get the data ready for the next iteration. We can also improve the stream when it is operating async to schedule a process call when a new buffer is ready to be filled. This reduces a cycle latency from alsa and improves latency in pulse.
This commit is contained in:
parent
9dfd261c71
commit
d2d722efb3
4 changed files with 72 additions and 43 deletions
|
|
@ -83,6 +83,7 @@ struct impl {
|
|||
unsigned int have_format:1;
|
||||
unsigned int started:1;
|
||||
unsigned int driver:1;
|
||||
unsigned int async:1;
|
||||
};
|
||||
|
||||
/** \endcond */
|
||||
|
|
@ -611,6 +612,8 @@ static void follower_info(void *data, const struct spa_node_info *info)
|
|||
{
|
||||
struct impl *this = data;
|
||||
|
||||
this->async = (info->flags & SPA_NODE_FLAG_ASYNC) != 0;
|
||||
|
||||
if (info->max_input_ports > 0)
|
||||
this->direction = SPA_DIRECTION_INPUT;
|
||||
else
|
||||
|
|
@ -898,19 +901,18 @@ static int impl_node_process(void *object)
|
|||
/* 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;
|
||||
}
|
||||
while (true) {
|
||||
status = this->convert ? spa_node_process(this->convert) : 0;
|
||||
/* schedule the follower when the converter needed
|
||||
* a recycled buffer */
|
||||
if (status == -EPIPE || status == 0)
|
||||
status = SPA_STATUS_HAVE_DATA;
|
||||
else if (status < 0)
|
||||
break;
|
||||
|
||||
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 */
|
||||
* is drained, process the follower. */
|
||||
fstatus = spa_node_process(this->follower);
|
||||
/* if the follower doesn't need more data or is
|
||||
* drained we can stop */
|
||||
|
|
@ -921,22 +923,28 @@ static int impl_node_process(void *object)
|
|||
/* the converter needs more data */
|
||||
if ((status & SPA_STATUS_NEED_DATA))
|
||||
break;
|
||||
} while (status > 0);
|
||||
}
|
||||
|
||||
if (this->direction == SPA_DIRECTION_OUTPUT &&
|
||||
!this->driver && this->convert) {
|
||||
status = SPA_STATUS_NEED_DATA;
|
||||
do {
|
||||
}
|
||||
} else if (!this->driver) {
|
||||
bool done;
|
||||
while (true) {
|
||||
/* 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)) {
|
||||
status = this->convert ? spa_node_process(this->convert) : 0;
|
||||
if (status == 0)
|
||||
status = SPA_STATUS_NEED_DATA;
|
||||
else if (status < 0)
|
||||
break;
|
||||
|
||||
done = (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED));
|
||||
|
||||
/* when not async, we can return the data when we are done.
|
||||
* In async mode we might first need to wake up the follower
|
||||
* to asynchronously provide more data for the next round. */
|
||||
if (!this->async && done)
|
||||
break;
|
||||
|
||||
if (status & SPA_STATUS_NEED_DATA) {
|
||||
/* the converter needs more data, schedule the
|
||||
* follower */
|
||||
fstatus = spa_node_process(this->follower);
|
||||
|
|
@ -945,10 +953,13 @@ static int impl_node_process(void *object)
|
|||
if ((fstatus & SPA_STATUS_HAVE_DATA) == 0)
|
||||
break;
|
||||
}
|
||||
/* converter produced something or is drained */
|
||||
if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED))
|
||||
/* converter produced something or is drained and we
|
||||
* scheduled the follower above, we can stop now*/
|
||||
if (done)
|
||||
break;
|
||||
} while (status > 0);
|
||||
}
|
||||
} else {
|
||||
status = spa_node_process(this->follower);
|
||||
}
|
||||
spa_log_trace_fp(this->log, "%p: process status:%d", this, status);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue