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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue