bluez: Configure the BlueZ broadcast source

This is the first draft where the configuration is done based on BIS information loaded from the config file.
This commit is contained in:
Silviu Florian Barbulescu 2024-04-22 09:35:49 +03:00 committed by Wim Taymans
parent db365ac855
commit 2bff4a9337
3 changed files with 430 additions and 0 deletions

View file

@ -103,12 +103,20 @@ static const struct {
{ BAP_CHANNEL_RS, SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */
};
struct bap_qos_preset {
char *name;
struct bap_qos qos;
};
#define BAP_QOS(rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \
((struct bap_qos){ .rate = (rate_), .frame_duration = (duration_), .framing = (framing_), \
.framelen = (framelen_), .retransmission = (rtn_), .latency = (latency_), \
.delay = (delay_), .priority = (priority_) })
#define BAP_PRESET_QOS(name_, rate_, duration_, framing_, framelen_, rtn_, latency_, delay_, priority_) \
((struct bap_qos_preset){ .name = name_, .qos = BAP_QOS(rate_, duration_, framing_, framelen_, \
rtn_, latency_, delay_, priority_)})
static const struct bap_qos bap_qos_configs[] = {
/* Priority: low-latency > high-reliability, 7.5ms > 10ms,
* bigger frequency and sdu better */
@ -150,6 +158,47 @@ static const struct bap_qos bap_qos_configs[] = {
BAP_QOS(LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 7), /* 48_6_2 */
};
static const struct bap_qos_preset bap_bcast_qos_configs[] = {
/* Priority: low-latency > high-reliability, 7.5ms > 10ms,
* bigger frequency and sdu better */
/* BAP v1.0.1 Table 6.4; low-latency */
BAP_PRESET_QOS("8_1_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 2, 8, 40000, 30), /* 8_1_1 */
BAP_PRESET_QOS("8_2_1", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 2, 10, 40000, 20), /* 8_2_1 */
BAP_PRESET_QOS("16_1_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 2, 8, 40000, 31), /* 16_1_1 */
BAP_PRESET_QOS("16_2_1", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 2, 10, 40000, 21), /* 16_2_1 (mandatory) */
BAP_PRESET_QOS("24_1_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 2, 8, 40000, 32), /* 24_1_1 */
BAP_PRESET_QOS("24_2_1", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 2, 10, 40000, 22), /* 24_2_1 */
BAP_PRESET_QOS("32_1_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 2, 8, 40000, 33), /* 32_1_1 */
BAP_PRESET_QOS("32_2_1", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 2, 10, 40000, 23), /* 32_2_1 */
BAP_PRESET_QOS("441_1_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 24, 40000, 34), /* 441_1_1 */
BAP_PRESET_QOS("441_2_1", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 31, 40000, 24), /* 441_2_1 */
BAP_PRESET_QOS("48_1_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 15, 40000, 35), /* 48_1_1 */
BAP_PRESET_QOS("48_2_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 20, 40000, 25), /* 48_2_1 */
BAP_PRESET_QOS("48_3_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 15, 40000, 36), /* 48_3_1 */
BAP_PRESET_QOS("48_4_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 20, 40000, 26), /* 48_4_1 */
BAP_PRESET_QOS("48_5_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 15, 40000, 37), /* 48_5_1 */
BAP_PRESET_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27), /* 48_6_1 */
/* BAP v1.0.1 Table 6.4; high-reliability */
BAP_PRESET_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10), /* 8_1_2 */
BAP_PRESET_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0), /* 8_1_2 */
BAP_PRESET_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11), /* 16_1_2 */
BAP_PRESET_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1), /* 16_2_2 */
BAP_PRESET_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12), /* 24_1_2 */
BAP_PRESET_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2), /* 24_2_2 */
BAP_PRESET_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13), /* 32_1_2 */
BAP_PRESET_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3), /* 32_2_2 */
BAP_PRESET_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14), /* 441_1_2 */
BAP_PRESET_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4), /* 441_2_2 */
BAP_PRESET_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15), /* 48_1_2 */
BAP_PRESET_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5), /* 48_2_2 */
BAP_PRESET_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16), /* 48_3_2 */
BAP_PRESET_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6), /* 48_4_2 */
BAP_PRESET_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17), /* 48_5_2 */
BAP_PRESET_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7), /* 48_6_2 */
};
static unsigned int get_rate_mask(uint8_t rate) {
switch (rate) {
case LC3_CONFIG_FREQ_8KHZ: return LC3_FREQ_8KHZ;
@ -1108,6 +1157,67 @@ static void codec_set_log(struct spa_log *global_log)
spa_log_topic_init(log, &codec_plugin_log_topic);
}
static int codec_get_bis_config(const struct media_codec *codec, uint8_t *caps,
uint8_t *caps_size, char *preset, int channel_allocation,
struct bap_codec_qos *qos)
{
int index = 0x0;
if (!codec)
return -ENOTSUP;
uint8_t *data = caps;
*caps_size = 0;
SPA_FOR_EACH_ELEMENT_VAR(bap_bcast_qos_configs, c) {
if (strcmp(c->name, preset) == 0) {
break;
}
index++;
}
switch (bap_bcast_qos_configs[index].qos.rate) {
case LC3_CONFIG_FREQ_48KHZ:
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_48KHZ);
break;
case LC3_CONFIG_FREQ_32KHZ:
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_32KHZ);
break;
case LC3_CONFIG_FREQ_24KHZ:
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_24KHZ);
break;
case LC3_CONFIG_FREQ_16KHZ:
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_16KHZ);
break;
case LC3_CONFIG_FREQ_8KHZ:
data += write_ltv_uint8(data, LC3_TYPE_FREQ, LC3_CONFIG_FREQ_8KHZ);
break;
default:
return -EINVAL;
}
*caps_size = *caps_size + 3;
data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(bap_bcast_qos_configs[index].qos.framelen));
*caps_size = *caps_size + 4;
data += write_ltv_uint8(data, LC3_TYPE_DUR, bap_bcast_qos_configs[index].qos.frame_duration);
*caps_size = *caps_size + 3;
data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(channel_allocation));
*caps_size = *caps_size + 6;
if(bap_bcast_qos_configs[index].qos.framing)
qos->framing = 1;
else
qos->framing = 0;
qos->sdu = bap_bcast_qos_configs[index].qos.framelen * get_channel_count(channel_allocation);
qos->retransmission = bap_bcast_qos_configs[index].qos.retransmission;
qos->latency = bap_bcast_qos_configs[index].qos.latency;
qos->delay = bap_bcast_qos_configs[index].qos.delay;
qos->phy = 2;
qos->interval = 10000;
return true;
}
const struct media_codec bap_codec_lc3 = {
.id = SPA_BLUETOOTH_AUDIO_CODEC_LC3,
.name = "lc3",
@ -1132,6 +1242,7 @@ const struct media_codec bap_codec_lc3 = {
.reduce_bitpool = codec_reduce_bitpool,
.increase_bitpool = codec_increase_bitpool,
.set_log = codec_set_log,
.get_bis_config = codec_get_bis_config
};
MEDIA_CODEC_EXPORT_DEF(

View file

@ -120,6 +120,8 @@ struct spa_bt_monitor {
unsigned int connection_info_supported:1;
unsigned int dummy_avrcp_player:1;
struct spa_list bcast_source_config_list;
struct spa_bt_quirks *quirks;
#define MAX_SETTINGS 128
@ -146,6 +148,33 @@ struct spa_bt_remote_endpoint {
bool acceptor;
};
#define METADATA_MAX_LEN 255
#define CC_MAX_LEN 255
struct spa_bt_metadata {
struct spa_list link;
int length;
int type;
uint8_t value[METADATA_MAX_LEN];
};
struct spa_bt_bis {
struct spa_list link;
char qos_preset[255];
int channel_allocation;
struct spa_list metadata_list;
};
#define BROADCAST_CODE_LEN 16
struct spa_bt_big {
struct spa_list link;
int broadcast_code[BROADCAST_CODE_LEN];
int presentation_delay;
struct spa_list bis_list;
int big_id;
};
/*
* Codec switching tries various codec/remote endpoint combinations
* in order, until an acceptable one is found. This triggers BlueZ
@ -1380,6 +1409,32 @@ static void adapter_free(struct spa_bt_adapter *adapter)
free(adapter);
}
static void metadata_entry_free(struct spa_bt_metadata *metadata_entry)
{
spa_list_remove(&metadata_entry->link);
free(metadata_entry);
}
static void bis_entry_free(struct spa_bt_bis *bis_entry)
{
struct spa_bt_metadata *m;
spa_list_consume(m, &bis_entry->metadata_list, link)
metadata_entry_free(m);
spa_list_remove(&bis_entry->link);
free(bis_entry);
}
static void big_entry_free(struct spa_bt_big *big_entry)
{
struct spa_bt_bis *b;
spa_list_consume(b, &big_entry->bis_list, link)
bis_entry_free(b);
spa_list_remove(&big_entry->link);
free(big_entry);
}
static uint32_t adapter_connectable_profiles(struct spa_bt_adapter *adapter)
{
struct spa_bt_monitor *monitor = adapter->monitor;
@ -5194,6 +5249,117 @@ static void reselect_backend(struct spa_bt_monitor *monitor, bool silent)
backend ? backend->name : "none");
}
static void configure_bis(struct spa_bt_monitor *monitor,
const struct media_codec *codec,
DBusConnection *conn,
const char *object_path,
const char *interface_name,
struct spa_bt_big *big,
struct spa_bt_bis *bis,
const char *local_endpoint)
{
DBusMessageIter iter, entry, variant, qos_dict;
DBusMessage *msg;
DBusMessageIter dict;
const char *entry_key = "QoS";
int bis_id = 0xFF;
uint8_t caps [CC_MAX_LEN];
uint8_t metadata [METADATA_MAX_LEN];
uint8_t caps_size, metadata_size = 0;
struct bap_codec_qos qos;
int presentation_delay;
struct spa_bt_metadata *metadata_entry;
int mse = 0;
int options = 0;
int skip = 0;
int sync_cte_type = 0;
int sync_factor = 1;
int sync_timeout = 2000;
int timeout = 2000;
/* Configure each BIS from a BIG */
spa_list_for_each(metadata_entry, &bis->metadata_list, link) {
metadata[metadata_size] = (uint8_t)metadata_entry->length;
metadata_size++;
metadata[metadata_size] = (uint8_t)metadata_entry->type;
metadata_size++;
memcpy(&metadata[metadata_size], metadata_entry->value, metadata_entry->length - 1);
metadata_size += metadata_entry->length - 1;
}
codec->get_bis_config(codec, caps, &caps_size, bis->qos_preset, bis->channel_allocation, &qos);
msg = dbus_message_new_method_call(BLUEZ_SERVICE,
object_path,
interface_name,
"SetConfiguration");
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &local_endpoint);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
append_basic_array_variant_dict_entry(&dict, "Capabilities", "ay", "y", DBUS_TYPE_BYTE, caps, caps_size);
append_basic_array_variant_dict_entry(&dict, "Metadata", "ay", "y", DBUS_TYPE_BYTE, metadata, metadata_size);
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &entry_key);
dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}", &variant);
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&qos_dict);
append_basic_variant_dict_entry(&qos_dict, "BIG", DBUS_TYPE_BYTE, "y", &big->big_id);
append_basic_variant_dict_entry(&qos_dict, "BIS", DBUS_TYPE_BYTE, "y", &bis_id);
append_basic_variant_dict_entry(&qos_dict, "SyncFactor", DBUS_TYPE_BYTE, "y", &sync_factor);
append_basic_variant_dict_entry(&qos_dict, "Options", DBUS_TYPE_BYTE, "y", &options);
append_basic_variant_dict_entry(&qos_dict, "Skip", DBUS_TYPE_UINT16, "q", &skip);
append_basic_variant_dict_entry(&qos_dict, "SyncTimeout", DBUS_TYPE_UINT16, "q", &sync_timeout);
append_basic_variant_dict_entry(&qos_dict, "SyncCteType", DBUS_TYPE_BYTE, "y", &sync_cte_type);
append_basic_variant_dict_entry(&qos_dict, "MSE", DBUS_TYPE_BYTE, "y", &mse);
append_basic_variant_dict_entry(&qos_dict, "Timeout", DBUS_TYPE_UINT16, "q", &timeout);
append_basic_array_variant_dict_entry(&qos_dict, "BCode", "ay", "y", DBUS_TYPE_BYTE, big->broadcast_code, BROADCAST_CODE_LEN);
append_basic_variant_dict_entry(&qos_dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval);
append_basic_variant_dict_entry(&qos_dict, "Framing", DBUS_TYPE_BYTE, "y", &qos.framing);
append_basic_variant_dict_entry(&qos_dict, "PHY", DBUS_TYPE_BYTE, "y", &qos.phy);
append_basic_variant_dict_entry(&qos_dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu);
append_basic_variant_dict_entry(&qos_dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission);
append_basic_variant_dict_entry(&qos_dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency);
append_basic_variant_dict_entry(&qos_dict, "PresentationDelay", DBUS_TYPE_UINT32, "u", &presentation_delay);
dbus_message_iter_close_container(&variant, &qos_dict);
dbus_message_iter_close_container(&entry, &variant);
dbus_message_iter_close_container(&dict, &entry);
dbus_message_iter_close_container(&iter, &dict);
dbus_message_set_no_reply(msg, TRUE);
if (!dbus_connection_send(conn, msg, NULL)) {
spa_log_error(monitor->log, "sending SetConfiguration failed");
}
dbus_message_unref(msg);
}
static void configure_bcast_source(struct spa_bt_monitor *monitor,
const struct media_codec *codec,
DBusConnection *conn,
const char *object_path,
const char *interface_name,
const char *local_endpoint)
{
struct spa_bt_big *big;
struct spa_bt_bis *bis;
/* Configure each BIS from a BIG */
spa_list_for_each(big, &monitor->bcast_source_config_list, link) {
spa_list_for_each(bis, &big->bis_list, link) {
configure_bis(monitor, codec, conn, object_path, interface_name,
big, bis, local_endpoint);
}
}
}
static void interface_added(struct spa_bt_monitor *monitor,
DBusConnection *conn,
const char *object_path,
@ -5281,6 +5447,26 @@ static void interface_added(struct spa_bt_monitor *monitor,
d = ep->device;
if (d)
spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
if (spa_streq(ep->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) {
int ret, i;
spa_autofree char *local_endpoint = NULL;
/* get local endpoint */
for (i = 0; monitor->media_codecs; i++) {
if (!monitor->media_codecs[i]->bap)
continue;
if (!is_media_codec_enabled(monitor, monitor->media_codecs[i]))
continue;
if (monitor->media_codecs[i]->codec_id == ep->codec){
ret = media_codec_to_endpoint(monitor->media_codecs[i], SPA_BT_MEDIA_SOURCE_BROADCAST, &local_endpoint);
if (ret == 0)
break;
}
}
if (local_endpoint != NULL)
configure_bcast_source(monitor, monitor->media_codecs[i], conn, object_path, interface_name, local_endpoint);
}
}
}
@ -5726,6 +5912,7 @@ static int impl_clear(struct spa_handle *handle)
struct spa_bt_device *d;
struct spa_bt_remote_endpoint *ep;
struct spa_bt_transport *t;
struct spa_bt_big *b;
const struct spa_dict_item *it;
size_t i;
@ -5754,6 +5941,8 @@ static int impl_clear(struct spa_handle *handle)
device_free(d);
spa_list_consume(a, &monitor->adapter_list, link)
adapter_free(a);
spa_list_consume(b, &monitor->bcast_source_config_list, link)
big_entry_free(b);
for (i = 0; i < SPA_N_ELEMENTS(monitor->backends); ++i) {
spa_bt_backend_free(monitor->backends[i]);
@ -5856,6 +6045,130 @@ done:
return res;
}
static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const struct spa_dict *info)
{
const char *str, *val;
char key[256];
char bis_key[256];
char qos_key[256];
int cursor;
int big_id = 0;
int len;
struct spa_json it[4], it_array[4];
struct spa_bt_big *big_entry = NULL;
struct spa_bt_bis *bis_entry = NULL;
struct spa_bt_metadata *metadata_entry = NULL;
int temp_val = 0;
/* Search for bluez5.bcast_source.config */
if (info && (str = spa_dict_lookup(info, "bluez5.bcast_source.config"))) {
spa_json_init(&it[0], str, strlen(str));
/* Verify is an array of BIGS */
if (spa_json_enter_array(&it[0], &it_array[0]) <= 0) {
spa_log_warn(monitor->log, "malformed bluez5.bcast_source.config setting ignored");
return;
}
spa_log_debug(monitor->log, "Here ");
/* Iterate on all BIG objects */
while (spa_json_enter_object(&it_array[0], &it[1]) > 0) {
big_entry = calloc(1, sizeof(struct spa_bt_big));
spa_list_init(&big_entry->bis_list);
/* Iterate on all BIG values */
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
if (spa_streq(key, "broadcast_code")) {
if (spa_json_enter_array(&it[1], &it_array[1]) <= 0)
goto parse_failed;
for (cursor = 0; cursor < BROADCAST_CODE_LEN; cursor++) {
if (spa_json_get_int(&it_array[1], &big_entry->broadcast_code[cursor]) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "big_entry->broadcast_code %d", big_entry->broadcast_code[cursor]);
}
} else if (spa_streq(key, "bis")) {
if (spa_json_enter_array(&it[1], &it_array[1]) <= 0)
goto parse_failed;
while (spa_json_enter_object(&it_array[1], &it[2]) > 0) {
/* Iterate on all BIS values */
bis_entry = calloc(1, sizeof(struct spa_bt_bis));
spa_list_init(&bis_entry->metadata_list);
while (spa_json_get_string(&it[2], bis_key, sizeof(bis_key)) > 0) {
if (spa_streq(bis_key, "qos_preset")) {
spa_json_get_string(&it[2], bis_entry->qos_preset, sizeof(bis_entry->qos_preset));
spa_log_debug(monitor->log, "bis_entry->qos_preset %s", bis_entry->qos_preset);
} else if (spa_streq(bis_key, "audio_channel_allocation")) {
if (spa_json_get_int(&it[2], &bis_entry->channel_allocation) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "bis_entry->channel_allocation %d", bis_entry->channel_allocation);
} else if (spa_streq(bis_key, "metadata")) {
if (spa_json_enter_array(&it[2], &it_array[2]) <= 0)
goto parse_failed;
while (spa_json_enter_object(&it_array[2], &it[3]) > 0) {
metadata_entry = calloc(1, sizeof(struct spa_bt_metadata));
while (spa_json_get_string(&it[3], qos_key, sizeof(qos_key)) > 0) {
if (spa_streq(qos_key, "length")) {
if (spa_json_get_int(&it[3], &metadata_entry->length) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "metadata_entry->length %d", metadata_entry->length);
} else if (spa_streq(qos_key, "type")) {
if (spa_json_get_int(&it[3], &metadata_entry->type) <= 0)
goto parse_failed;
spa_log_debug(monitor->log, "metadata_entry->type %d", metadata_entry->type);
} else if (spa_streq(qos_key, "value")) {
if ((len = spa_json_next(&it[3], &val)) <= 0)
goto parse_failed;
if (spa_json_is_array(val, len)) {
spa_json_enter(&it[3], &it_array[3]);
cursor = 0;
for (cursor = 0; cursor < metadata_entry->length; cursor++) {
if (spa_json_get_int(&it_array[3], &temp_val) <= 0)
break;
metadata_entry->value[cursor] = (uint8_t)temp_val;
spa_log_debug(monitor->log, "metadata_entry->value[cursor] %d", metadata_entry->value[cursor]);
}
spa_log_debug(monitor->log, "metadata_entry->value_size %d", cursor);
spa_list_append(&bis_entry->metadata_list, &metadata_entry->link);
metadata_entry = NULL;
}
}
}
}
}
}
spa_list_append(&big_entry->bis_list, &bis_entry->link);
bis_entry = NULL;
}
}
}
big_entry->big_id = big_id;
spa_list_append(&monitor->bcast_source_config_list, &big_entry->link);
big_id ++;
big_entry = NULL;
}
}
return;
parse_failed:
spa_log_debug(monitor->log, "Here failed");
if (metadata_entry)
free(metadata_entry);
if (bis_entry) {
spa_list_consume(metadata_entry, &bis_entry->metadata_list, link)
free(metadata_entry);
free(bis_entry);
}
if (big_entry) {
spa_list_consume(bis_entry, &big_entry->bis_list, link)
bis_entry_free(bis_entry);
free(big_entry);
}
spa_list_consume(big_entry, &monitor->bcast_source_config_list, link)
big_entry_free(big_entry);
}
static int parse_codec_array(struct spa_bt_monitor *this, const struct spa_dict *info)
{
const struct media_codec * const * const media_codecs = this->media_codecs;
@ -6039,11 +6352,13 @@ impl_init(const struct spa_handle_factory *factory,
spa_list_init(&this->device_list);
spa_list_init(&this->remote_endpoint_list);
spa_list_init(&this->transport_list);
spa_list_init(&this->bcast_source_config_list);
if ((res = parse_codec_array(this, info)) < 0)
goto fail;
parse_roles(this, info);
parse_broadcast_source_config(this, info);
this->default_audio_info.rate = A2DP_CODEC_DEFAULT_RATE;
this->default_audio_info.channels = A2DP_CODEC_DEFAULT_CHANNELS;

View file

@ -81,6 +81,10 @@ struct media_codec {
const struct media_codec *duplex_codec; /**< Codec for non-standard A2DP duplex channel */
int (*get_bis_config)(const struct media_codec *codec, uint8_t *caps,
uint8_t *caps_size, char *preset, int channel_allocation,
struct bap_codec_qos *qos);
/** 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]);