Bluetooth: audio: Improve stream coupling for CIS as the unicast client

Allow  the streams to be paired when creating unicast group. This will
allow to reuse the same ISO for the paired streams.

Fixes: #51796
Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-01-04 14:32:57 +01:00 committed by Carles Cufí
parent 9400de333e
commit 36058480e7
5 changed files with 217 additions and 159 deletions

View file

@ -1946,24 +1946,25 @@ int bt_audio_stream_release(struct bt_audio_stream *stream);
int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
uint16_t seq_num, uint32_t ts);
struct bt_audio_unicast_group_stream_param {
/** Pointer to a stream object. */
struct bt_audio_stream *stream;
/** The QoS settings for the stream object. */
struct bt_codec_qos *qos;
};
/** @brief Parameter struct for the unicast group functions
*
* Parameter struct for the bt_audio_unicast_group_create() and
* bt_audio_unicast_group_add_streams() functions.
*/
struct bt_audio_unicast_group_stream_param {
/** Pointer to a stream object. */
struct bt_audio_stream *stream;
struct bt_audio_unicast_group_stream_pair_param {
/** Pointer to a receiving stream parameters. */
struct bt_audio_unicast_group_stream_param *rx_param;
/** The QoS settings for the @ref bt_audio_unicast_group_stream_param.stream. */
struct bt_codec_qos *qos;
/** @brief The direction of the @ref bt_audio_unicast_group_stream_param.stream
*
* If two streams are being used for the same ACL connection but in
* different directions, they may use the same CIS.
*/
enum bt_audio_dir dir;
/** Pointer to a transmiting stream parameters. */
struct bt_audio_unicast_group_stream_param *tx_param;
};
struct bt_audio_unicast_group_param {
@ -1971,7 +1972,7 @@ struct bt_audio_unicast_group_param {
size_t params_count;
/** Array of stream parameters */
struct bt_audio_unicast_group_stream_param *params;
struct bt_audio_unicast_group_stream_pair_param *params;
/** @brief Unicast Group packing mode.
*
@ -2020,7 +2021,7 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param *param,
* @return 0 in case of success or negative value in case of error.
*/
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
struct bt_audio_unicast_group_stream_param params[],
struct bt_audio_unicast_group_stream_pair_param params[],
size_t num_param);
/** @brief Delete audio unicast group.

View file

@ -33,8 +33,10 @@ NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT,
static struct bt_audio_stream streams[CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT +
CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT];
static size_t configured_sink_stream_count;
static size_t configured_stream_count;
static size_t configured_source_stream_count;
#define configured_stream_count (configured_sink_stream_count + \
configured_source_stream_count)
/* Select a codec configuration to apply that is mandatory to support by both client and server.
* Allows this sample application to work without logic to parse the codec capabilities of the
@ -329,25 +331,6 @@ static void audio_timer_timeout(struct k_work *work)
#endif
static enum bt_audio_dir stream_dir(const struct bt_audio_stream *stream)
{
for (size_t i = 0U; i < ARRAY_SIZE(sinks); i++) {
if (sinks[i].ep != NULL && stream->ep == sinks[i].ep) {
return BT_AUDIO_DIR_SINK;
}
}
for (size_t i = 0U; i < ARRAY_SIZE(sources); i++) {
if (sources[i] != NULL && stream->ep == sources[i]) {
return BT_AUDIO_DIR_SOURCE;
}
}
__ASSERT(false, "Invalid stream");
return 0;
}
static void print_hex(const uint8_t *ptr, size_t len)
{
while (len-- != 0) {
@ -880,7 +863,6 @@ static int configure_streams(void)
}
printk("Configured sink stream[%zu]\n", i);
configured_stream_count++;
configured_sink_stream_count++;
}
@ -900,7 +882,7 @@ static int configure_streams(void)
}
printk("Configured source stream[%zu]\n", i);
configured_stream_count++;
configured_source_stream_count++;
}
return 0;
@ -908,18 +890,34 @@ static int configure_streams(void)
static int create_group(void)
{
struct bt_audio_unicast_group_stream_param stream_params[ARRAY_SIZE(streams)];
const size_t params_count = MAX(configured_sink_stream_count,
configured_source_stream_count);
struct bt_audio_unicast_group_stream_pair_param pair_params[params_count];
struct bt_audio_unicast_group_stream_param stream_params[configured_stream_count];
struct bt_audio_unicast_group_param param;
int err;
for (size_t i = 0U; i < configured_stream_count; i++) {
stream_params[i].stream = &streams[i];
stream_params[i].qos = &codec_configuration.qos;
stream_params[i].dir = stream_dir(stream_params[i].stream);
}
param.params = stream_params;
param.params_count = configured_stream_count;
for (size_t i = 0U; i < params_count; i++) {
if (i < configured_sink_stream_count) {
pair_params[i].tx_param = &stream_params[i];
} else {
pair_params[i].tx_param = NULL;
}
if (i < configured_source_stream_count) {
pair_params[i].rx_param = &stream_params[i + configured_sink_stream_count];
} else {
pair_params[i].rx_param = NULL;
}
}
param.params = pair_params;
param.params_count = params_count;
param.packing = BT_ISO_PACKING_SEQUENTIAL;
err = bt_audio_unicast_group_create(&param, &unicast_group);
@ -969,7 +967,7 @@ static int enable_streams(void)
init_lc3();
}
for (size_t i = 0; i < configured_stream_count; i++) {
for (size_t i = 0U; i < configured_stream_count; i++) {
int err;
err = bt_audio_stream_enable(&streams[i],
@ -992,7 +990,7 @@ static int enable_streams(void)
static int start_streams(void)
{
for (size_t i = 0; i < configured_stream_count; i++) {
for (size_t i = 0U; i < configured_stream_count; i++) {
int err;
err = bt_audio_stream_start(&streams[i]);
@ -1025,7 +1023,7 @@ static void reset_data(void)
k_sem_reset(&sem_stream_started);
configured_sink_stream_count = 0;
configured_stream_count = 0;
configured_source_stream_count = 0;
}
void main(void)

View file

@ -399,6 +399,10 @@ static void bt_audio_codec_qos_to_cig_param(struct bt_iso_cig_param *cig_param,
cig_param->sca = BT_GAP_SCA_UNKNOWN;
}
/* FIXME: Remove `qos` parameter. Some of the QoS related CIG can be different
* between CIS'es. The implementation shall take the CIG parameters from
* unicast_group instead.
*/
static int bt_audio_cig_create(struct bt_audio_unicast_group *group,
const struct bt_codec_qos *qos)
{
@ -730,35 +734,6 @@ int bt_audio_stream_connect(struct bt_audio_stream *stream)
}
}
static struct bt_audio_iso *get_new_iso(struct bt_audio_unicast_group *group,
struct bt_conn *acl,
enum bt_audio_dir dir)
{
struct bt_audio_stream *stream;
/* Check if there's already an ISO that can be used for this direction */
SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) {
__ASSERT(stream->audio_iso != NULL, "stream->audio_iso is NULL");
/* Don't attempt to couple streams if the ACL is either NULL,
* or the connection points differ
*/
if (acl == NULL || stream->conn != acl) {
continue;
}
if (bt_audio_iso_get_stream(stream->audio_iso, dir) == NULL) {
LOG_DBG("Returning existing audio_iso for group %p",
group);
return bt_audio_iso_ref(stream->audio_iso);
}
}
LOG_DBG("Returning new audio_iso for group %p", group);
return bt_unicast_client_new_audio_iso();
}
static int unicast_group_add_iso(struct bt_audio_unicast_group *group,
struct bt_audio_iso *iso)
{
@ -850,33 +825,20 @@ static void unicast_client_codec_qos_to_iso_qos(struct bt_audio_iso *iso,
}
}
static int unicast_group_add_stream(struct bt_audio_unicast_group *group,
struct bt_audio_stream *stream,
struct bt_codec_qos *qos,
enum bt_audio_dir dir)
static void unicast_group_add_stream(struct bt_audio_unicast_group *group,
struct bt_audio_unicast_group_stream_param *param,
struct bt_audio_iso *iso,
enum bt_audio_dir dir)
{
struct bt_audio_iso *iso;
int err;
struct bt_audio_stream *stream = param->stream;
struct bt_codec_qos *qos = param->qos;
LOG_DBG("group %p stream %p qos %p iso %p dir %u",
group, stream, qos, iso, dir);
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(stream != NULL);
__ASSERT_NO_MSG(stream->ep == NULL ||
(stream->ep != NULL && stream->ep->iso == NULL));
LOG_DBG("group %p stream %p dir %s",
group, stream, bt_audio_dir_str(dir));
iso = get_new_iso(group, stream->conn, dir);
if (iso == NULL) {
return -ENOMEM;
}
err = unicast_group_add_iso(group, iso);
if (err < 0) {
bt_audio_iso_unref(iso);
return err;
}
stream->qos = qos;
stream->dir = dir;
stream->unicast_group = group;
@ -890,10 +852,39 @@ static int unicast_group_add_stream(struct bt_audio_unicast_group *group,
/* Store the Codec QoS in the audio_iso */
unicast_client_codec_qos_to_iso_qos(iso, qos, dir);
bt_audio_iso_unref(iso);
sys_slist_append(&group->streams, &stream->_node);
}
LOG_DBG("Added stream %p to group %p", stream, group);
static int unicast_group_add_stream_pair(struct bt_audio_unicast_group *group,
struct bt_audio_unicast_group_stream_pair_param *param)
{
struct bt_audio_iso *iso;
int err;
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(param != NULL);
__ASSERT_NO_MSG(param->rx_param != NULL || param->tx_param != NULL);
iso = bt_unicast_client_new_audio_iso();
if (iso == NULL) {
return -ENOMEM;
}
err = unicast_group_add_iso(group, iso);
if (err < 0) {
bt_audio_iso_unref(iso);
return err;
}
if (param->rx_param != NULL) {
unicast_group_add_stream(group, param->rx_param, iso, BT_AUDIO_DIR_SOURCE);
}
if (param->tx_param != NULL) {
unicast_group_add_stream(group, param->tx_param, iso, BT_AUDIO_DIR_SINK);
}
bt_audio_iso_unref(iso);
return 0;
}
@ -921,6 +912,24 @@ static void unicast_group_del_stream(struct bt_audio_unicast_group *group,
}
}
static void unicast_group_del_stream_pair(struct bt_audio_unicast_group *group,
struct bt_audio_unicast_group_stream_pair_param *param)
{
__ASSERT_NO_MSG(group != NULL);
__ASSERT_NO_MSG(param != NULL);
__ASSERT_NO_MSG(param->rx_param != NULL || param->tx_param != NULL);
if (param->rx_param != NULL) {
__ASSERT_NO_MSG(param->rx_param->stream);
unicast_group_del_stream(group, param->rx_param->stream);
}
if (param->tx_param != NULL) {
__ASSERT_NO_MSG(param->tx_param->stream);
unicast_group_del_stream(group, param->tx_param->stream);
}
}
static struct bt_audio_unicast_group *unicast_group_alloc(void)
{
struct bt_audio_unicast_group *group = NULL;
@ -965,6 +974,72 @@ static void unicast_group_free(struct bt_audio_unicast_group *group)
group->allocated = false;
}
static int stream_param_check(const struct bt_audio_unicast_group_stream_param *param)
{
CHECKIF(param->stream == NULL) {
LOG_ERR("param->stream is NULL");
return -EINVAL;
}
CHECKIF(param->qos == NULL) {
LOG_ERR("param->qos is NULL");
return -EINVAL;
}
if (param->stream != NULL && param->stream->group != NULL) {
LOG_WRN("stream %p already part of group %p",
param->stream, param->stream->group);
return -EALREADY;
}
CHECKIF(!bt_audio_valid_qos(param->qos)) {
LOG_ERR("Invalid QoS");
return -EINVAL;
}
return 0;
}
static int stream_pair_param_check(const struct bt_audio_unicast_group_stream_pair_param *param)
{
int err;
CHECKIF(param->rx_param == NULL && param->tx_param == NULL) {
LOG_DBG("Invalid stream parameters");
return -EINVAL;
}
if (param->rx_param != NULL) {
err = stream_param_check(param->rx_param);
if (err < 0) {
return err;
}
}
if (param->tx_param != NULL) {
err = stream_param_check(param->tx_param);
if (err < 0) {
return err;
}
}
return 0;
}
static int group_qos_common_set(const struct bt_codec_qos **group_qos,
const struct bt_audio_unicast_group_stream_pair_param *param)
{
if (param->rx_param != NULL && *group_qos == NULL) {
*group_qos = param->rx_param->qos;
}
if (param->tx_param != NULL && *group_qos == NULL) {
*group_qos = param->tx_param->qos;
}
return 0;
}
int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param *param,
struct bt_audio_unicast_group **out_unicast_group)
{
@ -990,34 +1065,6 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param *param,
return -EINVAL;
}
for (size_t i = 0U; i < param->params_count; i++) {
struct bt_audio_unicast_group_stream_param *stream_param = &param->params[i];
CHECKIF(stream_param->stream == NULL ||
stream_param->qos == NULL ||
(stream_param->dir != BT_AUDIO_DIR_SINK &&
stream_param->dir != BT_AUDIO_DIR_SOURCE)) {
LOG_DBG("Invalid params[%zu] values", i);
return -EINVAL;
}
if (stream_param->stream->group != NULL) {
LOG_DBG("params[%zu] stream (%p) already part of group %p",
i, stream_param->stream,
stream_param->stream->group);
return -EALREADY;
}
if (group_qos == NULL) {
group_qos = stream_param->qos;
}
CHECKIF(!bt_audio_valid_qos(stream_param->qos)) {
LOG_DBG("Invalid QoS");
return -EINVAL;
}
}
unicast_group = unicast_group_alloc();
if (unicast_group == NULL) {
LOG_DBG("Could not allocate any more unicast groups");
@ -1025,12 +1072,22 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param *param,
}
for (size_t i = 0U; i < param->params_count; i++) {
struct bt_audio_unicast_group_stream_param *stream_param = &param->params[i];
struct bt_audio_unicast_group_stream_pair_param *stream_param;
err = unicast_group_add_stream(unicast_group,
stream_param->stream,
stream_param->qos,
stream_param->dir);
stream_param = &param->params[i];
err = stream_pair_param_check(stream_param);
if (err < 0) {
return err;
}
err = group_qos_common_set(&group_qos, stream_param);
if (err < 0) {
return err;
}
err = unicast_group_add_stream_pair(unicast_group,
stream_param);
if (err < 0) {
LOG_DBG("unicast_group_add_stream failed: %d", err);
unicast_group_free(unicast_group);
@ -1053,7 +1110,7 @@ int bt_audio_unicast_group_create(struct bt_audio_unicast_group_param *param,
}
int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_group,
struct bt_audio_unicast_group_stream_param params[],
struct bt_audio_unicast_group_stream_pair_param params[],
size_t num_param)
{
const struct bt_codec_qos *group_qos = unicast_group->qos;
@ -1078,26 +1135,6 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
return -EINVAL;
}
for (size_t i = 0U; i < num_param; i++) {
CHECKIF(params[i].stream == NULL ||
params[i].qos == NULL ||
(params[i].dir != BT_AUDIO_DIR_SINK &&
params[i].dir != BT_AUDIO_DIR_SOURCE)) {
LOG_DBG("Invalid params[%zu] values", i);
return -EINVAL;
}
if (params[i].stream->group != NULL) {
LOG_DBG("params[%zu] stream (%p) already part of group %p", i,
params[i].stream, params[i].stream->group);
return -EALREADY;
}
if (group_qos == NULL) {
group_qos = params[i].qos;
}
}
total_stream_cnt = num_param;
SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, tmp_stream, _node) {
total_stream_cnt++;
@ -1120,10 +1157,22 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
}
for (num_added = 0U; num_added < num_param; num_added++) {
err = unicast_group_add_stream(unicast_group,
params[num_added].stream,
params[num_added].qos,
params[num_added].dir);
struct bt_audio_unicast_group_stream_pair_param *stream_param;
stream_param = &params[num_added];
err = stream_pair_param_check(stream_param);
if (err < 0) {
return err;
}
err = group_qos_common_set(&group_qos, stream_param);
if (err < 0) {
return err;
}
err = unicast_group_add_stream_pair(unicast_group,
stream_param);
if (err < 0) {
LOG_DBG("unicast_group_add_stream failed: %d", err);
goto fail;
@ -1141,7 +1190,7 @@ int bt_audio_unicast_group_add_streams(struct bt_audio_unicast_group *unicast_gr
fail:
/* Restore group by removing the newly added streams */
while (num_added--) {
unicast_group_del_stream(unicast_group, params[num_added].stream);
unicast_group_del_stream_pair(unicast_group, &params[num_added]);
}
return err;

View file

@ -1068,14 +1068,22 @@ static int cmd_qos(const struct shell *sh, size_t argc, char *argv[])
struct bt_audio_unicast_group_stream_param stream_param = {
.stream = default_stream,
.qos = &default_preset->preset.qos,
.dir = stream_dir(default_stream)
};
struct bt_audio_unicast_group_stream_pair_param pair_param;
struct bt_audio_unicast_group_param param = {
.packing = BT_ISO_PACKING_SEQUENTIAL,
.params = &stream_param,
.params = &pair_param,
.params_count = 1,
};
if (stream_dir(default_stream) == BT_AUDIO_DIR_SOURCE) {
pair_param.rx_param = &stream_param;
pair_param.tx_param = NULL;
} else {
pair_param.rx_param = NULL;
pair_param.tx_param = &stream_param;
}
err = bt_audio_unicast_group_create(&param, &default_unicast_group);
if (err != 0) {
shell_error(sh, "Unable to create default unicast group: %d", err);

View file

@ -339,16 +339,18 @@ static size_t release_streams(size_t stream_cnt)
static void create_unicast_group(struct bt_audio_unicast_group **unicast_group,
size_t stream_cnt)
{
struct bt_audio_unicast_group_stream_pair_param pair_params[ARRAY_SIZE(g_streams)];
struct bt_audio_unicast_group_stream_param stream_params[ARRAY_SIZE(g_streams)];
struct bt_audio_unicast_group_param param;
for (size_t i = 0U; i < stream_cnt; i++) {
stream_params[i].stream = &g_streams[i];
stream_params[i].qos = &preset_16_2_1.qos;
stream_params[i].dir = BT_AUDIO_DIR_SINK; /* we only configure sinks */
pair_params[i].rx_param = NULL;
pair_params[i].tx_param = &stream_params[i];
}
param.params = stream_params;
param.params = pair_params;
param.params_count = stream_cnt;
param.packing = BT_ISO_PACKING_SEQUENTIAL;