From 5ae0dfb2391a2a7f1ddf869f1fa4011db825f464 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Mon, 1 Apr 2024 17:26:35 +0300 Subject: [PATCH] bluez5: bap: determine SDU interval from codec frame duration BlueZ API as BAP Server gives us the ISO interval, instead of the SDU interval, in the MediaTransport.QoS.Interval property. They are not necessarily the same. What we need is the SDU interval. The SDU interval is the interval between packets the encoder outputs, so it is determined by the codec configuration, and for LC3 is equal to the frame duration. Add codec method get_interval() that returns the correct interval, and use it in iso-io. --- spa/plugins/bluez5/bap-codec-lc3.c | 8 ++++++ spa/plugins/bluez5/bluez5-dbus.c | 1 - spa/plugins/bluez5/defs.h | 1 - spa/plugins/bluez5/iso-io.c | 39 +++++++++++++++++++++--------- spa/plugins/bluez5/media-codecs.h | 14 ++++++++--- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index 3e4a0c091..66a5902a7 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -1008,6 +1008,13 @@ static int codec_get_block_size(void *data) return this->codesize; } +static uint64_t codec_get_interval(void *data) +{ + struct impl *this = data; + + return (uint64_t)this->frame_dus * 1000; +} + static int codec_abr_process (void *data, size_t unsent) { return -ENOTSUP; @@ -1120,6 +1127,7 @@ const struct media_codec bap_codec_lc3 = { .init = codec_init, .deinit = codec_deinit, .get_block_size = codec_get_block_size, + .get_interval = codec_get_interval, .abr_process = codec_abr_process, .start_encode = codec_start_encode, .encode = codec_encode, diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index fdea5bda7..aca2c75eb 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -3357,7 +3357,6 @@ static int transport_update_props(struct spa_bt_transport *transport, transport->bap_bis = qos.bis; transport->delay_us = qos.qos.delay; transport->latency_us = (unsigned int)qos.qos.latency * 1000; - transport->bap_interval = qos.qos.interval; spa_bt_transport_emit_delay_changed(transport); } diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h index 298376f4a..a797073ec 100644 --- a/spa/plugins/bluez5/defs.h +++ b/spa/plugins/bluez5/defs.h @@ -659,7 +659,6 @@ struct spa_bt_transport { uint8_t bap_cis; uint8_t bap_big; uint8_t bap_bis; - uint32_t bap_interval; struct spa_bt_iso_io *iso_io; struct spa_bt_sco_io *sco_io; diff --git a/spa/plugins/bluez5/iso-io.c b/spa/plugins/bluez5/iso-io.c index f70486829..6b1e7378f 100644 --- a/spa/plugins/bluez5/iso-io.c +++ b/spa/plugins/bluez5/iso-io.c @@ -135,6 +135,9 @@ static int set_timers(struct group *group) { struct timespec now; + if (group->duration == 0) + return -EINVAL; + spa_system_clock_gettime(group->data_system, CLOCK_MONOTONIC, &now); group->next = SPA_ROUND_UP(SPA_TIMESPEC_TO_NSEC(&now) + group->duration, group->duration); @@ -244,11 +247,6 @@ static struct group *group_create(struct spa_bt_transport *t, struct group *group; uint8_t id; - if (t->bap_interval <= 5000) { - errno = EINVAL; - return NULL; - } - if (t->profile & (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE)) { id = t->bap_cig; } else if (t->profile & (SPA_BT_PROFILE_BAP_BROADCAST_SINK | SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) { @@ -268,7 +266,7 @@ static struct group *group_create(struct spa_bt_transport *t, group->log = log; group->data_loop = data_loop; group->data_system = data_system; - group->duration = t->bap_interval * SPA_NSEC_PER_USEC; + group->duration = 0; spa_list_init(&group->streams); @@ -325,20 +323,22 @@ static struct stream *stream_create(struct spa_bt_transport *t, struct group *gr struct spa_audio_info format = { 0 }; int res; bool sink; - if((t->profile == SPA_BT_PROFILE_BAP_SINK) || - (t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK)) { + + if (t->profile == SPA_BT_PROFILE_BAP_SINK || + t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) { sink = true; } else { sink = false; } - - if (!t->media_codec->bap) { + if (!t->media_codec->bap || !t->media_codec->get_interval) { res = -EINVAL; goto fail; } if (sink) { + uint64_t interval; + res = t->media_codec->validate_config(t->media_codec, 0, t->configuration, t->configuration_len, &format); if (res < 0) goto fail; @@ -355,6 +355,20 @@ static struct stream *stream_create(struct spa_bt_transport *t, struct group *gr res = -EINVAL; goto fail; } + + interval = t->media_codec->get_interval(codec_data); + if (interval <= 5000) { + res = -EINVAL; + goto fail; + } + + if (group->duration == 0) { + group->duration = interval; + } else if (interval != group->duration) { + /* SDU_Interval in ISO group must be same for each direction */ + res = -EINVAL; + goto fail; + } } stream = calloc(1, sizeof(struct stream)); @@ -364,7 +378,7 @@ static struct stream *stream_create(struct spa_bt_transport *t, struct group *gr stream->fd = t->fd; stream->sink = sink; stream->group = group; - stream->this.duration = group->duration; + stream->this.duration = sink ? group->duration : 0; stream->codec = t->media_codec; stream->this.codec_data = codec_data; @@ -452,6 +466,9 @@ void spa_bt_iso_io_set_cb(struct spa_bt_iso_io *this, spa_bt_iso_io_pull_t pull, struct stream *stream = SPA_CONTAINER_OF(this, struct stream, this); bool was_enabled, enabled; + if (!stream->sink) + return; + was_enabled = group_is_enabled(stream->group); stream->pull = pull; diff --git a/spa/plugins/bluez5/media-codecs.h b/spa/plugins/bluez5/media-codecs.h index 7d7df5246..5084bfc72 100644 --- a/spa/plugins/bluez5/media-codecs.h +++ b/spa/plugins/bluez5/media-codecs.h @@ -26,7 +26,7 @@ #define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private" -#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 7 +#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 8 struct spa_bluez5_codec_a2dp { struct spa_interface iface; @@ -81,8 +81,6 @@ struct media_codec { const struct media_codec *duplex_codec; /**< Codec for non-standard A2DP duplex channel */ - struct spa_log *log; - /** If fill_caps is NULL, no endpoint is registered (for sharing with another codec). */ int (*fill_caps) (const struct media_codec *codec, uint32_t flags, uint8_t caps[A2DP_MAX_CAPS_SIZE]); @@ -126,6 +124,16 @@ struct media_codec { /** Number of bytes needed for encoding */ int (*get_block_size) (void *data); + /** + * Duration of the next packet in nanoseconds. + * + * For BAP this shall be constant and equal to the SDU interval. + * + * \param data Codec data from init() + * \return Duration in nanoseconds. + */ + uint64_t (*get_interval) (void *data); + int (*abr_process) (void *data, size_t unsent); /**