bluez5: sco-sink: Add follower mode

This commit is contained in:
George Kiagiadakis 2021-09-10 11:57:49 +03:00 committed by Wim Taymans
parent 61a2577941
commit becd76de58

View file

@ -130,6 +130,7 @@ struct impl {
/* Flags */ /* Flags */
unsigned int started:1; unsigned int started:1;
unsigned int following:1;
/* Sources */ /* Sources */
struct spa_source source; struct spa_source source;
@ -243,18 +244,20 @@ static int impl_node_enum_params(void *object, int seq,
return 0; return 0;
} }
static void set_timeout(struct impl *this, uint64_t timeout) static int set_timeout(struct impl *this, uint64_t time)
{ {
struct itimerspec ts; struct itimerspec ts;
ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC;
ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC;
ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC;
ts.it_interval.tv_sec = 0; ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0; ts.it_interval.tv_nsec = 0;
return spa_system_timerfd_settime(this->data_system,
this->timerfd, 0, &ts, NULL);
}
spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); static int set_timers(struct impl *this)
this->source.mask = SPA_IO_IN; {
spa_loop_update_source(this->data_loop, &this->source); return set_timeout(this, this->following ? 0 : 1);
} }
static uint64_t get_next_timeout(struct impl *this, uint64_t now_time, uint64_t processed_samples) static uint64_t get_next_timeout(struct impl *this, uint64_t now_time, uint64_t processed_samples)
@ -273,11 +276,29 @@ static uint64_t get_next_timeout(struct impl *this, uint64_t now_time, uint64_t
return next_time; return next_time;
} }
static int do_reassign_follower(struct spa_loop *loop,
bool async,
uint32_t seq,
const void *data,
size_t size,
void *user_data)
{
struct impl *this = user_data;
set_timers(this);
return 0;
}
static inline bool is_following(struct impl *this)
{
return this->position && this->clock && this->position->clock.id != this->clock->id;
}
static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
{ {
struct impl *this = object; struct impl *this = object;
bool following;
spa_return_val_if_fail(object != NULL, -EINVAL); spa_return_val_if_fail(this != NULL, -EINVAL);
switch (id) { switch (id) {
case SPA_IO_Clock: case SPA_IO_Clock:
@ -290,6 +311,12 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
return -ENOENT; return -ENOENT;
} }
following = is_following(this);
if (this->started && following != this->following) {
spa_log_debug(this->log, NAME " %p: reassign follower %d->%d", this, this->following, following);
this->following = following;
spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this);
}
return 0; return 0;
} }
@ -343,61 +370,50 @@ static void flush_data(struct impl *this)
struct port *port = &this->port; struct port *port = &this->port;
struct spa_data *datas; struct spa_data *datas;
uint64_t next_timeout = 1; uint64_t next_timeout = 1;
uint32_t min_in_size; const uint32_t min_in_size =
uint8_t *packet; (this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
MSBC_DECODED_SIZE : this->transport->write_mtu;
uint8_t * const packet =
(this->transport->codec == HFP_AUDIO_CODEC_MSBC) ?
this->buffer_head : port->write_buffer;
if (this->transport == NULL || this->transport->sco_io == NULL) if (this->transport == NULL || this->transport->sco_io == NULL)
return; return;
/* get buffer */ if (!spa_list_is_empty(&port->ready)) {
if (!port->current_buffer) { /* get buffer */
spa_return_if_fail(!spa_list_is_empty(&port->ready)); if (!port->current_buffer) {
port->current_buffer = spa_list_first(&port->ready, struct buffer, link); spa_return_if_fail(!spa_list_is_empty(&port->ready));
port->ready_offset = 0; port->current_buffer = spa_list_first(&port->ready, struct buffer, link);
} port->ready_offset = 0;
datas = port->current_buffer->buf->datas; }
datas = port->current_buffer->buf->datas;
if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) { /* if buffer has data, copy it into the write buffer */
min_in_size = MSBC_DECODED_SIZE; if (datas[0].chunk->size - port->ready_offset > 0) {
packet = this->buffer_head; const uint32_t avail =
} else { SPA_MIN(min_in_size, datas[0].chunk->size - port->ready_offset);
min_in_size = this->transport->write_mtu; const uint32_t size =
packet = port->write_buffer; (avail + port->write_buffer_size) > min_in_size ?
} min_in_size - port->write_buffer_size : avail;
memcpy(port->write_buffer + port->write_buffer_size,
(uint8_t *)datas[0].data + port->ready_offset,
size);
port->write_buffer_size += size;
port->ready_offset += size;
} else {
struct buffer *b;
/* if buffer has data, copy it into the write buffer */ b = port->current_buffer;
if (datas[0].chunk->size - port->ready_offset > 0) { port->current_buffer = NULL;
uint32_t avail = SPA_MIN(min_in_size, datas[0].chunk->size - port->ready_offset);
uint32_t size = (avail + port->write_buffer_size) > min_in_size ? min_in_size - port->write_buffer_size : avail;
memcpy(port->write_buffer + port->write_buffer_size,
(uint8_t *)datas[0].data + port->ready_offset,
size);
port->write_buffer_size += size;
port->ready_offset += size;
}
/* otherwise request a new buffer */ /* reuse buffer */
else { spa_list_remove(&b->link);
struct buffer *b; b->outstanding = true;
spa_log_trace(this->log, "sco-sink %p: reuse buffer %u", this, b->id);
b = port->current_buffer; port->io->buffer_id = b->id;
port->current_buffer = NULL; spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
}
/* reuse buffer */
spa_list_remove(&b->link);
b->outstanding = true;
spa_log_trace(this->log, "sco-sink %p: reuse buffer %u", this, b->id);
port->io->buffer_id = b->id;
spa_node_call_reuse_buffer(&this->callbacks, 0, b->id);
/* notify we need more data */
port->io->status = SPA_STATUS_NEED_DATA;
spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
next_timeout = (this->transport->write_mtu / port->frame_size
* SPA_NSEC_PER_SEC / port->current_format.info.raw.rate);
set_timeout(this, next_timeout);
return;
} }
/* send the data if the write buffer is full */ /* send the data if the write buffer is full */
@ -476,6 +492,8 @@ static void flush_data(struct impl *this)
} }
} }
spa_log_trace(this->log, "write socket data %d", written);
next_timeout = get_next_timeout(this, now_time, processed / port->frame_size); next_timeout = get_next_timeout(this, now_time, processed / port->frame_size);
if (this->clock) { if (this->clock) {
@ -510,7 +528,7 @@ static void sco_on_timeout(struct spa_source *source)
spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno));
/* delay if no buffers available */ /* delay if no buffers available */
if (spa_list_is_empty(&port->ready)) { if (!this->following && spa_list_is_empty(&port->ready)) {
set_timeout(this, this->transport->write_mtu / port->frame_size * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate); set_timeout(this, this->transport->write_mtu / port->frame_size * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate);
port->io->status = SPA_STATUS_NEED_DATA; port->io->status = SPA_STATUS_NEED_DATA;
spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA); spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA);
@ -547,6 +565,10 @@ static int do_start(struct impl *this)
/* Make sure the transport is valid */ /* Make sure the transport is valid */
spa_return_val_if_fail(this->transport != NULL, -EIO); spa_return_val_if_fail(this->transport != NULL, -EIO);
this->following = is_following(this);
spa_log_debug(this->log, NAME " %p: start following:%d", this, this->following);
/* Do accept if Gateway; otherwise do connect for Head Unit */ /* Do accept if Gateway; otherwise do connect for Head Unit */
do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY; do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
@ -586,7 +608,7 @@ static int do_start(struct impl *this)
spa_loop_add_source(this->data_loop, &this->source); spa_loop_add_source(this->data_loop, &this->source);
/* start processing */ /* start processing */
set_timeout(this, 1); set_timers(this);
/* Set the started flag */ /* Set the started flag */
this->started = true; this->started = true;