bluez5: ensure capture target latency is uniform for an ISO group

All BAP client sources in the ISO group should use same target latency,
so that capture streams stay in sync.
This commit is contained in:
Pauli Virtanen 2025-07-12 13:40:54 +03:00 committed by Wim Taymans
parent 396d37594c
commit 7e04f8fe44
4 changed files with 60 additions and 11 deletions

View file

@ -221,7 +221,7 @@ static inline void spa_bt_decode_buffer_set_max_extra_latency(struct spa_bt_deco
this->max_extra = samples;
}
static inline int32_t spa_bt_decode_buffer_get_target_latency(struct spa_bt_decode_buffer *this)
static inline int32_t spa_bt_decode_buffer_get_auto_latency(struct spa_bt_decode_buffer *this)
{
const int32_t duration = this->duration;
const int32_t packet_size = SPA_CLAMP(this->packet_size.max, 0, INT32_MAX/8);
@ -229,16 +229,20 @@ static inline int32_t spa_bt_decode_buffer_get_target_latency(struct spa_bt_deco
const int32_t spike = SPA_CLAMP(this->spike.max, 0, max_buf);
int32_t target;
if (this->target)
target = this->target;
else
target = SPA_CLAMP(SPA_ROUND_UP(SPA_MAX(spike * 3/2, duration),
SPA_CLAMP((int)this->rate / 50, 1, INT32_MAX)),
duration, max_buf - 2*packet_size);
target = SPA_CLAMP(SPA_ROUND_UP(SPA_MAX(spike * 3/2, duration),
SPA_CLAMP((int)this->rate / 50, 1, INT32_MAX)),
duration, max_buf - 2*packet_size);
return SPA_MIN(target, duration + SPA_CLAMP(this->max_extra, 0, INT32_MAX - duration));
}
static inline int32_t spa_bt_decode_buffer_get_target_latency(struct spa_bt_decode_buffer *this)
{
if (this->target)
return this->target;
return spa_bt_decode_buffer_get_auto_latency(this);
}
static inline void spa_bt_decode_buffer_process(struct spa_bt_decode_buffer *this, uint32_t samples, uint32_t duration,
double rate_diff, uint64_t next_nsec)
{

View file

@ -21,6 +21,7 @@
#include "media-codecs.h"
#include "defs.h"
#include "decode-buffer.h"
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.iso");
#undef SPA_LOG_TOPIC_DEFAULT
@ -62,6 +63,8 @@ struct stream {
uint32_t block_size;
struct spa_bt_latency tx_latency;
struct spa_bt_decode_buffer *source_buf;
};
struct modify_info
@ -593,3 +596,27 @@ int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *this)
return spa_bt_latency_recv_errqueue(&stream->tx_latency, stream->fd, group->log);
}
/** Must be called from data thread */
void spa_bt_iso_io_set_source_buffer(struct spa_bt_iso_io *this, struct spa_bt_decode_buffer *buffer)
{
struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
stream->source_buf = buffer;
}
/** Must be called from data thread */
void spa_bt_iso_io_update_source_latency(struct spa_bt_iso_io *this)
{
struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this);
struct group *group = stream->group;
struct stream *s;
int32_t latency = 0;
spa_list_for_each(s, &group->streams, link)
if (s->source_buf)
latency = SPA_MAX(latency, spa_bt_decode_buffer_get_auto_latency(s->source_buf));
if (stream->source_buf)
spa_bt_decode_buffer_set_target_latency(stream->source_buf, latency);
}

View file

@ -11,6 +11,7 @@
#include <spa/node/io.h>
#include <spa/param/audio/format.h>
struct spa_bt_decode_buffer;
struct spa_bt_transport;
/**
@ -46,4 +47,7 @@ void spa_bt_iso_io_destroy(struct spa_bt_iso_io *io);
void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *io, spa_bt_iso_io_pull_t pull, void *user_data);
int spa_bt_iso_io_recv_errqueue(struct spa_bt_iso_io *io);
void spa_bt_iso_io_set_source_buffer(struct spa_bt_iso_io *io, struct spa_bt_decode_buffer *buffer);
void spa_bt_iso_io_update_source_latency(struct spa_bt_iso_io *io);
#endif

View file

@ -843,8 +843,10 @@ static int do_start_sco_iso_io(struct spa_loop *loop, bool async, uint32_t seq,
if (this->transport->sco_io)
spa_bt_sco_io_set_source_cb(this->transport->sco_io, media_sco_pull, this);
if (this->transport->iso_io)
if (this->transport->iso_io) {
spa_bt_iso_io_set_cb(this->transport->iso_io, media_iso_pull, this);
spa_bt_iso_io_set_source_buffer(this->transport->iso_io, &this->port.buffer);
}
return 0;
}
@ -1008,8 +1010,10 @@ static int do_remove_source(struct spa_loop *loop,
if (this->timer_source.loop)
spa_loop_remove_source(this->data_loop, &this->timer_source);
if (this->transport && this->transport->iso_io)
if (this->transport && this->transport->iso_io) {
spa_bt_iso_io_set_cb(this->transport->iso_io, NULL, NULL);
spa_bt_iso_io_set_source_buffer(this->transport->iso_io, NULL);
}
if (this->transport && this->transport->sco_io)
spa_bt_sco_io_set_source_cb(this->transport->sco_io, NULL, NULL);
set_timeout(this, 0);
@ -1646,9 +1650,19 @@ static void update_target_latency(struct impl *this)
if (this->transport == NULL || !port->have_format)
return;
if (this->codec->kind != MEDIA_CODEC_BAP)
return;
if (this->codec->kind != MEDIA_CODEC_BAP || this->is_input ||
this->transport->delay_us == SPA_BT_UNKNOWN_DELAY)
if (this->is_input) {
/* BAP Client. Should use same buffer size for all streams in the same
* group, so that capture is in sync.
*/
if (this->transport->iso_io)
spa_bt_iso_io_update_source_latency(this->transport->iso_io);
return;
}
if (this->transport->delay_us == SPA_BT_UNKNOWN_DELAY)
return;
get_samples(this, &duration);