mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
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:
parent
facf73b01c
commit
2fa3120486
2 changed files with 91 additions and 33 deletions
|
|
@ -568,13 +568,14 @@ static int setup_matching(struct impl *this)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int produce_buffer(struct impl *this);
|
||||||
|
|
||||||
static void media_on_timeout(struct spa_source *source)
|
static void media_on_timeout(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct impl *this = source->data;
|
struct impl *this = source->data;
|
||||||
struct port *port = &this->port;
|
struct port *port = &this->port;
|
||||||
uint64_t exp, duration;
|
uint64_t exp, duration;
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
struct spa_io_buffers *io = port->io;
|
|
||||||
uint64_t prev_time, now_time;
|
uint64_t prev_time, now_time;
|
||||||
|
|
||||||
if (this->transport == NULL)
|
if (this->transport == NULL)
|
||||||
|
|
@ -609,8 +610,11 @@ static void media_on_timeout(struct spa_source *source)
|
||||||
this->clock->next_nsec = this->next_time;
|
this->clock->next_nsec = this->next_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_log_trace(this->log, "%p: %d", this, io->status);
|
if (port->io) {
|
||||||
io->status = SPA_STATUS_HAVE_DATA;
|
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);
|
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA);
|
||||||
|
|
||||||
set_timeout(this, this->next_time);
|
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)
|
static int impl_node_process(void *object)
|
||||||
{
|
{
|
||||||
struct impl *this = object;
|
struct impl *this = object;
|
||||||
struct port *port;
|
struct port *port;
|
||||||
struct spa_io_buffers *io;
|
struct spa_io_buffers *io;
|
||||||
struct buffer *buffer;
|
|
||||||
|
|
||||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
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;
|
io->buffer_id = SPA_ID_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle buffering delay */
|
/* Follower produces buffers here, driver in timeout */
|
||||||
process_buffering(this);
|
if (this->following)
|
||||||
|
return produce_buffer(this);
|
||||||
/* Return if there are no buffers ready to be processed */
|
else
|
||||||
if (spa_list_is_empty(&port->ready))
|
|
||||||
return SPA_STATUS_OK;
|
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 = {
|
static const struct spa_node_methods impl_node = {
|
||||||
|
|
|
||||||
|
|
@ -591,13 +591,14 @@ static int setup_matching(struct impl *this)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int produce_buffer(struct impl *this);
|
||||||
|
|
||||||
static void sco_on_timeout(struct spa_source *source)
|
static void sco_on_timeout(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct impl *this = source->data;
|
struct impl *this = source->data;
|
||||||
struct port *port = &this->port;
|
struct port *port = &this->port;
|
||||||
uint64_t exp, duration;
|
uint64_t exp, duration;
|
||||||
uint32_t rate;
|
uint32_t rate;
|
||||||
struct spa_io_buffers *io = port->io;
|
|
||||||
uint64_t prev_time, now_time;
|
uint64_t prev_time, now_time;
|
||||||
|
|
||||||
if (this->transport == NULL)
|
if (this->transport == NULL)
|
||||||
|
|
@ -632,8 +633,11 @@ static void sco_on_timeout(struct spa_source *source)
|
||||||
this->clock->next_nsec = this->next_time;
|
this->clock->next_nsec = this->next_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_log_trace(this->log, "%p: %d", this, io->status);
|
if (port->io) {
|
||||||
io->status = SPA_STATUS_HAVE_DATA;
|
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);
|
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA);
|
||||||
|
|
||||||
set_timeout(this, this->next_time);
|
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 buffer *buffer;
|
||||||
|
struct port *port = &this->port;
|
||||||
|
struct spa_io_buffers *io = port->io;
|
||||||
|
|
||||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
if (io == NULL)
|
||||||
|
|
||||||
port = &this->port;
|
|
||||||
if ((io = port->io) == NULL)
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
/* Return if we already have a buffer */
|
/* Return if we already have a buffer */
|
||||||
|
|
@ -1309,7 +1309,7 @@ static int impl_node_process(void *object)
|
||||||
io->buffer_id = SPA_ID_INVALID;
|
io->buffer_id = SPA_ID_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Produce data */
|
/* Handle buffering */
|
||||||
process_buffering(this);
|
process_buffering(this);
|
||||||
|
|
||||||
/* Return if there are no buffers ready to be processed */
|
/* 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;
|
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 = {
|
static const struct spa_node_methods impl_node = {
|
||||||
SPA_VERSION_NODE_METHODS,
|
SPA_VERSION_NODE_METHODS,
|
||||||
.add_listener = impl_node_add_listener,
|
.add_listener = impl_node_add_listener,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue