bluez5: driver should produce buffers before ready

The graph cycle goes: driver timeout -> process output nodes -> process
driver node.  Hence, driver should produce buffers in the timeout,
otherwise there's one quantum extra latency.

Make the bluez5 media/sco sources as drivers put a buffer to io before
indicating ready, and as follower do it in process. Also make checks if
io == NULL, and don't set io->status to HAVE_DATA unless there really is
a buffer ready.
This commit is contained in:
Pauli Virtanen 2022-10-19 18:38:07 +03:00
parent facf73b01c
commit 2fa3120486
2 changed files with 91 additions and 33 deletions

View file

@ -568,13 +568,14 @@ static int setup_matching(struct impl *this)
return 0;
}
static int produce_buffer(struct impl *this);
static void media_on_timeout(struct spa_source *source)
{
struct impl *this = source->data;
struct port *port = &this->port;
uint64_t exp, duration;
uint32_t rate;
struct spa_io_buffers *io = port->io;
uint64_t prev_time, now_time;
if (this->transport == NULL)
@ -609,8 +610,11 @@ static void media_on_timeout(struct spa_source *source)
this->clock->next_nsec = this->next_time;
}
spa_log_trace(this->log, "%p: %d", this, io->status);
io->status = SPA_STATUS_HAVE_DATA;
if (port->io) {
int status = produce_buffer(this);
spa_log_trace(this->log, "%p: io:%d status:%d", this, port->io->status, status);
}
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA);
set_timeout(this, this->next_time);
@ -1343,12 +1347,50 @@ static void process_buffering(struct impl *this)
}
}
static int produce_buffer(struct impl *this)
{
struct buffer *buffer;
struct port *port = &this->port;
struct spa_io_buffers *io = port->io;
if (io == NULL)
return -EIO;
/* Return if we already have a buffer */
if (io->status == SPA_STATUS_HAVE_DATA)
return SPA_STATUS_HAVE_DATA;
/* Recycle */
if (io->buffer_id < port->n_buffers) {
recycle_buffer(this, port, io->buffer_id);
io->buffer_id = SPA_ID_INVALID;
}
/* Handle buffering */
process_buffering(this);
/* Return if there are no buffers ready to be processed */
if (spa_list_is_empty(&port->ready))
return SPA_STATUS_OK;
/* Get the new buffer from the ready list */
buffer = spa_list_first(&port->ready, struct buffer, link);
spa_list_remove(&buffer->link);
buffer->outstanding = true;
/* Set the new buffer in IO */
io->buffer_id = buffer->id;
io->status = SPA_STATUS_HAVE_DATA;
/* Notify we have a buffer ready to be processed */
return SPA_STATUS_HAVE_DATA;
}
static int impl_node_process(void *object)
{
struct impl *this = object;
struct port *port;
struct spa_io_buffers *io;
struct buffer *buffer;
spa_return_val_if_fail(this != NULL, -EINVAL);
@ -1368,24 +1410,11 @@ static int impl_node_process(void *object)
io->buffer_id = SPA_ID_INVALID;
}
/* Handle buffering delay */
process_buffering(this);
/* Return if there are no buffers ready to be processed */
if (spa_list_is_empty(&port->ready))
/* Follower produces buffers here, driver in timeout */
if (this->following)
return produce_buffer(this);
else
return SPA_STATUS_OK;
/* Get the new buffer from the ready list */
buffer = spa_list_first(&port->ready, struct buffer, link);
spa_list_remove(&buffer->link);
buffer->outstanding = true;
/* Set the new buffer in IO */
io->buffer_id = buffer->id;
io->status = SPA_STATUS_HAVE_DATA;
/* Notify we have a buffer ready to be processed */
return SPA_STATUS_HAVE_DATA;
}
static const struct spa_node_methods impl_node = {