Bluetooth: BAP: Broadcast Source: Update stream codec config data

When creating a BAP broadcast source with bt_bap_broadcast_source_create
only the subgroup information is stored in the streams and the remaining
BIS specific information is not stored in the stream->codec_cfg,
which it should.
Fix is to store bis specific information also in stream codec config.
Updated broadcast source BSIM test to verify above usecase.

Signed-off-by: Nithin Ramesh Myliattil <niym@demant.com>
This commit is contained in:
Nithin Ramesh Myliattil 2024-05-17 06:08:29 +02:00 committed by Anas Nashif
commit df45858d0f
6 changed files with 249 additions and 20 deletions

View file

@ -658,6 +658,59 @@ static enum bt_bap_ep_state broadcast_source_get_state(struct bt_bap_broadcast_s
return stream->ep->status.state;
}
static bool merge_bis_and_subgroup_data_cb(struct bt_data *data, void *user_data)
{
struct bt_audio_codec_cfg *codec_cfg = user_data;
int err;
err = bt_audio_codec_cfg_set_val(codec_cfg, data->type, data->data, data->data_len);
if (err < 0) {
LOG_DBG("Failed to set type %u with len %u in codec_cfg: %d", data->type,
data->data_len, err);
return false;
}
return true;
}
static int update_codec_cfg_data(struct bt_audio_codec_cfg *codec_cfg,
const struct bt_bap_broadcast_source_stream_param *stream_param)
{
int err;
/* Merge subgroup codec configuration with the BIS configuration
* As per the BAP spec, if a value exist at level 2 (subgroup) and 3 (BIS), then it is
* the value at level 3 that shall be used
*/
if (codec_cfg->id == BT_HCI_CODING_FORMAT_LC3) {
err = bt_audio_data_parse(stream_param->data, stream_param->data_len,
merge_bis_and_subgroup_data_cb, codec_cfg);
if (err != 0) {
LOG_DBG("Could not merge BIS and subgroup config in codec_cfg: %d", err);
return -EINVAL;
}
} else {
/* If it is not LC3, then we don't know how to merge the subgroup and BIS codecs,
* so we just append them
*/
if (codec_cfg->data_len + stream_param->data_len >
sizeof(codec_cfg->data)) {
LOG_DBG("Could not store BIS and subgroup config in codec_cfg (%u > %u)",
codec_cfg->data_len + stream_param->data_len,
sizeof(codec_cfg->data));
return -ENOMEM;
}
memcpy(&codec_cfg->data[codec_cfg->data_len], stream_param->data,
stream_param->data_len);
codec_cfg->data_len += stream_param->data_len;
}
return 0;
}
int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param,
struct bt_bap_broadcast_source **out_source)
{
@ -665,6 +718,7 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param,
struct bt_audio_codec_qos *qos;
size_t stream_count;
uint8_t index;
uint8_t bis_count;
int err;
CHECKIF(out_source == NULL) {
@ -694,7 +748,7 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param,
}
stream_count = 0U;
bis_count = 0U;
qos = param->qos;
/* Go through all subgroups and streams and setup each setup with an
* endpoint
@ -726,12 +780,33 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param,
for (size_t j = 0U; j < subgroup_param->params_count; j++) {
const struct bt_bap_broadcast_source_stream_param *stream_param;
struct bt_bap_stream *stream;
struct bt_audio_codec_cfg *codec_cfg;
codec_cfg = subgroup_param->codec_cfg;
stream_param = &subgroup_param->params[j];
stream = stream_param->stream;
if (CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0) {
if (bis_count >= BROADCAST_STREAM_CNT) {
LOG_DBG("Stream count %d exceeded", bis_count);
return -ENOMEM;
}
codec_cfg = &source->codec_cfg[bis_count];
memcpy(codec_cfg, subgroup_param->codec_cfg,
sizeof(struct bt_audio_codec_cfg));
err = update_codec_cfg_data(codec_cfg, stream_param);
if (err != 0) {
LOG_DBG("codec config update failed [%zu]: %d", i, err);
broadcast_source_cleanup(source);
return err;
}
bis_count++;
}
err = broadcast_source_setup_stream(index, stream,
subgroup_param->codec_cfg, qos, source);
codec_cfg, qos, source);
if (err != 0) {
LOG_DBG("Failed to setup streams[%zu]: %d", i, err);
broadcast_source_cleanup(source);
@ -789,6 +864,7 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source,
enum bt_bap_ep_state broadcast_state;
struct bt_audio_codec_qos *qos;
size_t subgroup_cnt;
uint8_t bis_count;
CHECKIF(source == NULL) {
LOG_DBG("source is NULL");
@ -859,7 +935,7 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source,
}
qos = param->qos;
bis_count = 0U;
/* We update up to the first param->params_count subgroups */
for (size_t i = 0U; i < param->params_count; i++) {
const struct bt_bap_broadcast_source_subgroup_param *subgroup_param;
@ -885,6 +961,26 @@ int bt_bap_broadcast_source_reconfig(struct bt_bap_broadcast_source *source,
stream_param = &subgroup_param->params[j];
stream = stream_param->stream;
if (CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0) {
int err;
if (bis_count >= BROADCAST_STREAM_CNT) {
LOG_DBG("Stream count %d exceeded", bis_count);
return -ENOMEM;
}
codec_cfg = &source->codec_cfg[bis_count];
memcpy(codec_cfg, subgroup_param->codec_cfg,
sizeof(struct bt_audio_codec_cfg));
err = update_codec_cfg_data(codec_cfg, stream_param);
if (err != 0) {
LOG_DBG("codec config update failed [%zu]: %d", i, err);
return err;
}
bis_count++;
}
stream_idx = 0U;
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, subgroup_stream, _node) {

View file

@ -103,9 +103,13 @@ struct bt_bap_broadcast_source {
/* The codec specific configured data for each stream in the subgroup */
struct bt_audio_broadcast_stream_data stream_data[BROADCAST_STREAM_CNT];
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
/* The complete codec specific configured data for each stream in the subgroup.
* This contains both the subgroup and the BIS-specific data for each stream.
*/
struct bt_audio_codec_cfg codec_cfg[BROADCAST_STREAM_CNT];
/* The subgroups containing the streams used to create the broadcast source */
sys_slist_t subgroups;
};

View file

@ -44,7 +44,8 @@ static void bap_broadcast_source_test_suite_fixture_init(
{
const uint8_t bis_cfg_data[] = {
BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT),
BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_LEFT |
BT_AUDIO_LOCATION_FRONT_RIGHT)),
};
const size_t streams_per_subgroup = CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT /
CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT;
@ -228,6 +229,19 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send
for (size_t j = 0U; j < create_param->params[i].params_count; j++) {
struct bt_bap_stream *bap_stream = create_param->params[i].params[j].stream;
/* verify bap stream started cb stream parameter */
zassert_equal(mock_bap_stream_started_cb_fake.arg0_history[i], bap_stream);
struct bt_audio_codec_cfg *codec_cfg = bap_stream->codec_cfg;
enum bt_audio_location chan_allocation;
/* verify subgroup codec data */
zassert_equal(bt_audio_codec_cfg_get_freq(codec_cfg),
BT_AUDIO_CODEC_CFG_FREQ_16KHZ);
zassert_equal(bt_audio_codec_cfg_get_frame_dur(codec_cfg),
BT_AUDIO_CODEC_CFG_DURATION_10);
/* verify bis specific codec data */
bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation);
zassert_equal(chan_allocation,
BT_AUDIO_LOCATION_FRONT_LEFT | BT_AUDIO_LOCATION_FRONT_RIGHT);
/* Since BAP doesn't care about the `buf` we can just provide NULL */
err = bt_bap_stream_send(bap_stream, NULL, 0);
zassert_equal(0, err,
@ -1220,6 +1234,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_id_inval_stat
zassert_not_equal(0, err, "Did not fail with deleted broadcast source");
}
ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base_single_bis)
{
struct bt_bap_broadcast_source_param *create_param = fixture->param;
@ -1237,8 +1253,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base_single_b
0x04, /* meta length */
0x03, 0x02, 0x01, 0x00, /* meta */
0x01, /* bis index */
0x03, /* bis cc length */
0x02, 0x03, 0x03 /* bis cc length */
0x06, /* bis cc length */
0x05, 0x03, 0x03, 0x00, 0x00, 0x00 /* bis cc length */
};
NET_BUF_SIMPLE_DEFINE(base_buf, 64);
@ -1294,8 +1310,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base)
0x04, /* meta length */
0x03, 0x02, 0x01, 0x00, /* meta */
0x01, /* bis index */
0x03, /* bis cc length */
0x02, 0x03, 0x03, /* bis cc length */
0x06, /* bis cc length */
0x05, 0x03, 0x03, 0x00, 0x00, 0x00, /* bis cc length */
0x01, /* Subgroup 1: bis count */
0x06, 0x00, 0x00, 0x00, 0x00, /* LC3 codec_id*/
0x10, /* cc length */
@ -1304,8 +1320,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_get_base)
0x04, /* meta length */
0x03, 0x02, 0x01, 0x00, /* meta */
0x02, /* bis index */
0x03, /* bis cc length */
0x02, 0x03, 0x03 /* bis cc length */
0x06, /* bis cc length */
0x05, 0x03, 0x03, 0x00, 0x00, 0x00 /* bis cc length */
};
NET_BUF_SIMPLE_DEFINE(base_buf, 128);

View file

@ -11,6 +11,7 @@ add_library(uut STATIC
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_iso.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_stream.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/bap_broadcast_source.c
${ZEPHYR_BASE}/subsys/bluetooth/audio/codec.c
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
${ZEPHYR_BASE}/subsys/net/buf_simple.c
)

View file

@ -446,7 +446,7 @@ static void validate_stream_codec_cfg(const struct bt_bap_stream *stream)
*/
ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation, false);
if (ret == 0) {
if (chan_allocation != BT_AUDIO_LOCATION_FRONT_LEFT) {
if (chan_allocation != BT_AUDIO_LOCATION_FRONT_CENTER) {
FAIL("Unexpected channel allocation: 0x%08X", chan_allocation);
return;

View file

@ -27,6 +27,11 @@
#include "bstests.h"
#include "common.h"
#define SUPPORTED_CHAN_COUNTS BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1, 2)
#define SUPPORTED_MIN_OCTETS_PER_FRAME 30
#define SUPPORTED_MAX_OCTETS_PER_FRAME 155
#define SUPPORTED_MAX_FRAMES_PER_SDU 1
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
/* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that
* the controller is never idle
@ -50,9 +55,123 @@ static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2
static struct bt_bap_lc3_preset preset_16_1_1 = BT_BAP_LC3_BROADCAST_PRESET_16_1_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
static uint8_t bis_codec_data[] = {
BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_CENTER)),
};
static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(broadcast_source_streams));
static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(broadcast_source_streams));
static void validate_stream_codec_cfg(const struct bt_bap_stream *stream)
{
const struct bt_audio_codec_cfg *codec_cfg = stream->codec_cfg;
const struct bt_audio_codec_cfg *exp_codec_cfg = &preset_16_1_1.codec_cfg;
enum bt_audio_location chan_allocation;
uint8_t frames_blocks_per_sdu;
size_t min_sdu_size_required;
uint16_t octets_per_frame;
uint8_t chan_cnt;
int ret;
int exp_ret;
ret = bt_audio_codec_cfg_get_freq(codec_cfg);
exp_ret = bt_audio_codec_cfg_get_freq(exp_codec_cfg);
if (ret >= 0) {
const int freq = bt_audio_codec_cfg_freq_to_freq_hz(ret);
const int exp_freq = bt_audio_codec_cfg_freq_to_freq_hz(exp_ret);
if (freq != exp_freq) {
FAIL("Invalid frequency: %d Expected: %d\n", freq, exp_freq);
return;
}
} else {
FAIL("Could not get frequency: %d\n", ret);
return;
}
ret = bt_audio_codec_cfg_get_frame_dur(codec_cfg);
exp_ret = bt_audio_codec_cfg_get_frame_dur(exp_codec_cfg);
if (ret >= 0) {
const int frm_dur_us = bt_audio_codec_cfg_frame_dur_to_frame_dur_us(ret);
const int exp_frm_dur_us = bt_audio_codec_cfg_frame_dur_to_frame_dur_us(exp_ret);
if (frm_dur_us != exp_frm_dur_us) {
FAIL("Invalid frame duration: %d Exp: %d\n", frm_dur_us, exp_frm_dur_us);
return;
}
} else {
FAIL("Could not get frame duration: %d\n", ret);
return;
}
/* The broadcast source sets the channel allocation in the BIS to
* BT_AUDIO_LOCATION_FRONT_CENTER
*/
ret = bt_audio_codec_cfg_get_chan_allocation(codec_cfg, &chan_allocation);
if (ret == 0) {
if (chan_allocation != BT_AUDIO_LOCATION_FRONT_CENTER) {
FAIL("Unexpected channel allocation: 0x%08X", chan_allocation);
return;
}
chan_cnt = bt_audio_get_chan_count(chan_allocation);
} else {
FAIL("Could not get subgroup channel allocation: %d\n", ret);
return;
}
if (chan_cnt == 0 || (BIT(chan_cnt - 1) & SUPPORTED_CHAN_COUNTS) == 0) {
FAIL("Unsupported channel count: %u\n", chan_cnt);
return;
}
ret = bt_audio_codec_cfg_get_octets_per_frame(codec_cfg);
if (ret > 0) {
octets_per_frame = (uint16_t)ret;
} else {
FAIL("Could not get subgroup octets per frame: %d\n", ret);
return;
}
if (!IN_RANGE(octets_per_frame, SUPPORTED_MIN_OCTETS_PER_FRAME,
SUPPORTED_MAX_OCTETS_PER_FRAME)) {
FAIL("Unsupported octets per frame: %u\n", octets_per_frame);
return;
}
ret = bt_audio_codec_cfg_get_frame_blocks_per_sdu(codec_cfg, false);
if (ret > 0) {
frames_blocks_per_sdu = (uint8_t)ret;
} else {
printk("Could not get octets per frame: %d\n", ret);
/* Frame blocks per SDU is optional and is implicitly 1 */
frames_blocks_per_sdu = 1U;
}
/* An SDU can consist of X frame blocks, each with Y frames (one per channel) of size Z in
* them. The minimum SDU size required for this is X * Y * Z.
*/
min_sdu_size_required = chan_cnt * octets_per_frame * frames_blocks_per_sdu;
if (min_sdu_size_required > stream->qos->sdu) {
FAIL("With %zu channels and %u octets per frame and %u frames per block, SDUs "
"shall be at minimum %zu, but the stream has been configured for %u\n",
chan_cnt, octets_per_frame, frames_blocks_per_sdu, min_sdu_size_required,
stream->qos->sdu);
return;
}
}
static void started_cb(struct bt_bap_stream *stream)
{
struct bt_bap_ep_info info;
@ -90,6 +209,7 @@ static void started_cb(struct bt_bap_stream *stream)
}
printk("Stream %p started\n", stream);
validate_stream_codec_cfg(stream);
k_sem_give(&sem_started);
}
@ -146,10 +266,6 @@ static struct bt_bap_stream_ops stream_ops = {
static int setup_broadcast_source(struct bt_bap_broadcast_source **source)
{
uint8_t bis_codec_data[] = {
BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC,
BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_LEFT)),
};
struct bt_bap_broadcast_source_stream_param
stream_params[ARRAY_SIZE(broadcast_source_streams)];
struct bt_bap_broadcast_source_subgroup_param
@ -297,10 +413,6 @@ static int setup_extended_adv(struct bt_bap_broadcast_source *source, struct bt_
static void test_broadcast_source_reconfig(struct bt_bap_broadcast_source *source)
{
uint8_t bis_codec_data[] = {
BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_FREQ,
BT_BYTES_LIST_LE16(BT_AUDIO_CODEC_CFG_FREQ_16KHZ)),
};
struct bt_bap_broadcast_source_stream_param
stream_params[ARRAY_SIZE(broadcast_source_streams)];
struct bt_bap_broadcast_source_subgroup_param