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