mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
bluez: process available buffers in read_ready callback for a2dp-source and sco-source
This commit is contained in:
parent
84405dae2a
commit
5363d3352c
2 changed files with 172 additions and 160 deletions
|
|
@ -302,6 +302,8 @@ static void decode_sbc_data(struct impl *this, uint8_t *src, size_t src_size)
|
||||||
{
|
{
|
||||||
const ssize_t header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
|
const ssize_t header_size = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
|
||||||
struct port *port = &this->port;
|
struct port *port = &this->port;
|
||||||
|
struct spa_io_buffers *io = port->io;
|
||||||
|
int32_t io_done_status = io->status;
|
||||||
struct buffer *buffer;
|
struct buffer *buffer;
|
||||||
struct spa_data *data;
|
struct spa_data *data;
|
||||||
uint8_t *dest;
|
uint8_t *dest;
|
||||||
|
|
@ -312,25 +314,17 @@ static void decode_sbc_data(struct impl *this, uint8_t *src, size_t src_size)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* skip the header */
|
/* Skip the header */
|
||||||
src += header_size;
|
src += header_size;
|
||||||
src_size -= header_size;
|
src_size -= header_size;
|
||||||
|
|
||||||
/* check if we have a new buffer */
|
/* Decode data if we have a buffer free */
|
||||||
if (spa_list_is_empty(&port->free)) {
|
if (!spa_list_is_empty(&port->free)) {
|
||||||
spa_log_warn(this->log, "no more buffers available, dropping data...");
|
/* Get the free buffer and remove it from the free list */
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the buffer */
|
|
||||||
buffer = spa_list_first(&port->free, struct buffer, link);
|
buffer = spa_list_first(&port->free, struct buffer, link);
|
||||||
|
|
||||||
/* remove the the buffer from the list */
|
|
||||||
spa_list_remove(&buffer->link);
|
spa_list_remove(&buffer->link);
|
||||||
/* update the outstanding flag */
|
|
||||||
buffer->outstanding = true;
|
|
||||||
|
|
||||||
/* set the header */
|
/* Set the header */
|
||||||
if (buffer->h) {
|
if (buffer->h) {
|
||||||
buffer->h->seq = this->sample_count;
|
buffer->h->seq = this->sample_count;
|
||||||
buffer->h->pts = SPA_TIMESPEC_TO_NSEC(&this->now);
|
buffer->h->pts = SPA_TIMESPEC_TO_NSEC(&this->now);
|
||||||
|
|
@ -345,7 +339,6 @@ static void decode_sbc_data(struct impl *this, uint8_t *src, size_t src_size)
|
||||||
/* decode the source data */
|
/* decode the source data */
|
||||||
spa_log_debug(this->log, "decoding data for buffer_id=%d %zd %zd",
|
spa_log_debug(this->log, "decoding data for buffer_id=%d %zd %zd",
|
||||||
buffer->id, src_size, dest_size);
|
buffer->id, src_size, dest_size);
|
||||||
|
|
||||||
while (src_size > 0 && dest_size > 0) {
|
while (src_size > 0 && dest_size > 0) {
|
||||||
decoded = sbc_decode(&this->sbc,
|
decoded = sbc_decode(&this->sbc,
|
||||||
src, src_size,
|
src, src_size,
|
||||||
|
|
@ -376,9 +369,29 @@ static void decode_sbc_data(struct impl *this, uint8_t *src, size_t src_size)
|
||||||
/* add the buffer to the queue */
|
/* add the buffer to the queue */
|
||||||
spa_log_debug(this->log, "data decoded %d successfully for buffer_id=%d",
|
spa_log_debug(this->log, "data decoded %d successfully for buffer_id=%d",
|
||||||
data[0].chunk->size, buffer->id);
|
data[0].chunk->size, buffer->id);
|
||||||
|
buffer->outstanding = true;
|
||||||
spa_list_append(&port->ready, &buffer->link);
|
spa_list_append(&port->ready, &buffer->link);
|
||||||
|
}
|
||||||
|
|
||||||
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_BUFFER);
|
/* Process a buffer if there is one ready and IO does not have one */
|
||||||
|
if (!spa_list_is_empty(&port->ready) && io->status != SPA_STATUS_HAVE_BUFFER) {
|
||||||
|
/* Get the ready buffer and remove it from the ready list */
|
||||||
|
buffer = spa_list_first(&port->ready, struct buffer, link);
|
||||||
|
spa_list_remove(&buffer->link);
|
||||||
|
|
||||||
|
/* Mark the buffer to be processed */
|
||||||
|
io->buffer_id = buffer->id;
|
||||||
|
io->status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
|
/* Add the buffer to the free list */
|
||||||
|
spa_list_append(&port->free, &buffer->link);
|
||||||
|
buffer->outstanding = false;
|
||||||
|
|
||||||
|
/* Set the done status as have buffer */
|
||||||
|
io_done_status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_node_call_ready(&this->callbacks, io_done_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void a2dp_on_ready_read(struct spa_source *source)
|
static void a2dp_on_ready_read(struct spa_source *source)
|
||||||
|
|
@ -761,10 +774,7 @@ impl_node_port_enum_params(void *object, int seq,
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: why filer is != NULL when linking it with a2dp-sink? */
|
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
|
||||||
/* if filter is null a2dp-source cannot be linked with a2dp-sink,
|
|
||||||
* so for now we always pass NULL */
|
|
||||||
if (spa_pod_filter(&b, &result.param, param, NULL) < 0)
|
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||||
|
|
@ -964,39 +974,35 @@ 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 *b;
|
struct buffer *buffer;
|
||||||
|
|
||||||
/* get IO */
|
|
||||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||||
|
|
||||||
port = &this->port;
|
port = &this->port;
|
||||||
io = port->io;
|
io = port->io;
|
||||||
spa_return_val_if_fail(io != NULL, -EIO);
|
spa_return_val_if_fail(io != NULL, -EIO);
|
||||||
|
|
||||||
/* don't do anything if IO does not need a buffer */
|
/* Return if we already have a buffer */
|
||||||
if (io->status != SPA_STATUS_NEED_BUFFER)
|
if (io->status == SPA_STATUS_HAVE_BUFFER)
|
||||||
return io->status;
|
|
||||||
|
|
||||||
/* Recycle previously played buffer */
|
|
||||||
if (io->buffer_id != SPA_ID_INVALID &&
|
|
||||||
io->buffer_id < port->n_buffers) {
|
|
||||||
spa_log_debug(this->log, "recycling buffer_id=%d", io->buffer_id);
|
|
||||||
recycle_buffer(this, port, io->buffer_id);
|
|
||||||
io->buffer_id = SPA_ID_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have new buffers in the queue */
|
|
||||||
if (spa_list_is_empty(&port->ready))
|
|
||||||
return SPA_STATUS_HAVE_BUFFER;
|
return SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
/* Pop the new buffer from the queue */
|
/* Return if there is not buffers ready to be processed */
|
||||||
b = spa_list_first(&port->ready, struct buffer, link);
|
if (spa_list_is_empty(&port->ready))
|
||||||
spa_list_remove(&b->link);
|
return io->status;
|
||||||
|
|
||||||
/* Set the new buffer in IO to be played */
|
/* Get the new buffer from the ready list */
|
||||||
io->buffer_id = b->id;
|
buffer = spa_list_first(&port->ready, struct buffer, link);
|
||||||
|
spa_list_remove(&buffer->link);
|
||||||
|
|
||||||
|
/* Set the new buffer in IO */
|
||||||
|
io->buffer_id = buffer->id;
|
||||||
io->status = SPA_STATUS_HAVE_BUFFER;
|
io->status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
|
/* Add the buffer to the free list */
|
||||||
|
spa_list_append(&port->free, &buffer->link);
|
||||||
|
buffer->outstanding = false;
|
||||||
|
|
||||||
|
/* Notify we have a buffer ready to be processed */
|
||||||
return SPA_STATUS_HAVE_BUFFER;
|
return SPA_STATUS_HAVE_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ struct props {
|
||||||
uint32_t max_latency;
|
uint32_t max_latency;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FILL_FRAMES 2
|
||||||
#define MAX_BUFFERS 32
|
#define MAX_BUFFERS 32
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
|
|
@ -295,22 +296,19 @@ static bool read_data(struct impl *this, uint8_t *data, uint32_t size, uint32_t
|
||||||
const uint32_t mtu_size = this->transport->read_mtu;
|
const uint32_t mtu_size = this->transport->read_mtu;
|
||||||
uint32_t local_total_read = 0;
|
uint32_t local_total_read = 0;
|
||||||
|
|
||||||
/* TODO: For now we assume the size is always a mutliple of mtu_size */
|
/* Read chunks of mtu_size */
|
||||||
while (local_total_read < (size - mtu_size)) {
|
while (local_total_read <= (size - mtu_size)) {
|
||||||
const int bytes_read = read(this->sock_fd, data, mtu_size);
|
const int bytes_read = read(this->sock_fd, data, mtu_size);
|
||||||
if (bytes_read == 0) {
|
if (bytes_read < 0) {
|
||||||
/* Stop */
|
|
||||||
return false;
|
|
||||||
} else if (bytes_read < 0) {
|
|
||||||
/* Retry */
|
/* Retry */
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Socked has no data so return total data read */
|
/* Socked has no data */
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Print error and stop */
|
/* Error */
|
||||||
spa_log_error(this->log, "read error: %s", strerror(errno));
|
spa_log_error(this->log, "read error: %s", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -325,55 +323,81 @@ done:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void recycle_buffer(struct impl *this, struct port *port, uint32_t buffer_id)
|
||||||
|
{
|
||||||
|
struct buffer *b = &port->buffers[buffer_id];
|
||||||
|
|
||||||
|
if (b->outstanding) {
|
||||||
|
spa_log_trace(this->log, NAME " %p: recycle buffer %u", this, buffer_id);
|
||||||
|
spa_list_append(&port->free, &b->link);
|
||||||
|
b->outstanding = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void sco_on_ready_read(struct spa_source *source)
|
static void sco_on_ready_read(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct impl *this = source->data;
|
struct impl *this = source->data;
|
||||||
struct port *port = &this->port;
|
struct port *port = &this->port;
|
||||||
|
struct spa_io_buffers *io = port->io;
|
||||||
|
int32_t io_done_status = io->status;
|
||||||
struct buffer *buffer;
|
struct buffer *buffer;
|
||||||
struct spa_data *buffer_data;
|
struct spa_data *buffer_data;
|
||||||
uint32_t total_read;
|
uint32_t total_read;
|
||||||
|
|
||||||
/* update the current pts */
|
spa_return_if_fail(io != NULL);
|
||||||
clock_gettime(CLOCK_MONOTONIC, &this->now);
|
|
||||||
|
|
||||||
/* check if we have a new buffer */
|
/* Read a buffer if there is one free */
|
||||||
if (spa_list_is_empty(&port->free)) {
|
if (!spa_list_is_empty(&port->free)) {
|
||||||
spa_log_warn(this->log, "waiting for buffer");
|
/* Get the free buffer and remove it from the free list */
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the buffer data */
|
|
||||||
buffer = spa_list_first(&port->free, struct buffer, link);
|
buffer = spa_list_first(&port->free, struct buffer, link);
|
||||||
|
spa_list_remove(&buffer->link);
|
||||||
|
|
||||||
buffer_data = &buffer->buf->datas[0];
|
buffer_data = &buffer->buf->datas[0];
|
||||||
spa_assert(buffer_data->data);
|
spa_assert(buffer_data->data);
|
||||||
|
|
||||||
/* read data */
|
/* Read sco data */
|
||||||
if (!read_data(this, buffer_data->data, buffer_data->maxsize, &total_read))
|
if (!read_data(this, buffer_data->data, buffer_data->maxsize, &total_read)) {
|
||||||
goto stop;
|
if (this->source.loop)
|
||||||
if (total_read == 0)
|
spa_loop_remove_source(this->data_loop, &this->source);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* update the buffer offset, size and stride */
|
/* Append a ready buffer if data could be read */
|
||||||
|
if (total_read > 0) {
|
||||||
|
/* Update the buffer offset, size and stride */
|
||||||
buffer_data->chunk->offset = 0;
|
buffer_data->chunk->offset = 0;
|
||||||
buffer_data->chunk->size = total_read;
|
buffer_data->chunk->size = total_read;
|
||||||
buffer_data->chunk->stride = port->frame_size;
|
buffer_data->chunk->stride = port->frame_size;
|
||||||
|
|
||||||
/* update the sample count */
|
/* Update the sample count */
|
||||||
this->sample_count += buffer_data->chunk->size / port->frame_size;
|
this->sample_count += buffer_data->chunk->size / port->frame_size;
|
||||||
|
|
||||||
/* remove the buffer from the free list and add it to the ready list */
|
/* Add the buffer to the ready list */
|
||||||
spa_list_remove(&buffer->link);
|
|
||||||
buffer->outstanding = true;
|
buffer->outstanding = true;
|
||||||
spa_list_append(&port->ready, &buffer->link);
|
spa_list_append(&port->ready, &buffer->link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Notify we are ready for the next buffer */
|
/* Process a buffer if there is one ready and IO does not have one */
|
||||||
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_BUFFER);
|
if (!spa_list_is_empty(&port->ready) && io->status != SPA_STATUS_HAVE_BUFFER) {
|
||||||
|
/* Get the ready buffer and remove it from the ready list */
|
||||||
|
buffer = spa_list_first(&port->ready, struct buffer, link);
|
||||||
|
spa_list_remove(&buffer->link);
|
||||||
|
|
||||||
return;
|
/* Mark the buffer to be processed */
|
||||||
|
io->buffer_id = buffer->id;
|
||||||
|
io->status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
stop:
|
/* Add the buffer to the free list */
|
||||||
if (this->source.loop)
|
spa_list_append(&port->free, &buffer->link);
|
||||||
spa_loop_remove_source(this->data_loop, &this->source);
|
buffer->outstanding = false;
|
||||||
|
|
||||||
|
/* Set the done status as have buffer */
|
||||||
|
io_done_status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify the current status */
|
||||||
|
spa_node_call_ready(&this->callbacks, io_done_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_start(struct impl *this)
|
static int do_start(struct impl *this)
|
||||||
|
|
@ -397,12 +421,12 @@ static int do_start(struct impl *this)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Set the write MTU */
|
/* Set the write MTU */
|
||||||
val = this->transport->write_mtu;
|
val = FILL_FRAMES * this->transport->write_mtu;
|
||||||
if (setsockopt(this->sock_fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
|
if (setsockopt(this->sock_fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
|
||||||
spa_log_warn(this->log, "sco-source %p: SO_SNDBUF %m", this);
|
spa_log_warn(this->log, "sco-source %p: SO_SNDBUF %m", this);
|
||||||
|
|
||||||
/* Set the read MTU */
|
/* Set the read MTU */
|
||||||
val = this->transport->read_mtu;
|
val = FILL_FRAMES * this->transport->read_mtu;
|
||||||
if (setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)
|
if (setsockopt(this->sock_fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)
|
||||||
spa_log_warn(this->log, "sco-source %p: SO_RCVBUF %m", this);
|
spa_log_warn(this->log, "sco-source %p: SO_RCVBUF %m", this);
|
||||||
|
|
||||||
|
|
@ -681,10 +705,7 @@ impl_node_port_enum_params(void *object, int seq,
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: why filer is != NULL when linking it with sco-sink? */
|
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
|
||||||
/* if filter is null sco-source cannot be linked with sco-sink,
|
|
||||||
* so for now we always pass NULL */
|
|
||||||
if (spa_pod_filter(&b, &result.param, param, NULL) < 0)
|
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||||
|
|
@ -847,17 +868,6 @@ impl_node_port_set_io(void *object,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recycle_buffer(struct impl *this, struct port *port, uint32_t buffer_id)
|
|
||||||
{
|
|
||||||
struct buffer *b = &port->buffers[buffer_id];
|
|
||||||
|
|
||||||
if (b->outstanding) {
|
|
||||||
spa_log_trace(this->log, NAME " %p: recycle buffer %u", this, buffer_id);
|
|
||||||
spa_list_append(&port->free, &b->link);
|
|
||||||
b->outstanding = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
|
static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
|
||||||
{
|
{
|
||||||
struct impl *this = object;
|
struct impl *this = object;
|
||||||
|
|
@ -884,39 +894,35 @@ 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 *b;
|
struct buffer *buffer;
|
||||||
|
|
||||||
/* get IO */
|
|
||||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||||
|
|
||||||
port = &this->port;
|
port = &this->port;
|
||||||
io = port->io;
|
io = port->io;
|
||||||
spa_return_val_if_fail(io != NULL, -EIO);
|
spa_return_val_if_fail(io != NULL, -EIO);
|
||||||
|
|
||||||
/* don't do anything if IO does not need a buffer */
|
/* Return if we already have a buffer */
|
||||||
if (io->status != SPA_STATUS_NEED_BUFFER)
|
if (io->status == SPA_STATUS_HAVE_BUFFER)
|
||||||
return io->status;
|
|
||||||
|
|
||||||
/* Recycle previously played buffer */
|
|
||||||
if (io->buffer_id != SPA_ID_INVALID &&
|
|
||||||
io->buffer_id < port->n_buffers) {
|
|
||||||
spa_log_debug(this->log, "recycling buffer_id=%d", io->buffer_id);
|
|
||||||
recycle_buffer(this, port, io->buffer_id);
|
|
||||||
io->buffer_id = SPA_ID_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have new buffers in the queue */
|
|
||||||
if (spa_list_is_empty(&port->ready))
|
|
||||||
return SPA_STATUS_HAVE_BUFFER;
|
return SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
/* Pop the new buffer from the queue */
|
/* Return if there is not buffers ready to be processed */
|
||||||
b = spa_list_first(&port->ready, struct buffer, link);
|
if (spa_list_is_empty(&port->ready))
|
||||||
spa_list_remove(&b->link);
|
return io->status;
|
||||||
|
|
||||||
/* Set the new buffer in IO to be played */
|
/* Get the new buffer from the ready list */
|
||||||
io->buffer_id = b->id;
|
buffer = spa_list_first(&port->ready, struct buffer, link);
|
||||||
|
spa_list_remove(&buffer->link);
|
||||||
|
|
||||||
|
/* Set the new buffer in IO */
|
||||||
|
io->buffer_id = buffer->id;
|
||||||
io->status = SPA_STATUS_HAVE_BUFFER;
|
io->status = SPA_STATUS_HAVE_BUFFER;
|
||||||
|
|
||||||
|
/* Add the buffer to the free list */
|
||||||
|
spa_list_append(&port->free, &buffer->link);
|
||||||
|
buffer->outstanding = false;
|
||||||
|
|
||||||
|
/* Notify we have a buffer ready to be processed */
|
||||||
return SPA_STATUS_HAVE_BUFFER;
|
return SPA_STATUS_HAVE_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue