diff --git a/include/zephyr/bluetooth/audio/audio.h b/include/zephyr/bluetooth/audio/audio.h index 2ceef7f9a50..321d218fb32 100644 --- a/include/zephyr/bluetooth/audio/audio.h +++ b/include/zephyr/bluetooth/audio/audio.h @@ -2077,7 +2077,7 @@ int bt_audio_broadcast_source_create(struct bt_audio_broadcast_source_create_par /** @brief Reconfigure audio broadcast source. * * Reconfigure an audio broadcast source with a new codec and codec quality of - * service parameters. + * service parameters. This can only be done when the source is stopped. * * @param source Pointer to the broadcast source * @param codec Codec configuration. @@ -2089,6 +2089,22 @@ int bt_audio_broadcast_source_reconfig(struct bt_audio_broadcast_source *source, struct bt_codec *codec, struct bt_codec_qos *qos); +/** @brief Modify the metadata of an audio broadcast source. + * + * Modify the metadata an audio broadcast source. This can only be done when + * the source is started. To update the metadata in the stopped state, use + * bt_audio_broadcast_source_reconfig(). + * + * @param source Pointer to the broadcast source. + * @param meta Metadata entries. + * @param meta_count Number of metadata entries. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_audio_broadcast_source_update_metadata(struct bt_audio_broadcast_source *source, + const struct bt_codec_data meta[], + size_t meta_count); + /** @brief Start audio broadcast source. * * Start an audio broadcast source with one or more audio streams. diff --git a/subsys/bluetooth/audio/broadcast_source.c b/subsys/bluetooth/audio/broadcast_source.c index 06eb7ddf17f..f484870a810 100644 --- a/subsys/bluetooth/audio/broadcast_source.c +++ b/subsys/bluetooth/audio/broadcast_source.c @@ -765,6 +765,81 @@ int bt_audio_broadcast_source_reconfig(struct bt_audio_broadcast_source *source, return 0; } +static void broadcast_source_store_metadata(struct bt_codec *codec, + const struct bt_codec_data meta[], + size_t meta_count) +{ + size_t old_meta_count; + + old_meta_count = codec->meta_count; + + /* Update metadata */ + codec->meta_count = meta_count; + (void)memcpy(codec->meta, meta, meta_count * sizeof(*meta)); + if (old_meta_count > meta_count) { + size_t meta_count_diff = old_meta_count - meta_count; + + /* If we previously had more metadata entries we reset the + * data that was not overwritten by the new metadata + */ + (void)memset(&codec->meta[meta_count], + 0, meta_count_diff * sizeof(*meta)); + } +} + +int bt_audio_broadcast_source_update_metadata(struct bt_audio_broadcast_source *source, + const struct bt_codec_data meta[], + size_t meta_count) +{ + struct bt_audio_broadcast_subgroup *subgroup; + enum bt_audio_state broadcast_state; + + CHECKIF(source == NULL) { + LOG_DBG("source is NULL"); + + return -EINVAL; + } + + CHECKIF((meta == NULL && meta_count != 0) || + (meta != NULL && meta_count == 0)) { + LOG_DBG("Invalid metadata combination: %p %zu", + meta, meta_count); + + return -EINVAL; + } + + CHECKIF(meta_count > CONFIG_BT_CODEC_MAX_METADATA_COUNT) { + LOG_DBG("Invalid meta_count: %zu (max %d)", + meta_count, CONFIG_BT_CODEC_MAX_METADATA_COUNT); + + return -EINVAL; + } + + for (size_t i = 0; i < meta_count; i++) { + CHECKIF(meta[i].data.data_len > sizeof(meta[i].value)) { + LOG_DBG("Invalid meta[%zu] data_len %u", + i, meta[i].data.data_len); + + return -EINVAL; + } + } + broadcast_state = broadcast_source_get_state(source); + if (broadcast_source_get_state(source) != BT_AUDIO_EP_STATE_STREAMING) { + LOG_DBG("Broadcast source invalid state: %u", broadcast_state); + + return -EBADMSG; + } + + /* TODO: We should probably find a way to update the metadata + * for each subgroup individually + */ + SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) { + broadcast_source_store_metadata(subgroup->codec, meta, meta_count); + } + + return 0; +} + int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source, struct bt_le_ext_adv *adv) { diff --git a/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_sink_test.c b/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_sink_test.c index 12837f994e0..caba1fa69f7 100644 --- a/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_sink_test.c +++ b/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_sink_test.c @@ -15,6 +15,7 @@ extern enum bst_result_t bst_result; CREATE_FLAG(broadcaster_found); CREATE_FLAG(base_received); +CREATE_FLAG(flag_base_metadata_updated); CREATE_FLAG(pa_synced); CREATE_FLAG(flag_syncable); CREATE_FLAG(pa_sync_lost); @@ -30,6 +31,8 @@ static struct bt_audio_lc3_preset preset_16_2_1 = static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams)); static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams)); +static struct bt_codec_data metadata[CONFIG_BT_CODEC_MAX_METADATA_COUNT]; + /* Create a mask for the maximum BIS we can sync to using the number of streams * we have. We add an additional 1 since the bis indexes start from 1 and not * 0. @@ -76,6 +79,17 @@ static void base_recv_cb(struct bt_audio_broadcast_sink *sink, uint32_t base_bis_index_bitfield = 0U; if (TEST_FLAG(base_received)) { + + if (base->subgroup_count > 0 && + memcmp(metadata, base->subgroups[0].codec.meta, + sizeof(base->subgroups[0].codec.meta)) != 0) { + + (void)memcpy(metadata, base->subgroups[0].codec.meta, + sizeof(base->subgroups[0].codec.meta)); + + SET_FLAG(flag_base_metadata_updated); + } + return; } @@ -235,6 +249,10 @@ static void test_main(void) printk("Waiting for data\n"); WAIT_FOR_FLAG(flag_received); + /* Ensure that we also see the metadata update */ + printk("Waiting for metadata update\n"); + WAIT_FOR_FLAG(flag_base_metadata_updated) + /* The order of PA sync lost and BIG Sync lost is irrelevant * and depend on timeout parameters. We just wait for PA first, but * either way will work. @@ -293,6 +311,10 @@ static void test_sink_disconnect(void) printk("Waiting for data\n"); WAIT_FOR_FLAG(flag_received); + /* Ensure that we also see the metadata update */ + printk("Waiting for metadata update\n"); + WAIT_FOR_FLAG(flag_base_metadata_updated) + err = bt_audio_broadcast_sink_stop(g_sink); if (err != 0) { FAIL("Unable to stop sink: %d", err); diff --git a/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_source_test.c b/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_source_test.c index 5d6cef514b6..5bc5b9e7a2b 100644 --- a/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_source_test.c +++ b/tests/bluetooth/bsim_bt/bsim_test_audio/src/broadcast_source_test.c @@ -244,6 +244,8 @@ static int stop_extended_adv(struct bt_le_ext_adv *adv) static void test_main(void) { + struct bt_codec_data new_metadata[1] = + BT_CODEC_LC3_CONFIG_META(BT_AUDIO_CONTEXT_TYPE_ALERTS); struct bt_audio_broadcast_source *source; struct bt_le_ext_adv *adv; int err; @@ -299,6 +301,18 @@ static void test_main(void) /* Keeping running for a little while */ k_sleep(K_SECONDS(15)); + /* Update metadata while streaming */ + printk("Updating metadata\n"); + err = bt_audio_broadcast_source_update_metadata(source, new_metadata, + ARRAY_SIZE(new_metadata)); + if (err != 0) { + FAIL("Failed to update metadata broadcast source: %d", err); + return; + } + + /* Keeping running for a little while */ + k_sleep(K_SECONDS(5)); + printk("Stopping broadcast source\n"); SET_FLAG(flag_stopping); err = bt_audio_broadcast_source_stop(source);