samples: Bluetooth: Add stereo support for broadcast audio sink
The broadcast audio sink now supports stereo if CONFIG_TARGET_BROADCAST_CHANNEL=3 (LEFT | RIGHT). It parses the BASE to find a set of BIS (1 or 2) that contain the channel allocation from CONFIG_TARGET_BROADCAST_CHANNEL. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
121896c0cf
commit
4d2bc5f5e8
|
@ -35,6 +35,14 @@ config TARGET_BROADCAST_NAME
|
|||
Name of target broadcast device. If not empty string, sink device
|
||||
will only listen to the specified broadcast source. Not case sensitive.
|
||||
|
||||
config MAX_CODEC_FRAMES_PER_SDU
|
||||
int "The maximum number of codec frame per SDU supported"
|
||||
default 1
|
||||
range 1 255
|
||||
help
|
||||
Maximum number of codec frames per SDU supported by this device. Increasing this value
|
||||
allows support for a greater variaty of broadcasts, but also increases memory usage.
|
||||
|
||||
config ENABLE_LC3
|
||||
bool "Enable the LC3 codec"
|
||||
# By default let's enable it in the platforms we know are capable of supporting it
|
||||
|
@ -64,7 +72,7 @@ config USE_SPECIFIC_BROADCAST_CHANNEL
|
|||
|
||||
config TARGET_BROADCAST_CHANNEL
|
||||
int "Broadcast Channel Audio Location to sync to"
|
||||
range 0 2
|
||||
range 0 3
|
||||
default 1
|
||||
depends on USE_SPECIFIC_BROADCAST_CHANNEL
|
||||
help
|
||||
|
|
|
@ -112,7 +112,8 @@ static struct bt_le_ext_adv *ext_adv;
|
|||
|
||||
static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3(
|
||||
BT_AUDIO_CODEC_CAP_FREQ_16KHZ | BT_AUDIO_CODEC_CAP_FREQ_24KHZ,
|
||||
BT_AUDIO_CODEC_CAP_DURATION_10, BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
|
||||
BT_AUDIO_CODEC_CAP_DURATION_10, BT_AUDIO_CODEC_CAP_CHAN_COUNT_SUPPORT(1), 40u, 60u,
|
||||
CONFIG_MAX_CODEC_FRAMES_PER_SDU,
|
||||
(BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA));
|
||||
|
||||
/* Create a mask for the maximum BIS we can sync to using the number of streams
|
||||
|
@ -132,53 +133,18 @@ static int stop_adv(void);
|
|||
RING_BUF_DECLARE(usb_ring_buf, USB_RING_BUF_SIZE);
|
||||
NET_BUF_POOL_DEFINE(usb_tx_buf_pool, USB_ENQUEUE_COUNT, USB_STEREO_SAMPLE_SIZE, 0, net_buf_destroy);
|
||||
|
||||
static void mix_mono_to_stereo(int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO]);
|
||||
static void add_to_usb_ring_buf(const int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO]);
|
||||
#endif /* defined(CONFIG_USB_DEVICE_AUDIO) */
|
||||
|
||||
#if defined(CONFIG_LIBLC3)
|
||||
static K_SEM_DEFINE(lc3_decoder_sem, 0, 1);
|
||||
|
||||
static bool do_lc3_decode(struct broadcast_sink_stream *sink_stream,
|
||||
int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO]);
|
||||
static void do_lc3_decode(lc3_decoder_t decoder, const void *in_data, uint8_t octets_per_frame,
|
||||
int16_t out_data[LC3_MAX_NUM_SAMPLES_MONO]);
|
||||
static void lc3_decoder_thread(void *arg1, void *arg2, void *arg3);
|
||||
K_THREAD_DEFINE(decoder_tid, LC3_ENCODER_STACK_SIZE, lc3_decoder_thread,
|
||||
NULL, NULL, NULL, LC3_ENCODER_PRIORITY, 0, -1);
|
||||
|
||||
/* Consumer thread of the decoded stream data */
|
||||
static void lc3_decoder_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
while (true) {
|
||||
static int16_t lc3_audio_buf[LC3_MAX_NUM_SAMPLES_STEREO];
|
||||
|
||||
k_sem_take(&lc3_decoder_sem, K_FOREVER);
|
||||
#if defined(CONFIG_USB_DEVICE_AUDIO)
|
||||
|
||||
/* For now we only handle one BIS, so always only decode the first element
|
||||
* in streams.
|
||||
*/
|
||||
struct broadcast_sink_stream *stream_for_usb = &streams[0];
|
||||
|
||||
/* Not enough space to store data */
|
||||
if (ring_buf_space_get(&usb_ring_buf) < sizeof(lc3_audio_buf)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* lc3_audio_buf will be filled with the last decoded value, so e.g. if the stream
|
||||
* contains both left and right, the lc3_audio_buf will always contain right.
|
||||
*/
|
||||
if (do_lc3_decode(stream_for_usb, lc3_audio_buf)) {
|
||||
mix_mono_to_stereo(lc3_audio_buf);
|
||||
add_to_usb_ring_buf(lc3_audio_buf);
|
||||
}
|
||||
#else
|
||||
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
|
||||
(void)do_lc3_decode(&streams[i], lc3_audio_buf);
|
||||
}
|
||||
#endif /* #if defined(CONFIG_USB_DEVICE_AUDIO) */
|
||||
}
|
||||
}
|
||||
|
||||
static size_t get_chan_cnt(enum bt_audio_location chan_allocation)
|
||||
{
|
||||
size_t cnt = 0U;
|
||||
|
@ -195,61 +161,171 @@ static size_t get_chan_cnt(enum bt_audio_location chan_allocation)
|
|||
return cnt;
|
||||
}
|
||||
|
||||
/** Decode LC3 data on a stream and returns true if successful */
|
||||
static bool do_lc3_decode(struct broadcast_sink_stream *sink_stream,
|
||||
int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO])
|
||||
/* Consumer thread of the decoded stream data */
|
||||
static void lc3_decoder_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
const uint8_t frames_blocks_per_sdu = sink_stream->lc3_frames_blocks_per_sdu;
|
||||
const uint16_t octets_per_frame = sink_stream->lc3_octets_per_frame;
|
||||
uint16_t frames_per_block;
|
||||
struct net_buf *buf;
|
||||
while (true) {
|
||||
#if defined(CONFIG_USB_DEVICE_AUDIO)
|
||||
static int16_t right_frames[CONFIG_MAX_CODEC_FRAMES_PER_SDU]
|
||||
[LC3_MAX_NUM_SAMPLES_MONO];
|
||||
static int16_t left_frames[CONFIG_MAX_CODEC_FRAMES_PER_SDU]
|
||||
[LC3_MAX_NUM_SAMPLES_MONO];
|
||||
size_t right_frames_cnt = 0;
|
||||
size_t left_frames_cnt = 0;
|
||||
|
||||
k_mutex_lock(&sink_stream->lc3_decoder_mutex, K_FOREVER);
|
||||
memset(right_frames, 0, sizeof(right_frames));
|
||||
memset(left_frames, 0, sizeof(left_frames));
|
||||
#else
|
||||
static int16_t lc3_audio_buf[LC3_MAX_NUM_SAMPLES_MONO];
|
||||
#endif /* CONFIG_USB_DEVICE_AUDIO */
|
||||
|
||||
if (sink_stream->in_buf == NULL) {
|
||||
k_mutex_unlock(&sink_stream->lc3_decoder_mutex);
|
||||
k_sem_take(&lc3_decoder_sem, K_FOREVER);
|
||||
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
|
||||
struct broadcast_sink_stream *stream = &streams[i];
|
||||
const uint8_t frames_blocks_per_sdu = stream->lc3_frames_blocks_per_sdu;
|
||||
const uint16_t octets_per_frame = stream->lc3_octets_per_frame;
|
||||
uint16_t frames_per_block;
|
||||
struct net_buf *buf;
|
||||
|
||||
buf = net_buf_ref(sink_stream->in_buf);
|
||||
net_buf_unref(sink_stream->in_buf);
|
||||
sink_stream->in_buf = NULL;
|
||||
k_mutex_unlock(&sink_stream->lc3_decoder_mutex);
|
||||
k_mutex_lock(&stream->lc3_decoder_mutex, K_FOREVER);
|
||||
|
||||
frames_per_block = get_chan_cnt(sink_stream->chan_allocation);
|
||||
if (buf->len != (frames_per_block * octets_per_frame * frames_blocks_per_sdu)) {
|
||||
printk("Expected %u frame blocks with %u frames of size %u, but length is %u\n",
|
||||
frames_blocks_per_sdu, frames_per_block, octets_per_frame, buf->len);
|
||||
if (stream->in_buf == NULL) {
|
||||
k_mutex_unlock(&stream->lc3_decoder_mutex);
|
||||
|
||||
net_buf_unref(buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
buf = net_buf_ref(stream->in_buf);
|
||||
net_buf_unref(stream->in_buf);
|
||||
stream->in_buf = NULL;
|
||||
k_mutex_unlock(&stream->lc3_decoder_mutex);
|
||||
|
||||
for (uint8_t i = 0U; i < frames_blocks_per_sdu; i++) {
|
||||
for (uint16_t j = 0U; j < frames_per_block; j++) {
|
||||
const void *data = net_buf_pull_mem(buf, octets_per_frame);
|
||||
int err;
|
||||
|
||||
err = lc3_decode(sink_stream->lc3_decoder, data, octets_per_frame,
|
||||
LC3_PCM_FORMAT_S16, audio_buf, 1);
|
||||
|
||||
if (err == 1) {
|
||||
printk(" decoder performed PLC\n");
|
||||
} else if (err < 0) {
|
||||
printk(" decoder failed - wrong parameters? (err = %d)\n", err);
|
||||
frames_per_block = get_chan_cnt(stream->chan_allocation);
|
||||
if (buf->len !=
|
||||
(frames_per_block * octets_per_frame * frames_blocks_per_sdu)) {
|
||||
printk("Expected %u frame blocks with %u frames of size %u, but "
|
||||
"length is %u\n",
|
||||
frames_blocks_per_sdu, frames_per_block, octets_per_frame,
|
||||
buf->len);
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_DEVICE_AUDIO)
|
||||
const bool has_left =
|
||||
(stream->chan_allocation & BT_AUDIO_LOCATION_FRONT_LEFT) != 0;
|
||||
const bool has_right =
|
||||
(stream->chan_allocation & BT_AUDIO_LOCATION_FRONT_RIGHT) != 0;
|
||||
const bool is_mono =
|
||||
stream->chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
|
||||
/* Split the SDU into frames*/
|
||||
for (uint8_t i = 0U; i < frames_blocks_per_sdu; i++) {
|
||||
for (uint16_t j = 0U; j < frames_per_block; j++) {
|
||||
const bool is_left = j == 0 && has_left;
|
||||
const bool is_right =
|
||||
has_right && (j == 0 || (j == 1 && has_left));
|
||||
const void *data = net_buf_pull_mem(buf, octets_per_frame);
|
||||
int16_t *out_frame;
|
||||
|
||||
if (is_left) {
|
||||
out_frame = left_frames[left_frames_cnt++];
|
||||
} else if (is_right) {
|
||||
out_frame = right_frames[right_frames_cnt++];
|
||||
} else if (is_mono) {
|
||||
/* Use left as mono*/
|
||||
out_frame = left_frames[left_frames_cnt++];
|
||||
} else {
|
||||
/* unused channel */
|
||||
break;
|
||||
}
|
||||
|
||||
do_lc3_decode(stream->lc3_decoder, data, octets_per_frame,
|
||||
out_frame);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Dummy behavior: Decode and discard data */
|
||||
for (uint8_t i = 0U; i < frames_blocks_per_sdu; i++) {
|
||||
for (uint16_t j = 0U; j < frames_per_block; j++) {
|
||||
const void *data = net_buf_pull_mem(buf, octets_per_frame);
|
||||
|
||||
do_lc3_decode(stream->lc3_decoder, data, octets_per_frame,
|
||||
lc3_audio_buf);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USB_DEVICE_AUDIO */
|
||||
|
||||
net_buf_unref(buf);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USB_DEVICE_AUDIO)
|
||||
const bool is_left_only = right_frames_cnt == 0U;
|
||||
const bool is_right_only = left_frames_cnt == 0U;
|
||||
|
||||
if (!is_left_only && !is_right_only && left_frames_cnt != right_frames_cnt) {
|
||||
printk("Mismatch between number of left (%zu) and right (%zu) frames, "
|
||||
"discard SDU",
|
||||
left_frames_cnt, right_frames_cnt);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Send frames to USB - If we only have a single channel we mix it to stereo */
|
||||
for (size_t i = 0U; i < MAX(left_frames_cnt, right_frames_cnt); i++) {
|
||||
const bool is_single_channel = is_left_only || is_right_only;
|
||||
static int16_t stereo_frame[LC3_MAX_NUM_SAMPLES_STEREO];
|
||||
int16_t *right_frame = right_frames[i];
|
||||
int16_t *left_frame = left_frames[i];
|
||||
|
||||
/* Not enough space to store data */
|
||||
if (ring_buf_space_get(&usb_ring_buf) < sizeof(stereo_frame)) {
|
||||
break;
|
||||
}
|
||||
|
||||
memset(stereo_frame, 0, sizeof(stereo_frame));
|
||||
|
||||
/* Generate the stereo frame
|
||||
*
|
||||
* If we only have single channel then that is always stored in the
|
||||
* left_frame, and we mix that to stereo
|
||||
*/
|
||||
for (int j = 0; j < LC3_MAX_NUM_SAMPLES_MONO; j++) {
|
||||
if (is_single_channel) {
|
||||
/* Mix to stereo */
|
||||
if (is_left_only) {
|
||||
stereo_frame[j * 2] = left_frame[j];
|
||||
stereo_frame[j * 2 + 1] = left_frame[j];
|
||||
} else if (is_right_only) {
|
||||
stereo_frame[j * 2] = right_frame[j];
|
||||
stereo_frame[j * 2 + 1] = right_frame[j];
|
||||
}
|
||||
} else {
|
||||
stereo_frame[j * 2] = left_frame[j];
|
||||
stereo_frame[j * 2 + 1] = right_frame[j];
|
||||
}
|
||||
}
|
||||
|
||||
add_to_usb_ring_buf(stereo_frame);
|
||||
}
|
||||
#endif /* CONFIG_USB_DEVICE_AUDIO */
|
||||
}
|
||||
}
|
||||
|
||||
net_buf_unref(buf);
|
||||
/** Decode LC3 data on a stream and returns true if successful */
|
||||
static void do_lc3_decode(lc3_decoder_t decoder, const void *in_data, uint8_t octets_per_frame,
|
||||
int16_t out_data[LC3_MAX_NUM_SAMPLES_MONO])
|
||||
{
|
||||
int err;
|
||||
|
||||
return true;
|
||||
err = lc3_decode(decoder, in_data, octets_per_frame, LC3_PCM_FORMAT_S16, out_data, 1);
|
||||
if (err == 1) {
|
||||
printk(" decoder performed PLC\n");
|
||||
} else if (err < 0) {
|
||||
printk(" decoder failed - wrong parameters? (err = %d)\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static int lc3_enable(struct broadcast_sink_stream *sink_stream)
|
||||
|
@ -342,21 +418,6 @@ static int lc3_enable(struct broadcast_sink_stream *sink_stream)
|
|||
#endif /* defined(CONFIG_LIBLC3) */
|
||||
|
||||
#if defined(CONFIG_USB_DEVICE_AUDIO)
|
||||
/* Duplicate the audio from one channel and put it in both channels */
|
||||
static void mix_mono_to_stereo(int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO])
|
||||
{
|
||||
/* Interleave the channel sample inline
|
||||
* Take the first LC3_MAX_NUM_SAMPLES_MONO samples from audio_buf and mix it to
|
||||
* interleaved stereo, so that 012345 becomes 001122334455
|
||||
*/
|
||||
for (int i = LC3_MAX_NUM_SAMPLES_MONO - 1; i >= 0; i--) {
|
||||
const int16_t sample = audio_buf[i];
|
||||
|
||||
audio_buf[i * 2] = sample;
|
||||
audio_buf[i * 2 + 1] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move the LC3 data to the USB ring buffer */
|
||||
static void add_to_usb_ring_buf(const int16_t audio_buf[LC3_MAX_NUM_SAMPLES_STEREO])
|
||||
{
|
||||
|
@ -508,67 +569,221 @@ static struct bt_bap_stream_ops stream_ops = {
|
|||
};
|
||||
|
||||
#if defined(CONFIG_TARGET_BROADCAST_CHANNEL)
|
||||
struct find_valid_bis_data {
|
||||
struct {
|
||||
uint8_t index;
|
||||
enum bt_audio_location chan_allocation;
|
||||
} bis[BT_ISO_BIS_INDEX_MAX];
|
||||
|
||||
uint8_t cnt;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is called for each BIS in a subgroup
|
||||
*
|
||||
* It returns `false` if the current BIS contains all of the channels we are looking for,
|
||||
* or if it does not contain any and we are looking for BT_AUDIO_LOCATION_MONO_AUDIO. This stops
|
||||
* the iteration of the remaining BIS in the subgroup.
|
||||
*
|
||||
* It returns `true` if the BIS either contains none or some of the channels we are looking for.
|
||||
* If it contains some, then that is being stored in the user_data, so that the calling function
|
||||
* can check if a combination of the BIS satisfy the channel allocations we want.
|
||||
*/
|
||||
static bool find_valid_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
|
||||
void *user_data)
|
||||
{
|
||||
int err;
|
||||
struct find_valid_bis_data *data = user_data;
|
||||
struct bt_audio_codec_cfg codec_cfg = {0};
|
||||
enum bt_audio_location chan_allocation;
|
||||
uint8_t *bis_index = user_data;
|
||||
int err;
|
||||
|
||||
err = bt_bap_base_subgroup_bis_codec_to_codec_cfg(bis, &codec_cfg);
|
||||
if (err != 0) {
|
||||
printk("Could not find codec configuration (err=%d)\n", err);
|
||||
printk("Could not get codec configuration for BIS: %d\n", err);
|
||||
return true;
|
||||
}
|
||||
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &chan_allocation);
|
||||
if (err != 0) {
|
||||
printk("Could not find channel allocation (err=%d)\n", err);
|
||||
if (err == -ENODATA && strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) {
|
||||
printk("Could not find channel allocation for BIS: %d\n", err);
|
||||
|
||||
/* Absence of channel allocation is implicitly mono as per the BAP spec */
|
||||
if (CONFIG_TARGET_BROADCAST_CHANNEL == BT_AUDIO_LOCATION_MONO_AUDIO) {
|
||||
data->bis[0].index = bis->index;
|
||||
data->bis[0].chan_allocation = chan_allocation;
|
||||
data->cnt = 1;
|
||||
|
||||
return false;
|
||||
} else if (err == -ENODATA && strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) {
|
||||
/* Accept no channel allocation data available
|
||||
* if TARGET_BROADCAST_NAME defined. Use current index.
|
||||
*/
|
||||
*bis_index = bis->index;
|
||||
data->bis[0].index = bis->index;
|
||||
data->bis[0].chan_allocation = chan_allocation;
|
||||
data->cnt = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
/* Found single BIS with all channels we want - keep as only and stop
|
||||
* parsing
|
||||
*/
|
||||
data->bis[0].index = bis->index;
|
||||
data->bis[0].chan_allocation = chan_allocation;
|
||||
data->cnt = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((CONFIG_TARGET_BROADCAST_CHANNEL == BT_AUDIO_LOCATION_MONO_AUDIO &&
|
||||
chan_allocation == BT_AUDIO_LOCATION_MONO_AUDIO) ||
|
||||
chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
*bis_index = bis->index;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
} else if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) != 0) {
|
||||
/* BIS contains part of what we are looking for - Store and see if there are
|
||||
* other BIS that may fill the gaps
|
||||
*/
|
||||
data->bis[data->cnt].index = bis->index;
|
||||
data->bis[data->cnt].chan_allocation = chan_allocation;
|
||||
data->cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function searches all the BIS in a subgroup for a set of BIS indexes that satisfy
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL
|
||||
*
|
||||
* Returns `true` if the right channels were found, otherwise `false`.
|
||||
*/
|
||||
static bool find_valid_bis_in_subgroup_bis(const struct bt_bap_base_subgroup *subgroup,
|
||||
uint32_t *bis_indexes)
|
||||
{
|
||||
struct find_valid_bis_data data = {0};
|
||||
int err;
|
||||
|
||||
err = bt_bap_base_subgroup_foreach_bis(subgroup, find_valid_bis_cb, &data);
|
||||
if (err == -ECANCELED) {
|
||||
/* We found what we are looking for in a single BIS */
|
||||
|
||||
*bis_indexes = BIT(data.bis[0].index);
|
||||
|
||||
return true;
|
||||
} else if (err == 0) {
|
||||
/* We are finished parsing all BIS - Try to find a combination that satisfy our
|
||||
* channel allocation. For simplicity this is using a greedy approach, rather than
|
||||
* an optimal one.
|
||||
*/
|
||||
enum bt_audio_location chan_allocation = BT_AUDIO_LOCATION_MONO_AUDIO;
|
||||
*bis_indexes = 0;
|
||||
|
||||
for (uint8_t i = 0U; i < data.cnt; i++) {
|
||||
chan_allocation |= data.bis[i].chan_allocation;
|
||||
*bis_indexes |= BIT(data.bis[i].index);
|
||||
|
||||
if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Some error occurred or we did not find expected channel allocation */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each subgroup in the BASE. Will populate the 32-bit bitfield of BIS indexes if the
|
||||
* subgroup contains it.
|
||||
*
|
||||
* The channel allocation may
|
||||
* - Not exist at all, implicitly meaning BT_AUDIO_LOCATION_MONO_AUDIO
|
||||
* - Exist only in the subgroup codec configuration
|
||||
* - Exist only in the BIS codec configuration
|
||||
* - Exist in both the subgroup and BIS codec configuration, in which case, the BIS codec
|
||||
* configuration overwrites the subgroup values
|
||||
*
|
||||
* This function returns `true` if the subgroup does not support the channels in
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL which makes it iterate over the next subgroup, and returns
|
||||
* `false` if this subgroup satisfies our CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
static bool find_valid_bis_in_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
|
||||
void *user_data)
|
||||
{
|
||||
return bt_bap_base_subgroup_foreach_bis(subgroup, find_valid_bis_cb, user_data)
|
||||
== -ECANCELED ? false : true;
|
||||
enum bt_audio_location chan_allocation;
|
||||
struct bt_audio_codec_cfg codec_cfg;
|
||||
uint32_t *bis_indexes = user_data;
|
||||
int err;
|
||||
|
||||
/* We only want indexes from a single subgroup, so reset between each of them*/
|
||||
*bis_indexes = 0U;
|
||||
|
||||
err = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, &codec_cfg);
|
||||
if (err != 0) {
|
||||
printk("Could not get codec configuration: %d\n", err);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
err = bt_audio_codec_cfg_get_chan_allocation(&codec_cfg, &chan_allocation);
|
||||
if (err != 0) {
|
||||
printk("Could not find subgroup channel allocation: %d - Looking in the BISes\n",
|
||||
err);
|
||||
|
||||
/* Find chan alloc in BIS */
|
||||
if (find_valid_bis_in_subgroup_bis(subgroup, bis_indexes)) {
|
||||
/* Found BISes with correct channel allocation */
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* If the subgroup contains a single channel, then we just grab the first BIS index
|
||||
*/
|
||||
if (get_chan_cnt(chan_allocation) == 1 &&
|
||||
chan_allocation == CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
uint32_t subgroup_bis_indexes;
|
||||
|
||||
/* Set bis_indexes to the first bit set */
|
||||
err = bt_bap_base_subgroup_get_bis_indexes(subgroup, &subgroup_bis_indexes);
|
||||
if (err != 0) {
|
||||
/* Should never happen as that would indicate an invalid
|
||||
* subgroup If it does, we just parse the next subgroup
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We found the BIS index we want, stop parsing*/
|
||||
*bis_indexes = BIT(find_lsb_set(subgroup_bis_indexes) - 1);
|
||||
|
||||
return false;
|
||||
} else if ((chan_allocation & CONFIG_TARGET_BROADCAST_CHANNEL) ==
|
||||
CONFIG_TARGET_BROADCAST_CHANNEL) {
|
||||
/* The subgroup contains all channels we are looking for/
|
||||
* We continue searching each BIS to get the minimal amount of BIS that
|
||||
* satisfy CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
|
||||
if (find_valid_bis_in_subgroup_bis(subgroup, bis_indexes)) {
|
||||
/* Found BISes with correct channel allocation */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int base_get_first_valid_bis(const struct bt_bap_base *base, uint32_t *bis_index)
|
||||
/**
|
||||
* This function gets a 32-bit bitfield of BIS indexes that cover the channel allocation values in
|
||||
* CONFIG_TARGET_BROADCAST_CHANNEL.
|
||||
*/
|
||||
static int base_get_valid_bis_indexes(const struct bt_bap_base *base, uint32_t *bis_indexes)
|
||||
{
|
||||
int err;
|
||||
uint8_t valid_bis_index = 0U;
|
||||
|
||||
err = bt_bap_base_foreach_subgroup(base, find_valid_bis_in_subgroup_cb, &valid_bis_index);
|
||||
err = bt_bap_base_foreach_subgroup(base, find_valid_bis_in_subgroup_cb, bis_indexes);
|
||||
if (err != -ECANCELED) {
|
||||
printk("Failed to parse subgroups: %d\n", err);
|
||||
return err != 0 ? err : -ENOENT;
|
||||
}
|
||||
|
||||
*bis_index = 0;
|
||||
*bis_index |= ((uint8_t)1 << valid_bis_index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_TARGET_BROADCAST_CHANNEL */
|
||||
|
@ -587,7 +802,7 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap
|
|||
bt_bap_base_get_subgroup_count(base), sink);
|
||||
|
||||
#if defined(CONFIG_TARGET_BROADCAST_CHANNEL)
|
||||
err = base_get_first_valid_bis(base, &base_bis_index_bitfield);
|
||||
err = base_get_valid_bis_indexes(base, &base_bis_index_bitfield);
|
||||
if (err != 0) {
|
||||
printk("Failed to find a valid BIS\n");
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue