From 2fa3120486f1bda3c1915e57a84620061a63eab0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 19 Oct 2022 18:38:07 +0300 Subject: [PATCH] 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. --- spa/plugins/bluez5/media-source.c | 71 ++++++++++++++++++++++--------- spa/plugins/bluez5/sco-source.c | 53 +++++++++++++++++------ 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/spa/plugins/bluez5/media-source.c b/spa/plugins/bluez5/media-source.c index 515eedbe6..62344ed8d 100644 --- a/spa/plugins/bluez5/media-source.c +++ b/spa/plugins/bluez5/media-source.c @@ -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 = { diff --git a/spa/plugins/bluez5/sco-source.c b/spa/plugins/bluez5/sco-source.c index 11c126262..8156bf00b 100644 --- a/spa/plugins/bluez5/sco-source.c +++ b/spa/plugins/bluez5/sco-source.c @@ -591,13 +591,14 @@ static int setup_matching(struct impl *this) return 0; } +static int produce_buffer(struct impl *this); + static void sco_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) @@ -632,8 +633,11 @@ static void sco_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); @@ -1286,17 +1290,13 @@ static void process_buffering(struct impl *this) } } -static int impl_node_process(void *object) +static int produce_buffer(struct impl *this) { - struct impl *this = object; - struct port *port; - struct spa_io_buffers *io; struct buffer *buffer; + struct port *port = &this->port; + struct spa_io_buffers *io = port->io; - spa_return_val_if_fail(this != NULL, -EINVAL); - - port = &this->port; - if ((io = port->io) == NULL) + if (io == NULL) return -EIO; /* Return if we already have a buffer */ @@ -1309,7 +1309,7 @@ static int impl_node_process(void *object) io->buffer_id = SPA_ID_INVALID; } - /* Produce data */ + /* Handle buffering */ process_buffering(this); /* Return if there are no buffers ready to be processed */ @@ -1329,6 +1329,35 @@ static int impl_node_process(void *object) return SPA_STATUS_HAVE_DATA; } +static int impl_node_process(void *object) +{ + struct impl *this = object; + struct port *port; + struct spa_io_buffers *io; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + port = &this->port; + if ((io = port->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; + } + + /* Follower produces buffers here, driver in timeout */ + if (this->following) + return produce_buffer(this); + else + return SPA_STATUS_OK; +} + static const struct spa_node_methods impl_node = { SPA_VERSION_NODE_METHODS, .add_listener = impl_node_add_listener,