Merge branch 'bt-16' into '1.6'

[1.6] bluez5: backport fixes to 1.6 branch

See merge request pipewire/pipewire!2769
This commit is contained in:
Pauli Virtanen 2026-03-30 07:44:37 +00:00
commit d090fc3ea3
7 changed files with 138 additions and 198 deletions

View file

@ -1375,9 +1375,9 @@ Default: as per QoS preset.
@PAR@ device-prop bluez5.bap.force-target-latency = "balanced" # string @PAR@ device-prop bluez5.bap.force-target-latency = "balanced" # string
BAP QoS target latency profile forced for QoS configuration selection. BAP QoS target latency profile forced for QoS configuration selection.
If not set or set to "balanced", both low-latency and high-reliabilty QoS configuration table are used. If not set or set to "balanced", both low-latency and high-reliability QoS configuration table are used.
This property is experimental. This property is experimental.
Available: low-latency, high-reliabilty, balanced Available: low-latency, high-reliability, balanced
## Node properties ## Node properties

View file

@ -106,8 +106,8 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags,
static const struct media_codec_config static const struct media_codec_config
aac_frequencies[] = { aac_frequencies[] = {
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
{ AAC_SAMPLING_FREQ_44100, 44100, 10 }, { AAC_SAMPLING_FREQ_44100, 44100, 10 },
{ AAC_SAMPLING_FREQ_48000, 48000, 11 },
{ AAC_SAMPLING_FREQ_96000, 96000, 9 }, { AAC_SAMPLING_FREQ_96000, 96000, 9 },
{ AAC_SAMPLING_FREQ_88200, 88200, 8 }, { AAC_SAMPLING_FREQ_88200, 88200, 8 },
{ AAC_SAMPLING_FREQ_64000, 64000, 7 }, { AAC_SAMPLING_FREQ_64000, 64000, 7 },
@ -194,75 +194,6 @@ static int codec_select_config(const struct media_codec *codec, uint32_t flags,
return sizeof(conf); return sizeof(conf);
} }
static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
a2dp_aac_t conf;
struct spa_pod_frame f[2];
struct spa_pod_choice *choice;
uint32_t position[2];
uint32_t i = 0;
if (caps_size < sizeof(conf))
return -EINVAL;
memcpy(&conf, caps, sizeof(conf));
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16),
0);
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0);
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
i = 0;
SPA_FOR_EACH_ELEMENT_VAR(aac_frequencies, f) {
if (AAC_GET_FREQUENCY(conf) & f->config) {
if (i++ == 0)
spa_pod_builder_int(b, f->value);
spa_pod_builder_int(b, f->value);
}
}
if (i > 1)
choice->body.type = SPA_CHOICE_Enum;
spa_pod_builder_pop(b, &f[1]);
if (i == 0)
return -EINVAL;
if (SPA_FLAG_IS_SET(conf.channels, AAC_CHANNELS_1 | AAC_CHANNELS_2)) {
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
0);
} else if (conf.channels & AAC_CHANNELS_1) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 1, position),
0);
} else if (conf.channels & AAC_CHANNELS_2) {
position[0] = SPA_AUDIO_CHANNEL_FL;
position[1] = SPA_AUDIO_CHANNEL_FR;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 2, position),
0);
} else
return -EINVAL;
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}
static int codec_validate_config(const struct media_codec *codec, uint32_t flags, static int codec_validate_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, const void *caps, size_t caps_size,
struct spa_audio_info *info) struct spa_audio_info *info)
@ -283,8 +214,10 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
/* /*
* A2DP v1.3.2, 4.5.2: only one bit shall be set in bitfields. * A2DP v1.3.2, 4.5.2: only one bit shall be set in bitfields.
* However, there is a report (#1342) of device setting multiple * However, there is a report (#1342) of device setting multiple
* bits for AAC object type. It's not clear if this was due to * bits for AAC object type. In addition AirPods set multiple bits.
* a BlueZ bug, but we can be lax here and below in codec_init. *
* Some devices also set multiple bits in frequencies & channels.
* For these, pick a "preferred" choice.
*/ */
if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC | if (!(conf.object_type & (AAC_OBJECT_TYPE_MPEG2_AAC_LC |
AAC_OBJECT_TYPE_MPEG4_AAC_LC | AAC_OBJECT_TYPE_MPEG4_AAC_LC |
@ -315,6 +248,35 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags
return 0; return 0;
} }
static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param)
{
struct spa_audio_info info;
struct spa_pod_frame f[1];
int res;
if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
return res;
if (idx > 0)
return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
0);
*param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1;
}
static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings) static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings)
{ {
struct props *p = calloc(1, sizeof(struct props)); struct props *p = calloc(1, sizeof(struct props));
@ -369,14 +331,14 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags,
goto error; goto error;
/* If object type has multiple bits set (invalid per spec, see above), /* If object type has multiple bits set (invalid per spec, see above),
* assume the device usually means AAC-LC. * assume the device usually means MPEG2 AAC LC which is mandatory.
*/ */
if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) { if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC); res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC);
if (res != AACENC_OK) if (res != AACENC_OK)
goto error; goto error;
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG2_AAC_LC) { } else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_LC) {
res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_MP2_AAC_LC); res = aacEncoder_SetParam(this->aacenc, AACENC_AOT, AOT_AAC_LC);
if (res != AACENC_OK) if (res != AACENC_OK)
goto error; goto error;
} else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD) { } else if (conf->object_type & AAC_OBJECT_TYPE_MPEG4_AAC_ELD) {

View file

@ -343,77 +343,27 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags,
const void *caps, size_t caps_size, uint32_t id, uint32_t idx, const void *caps, size_t caps_size, uint32_t id, uint32_t idx,
struct spa_pod_builder *b, struct spa_pod **param) struct spa_pod_builder *b, struct spa_pod **param)
{ {
a2dp_sbc_t conf; struct spa_audio_info info;
struct spa_pod_frame f[2]; struct spa_pod_frame f[1];
struct spa_pod_choice *choice; int res;
uint32_t i = 0;
uint32_t position[2];
if (caps_size < sizeof(conf)) if ((res = codec_validate_config(codec, flags, caps, caps_size, &info)) < 0)
return -EINVAL; return res;
memcpy(&conf, caps, sizeof(conf));
if (idx > 0) if (idx > 0)
return 0; return 0;
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id); spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id);
spa_pod_builder_add(b, spa_pod_builder_add(b,
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaType, SPA_POD_Id(info.media_type),
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), SPA_FORMAT_mediaSubtype, SPA_POD_Id(info.media_subtype),
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S16), SPA_FORMAT_AUDIO_format, SPA_POD_Id(info.info.raw.format),
0); SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate),
spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels),
spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0);
choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]);
i = 0;
if (conf.frequency & SBC_SAMPLING_FREQ_48000) {
if (i++ == 0)
spa_pod_builder_int(b, 48000);
spa_pod_builder_int(b, 48000);
}
if (conf.frequency & SBC_SAMPLING_FREQ_44100) {
if (i++ == 0)
spa_pod_builder_int(b, 44100);
spa_pod_builder_int(b, 44100);
}
if (conf.frequency & SBC_SAMPLING_FREQ_32000) {
if (i++ == 0)
spa_pod_builder_int(b, 32000);
spa_pod_builder_int(b, 32000);
}
if (conf.frequency & SBC_SAMPLING_FREQ_16000) {
if (i++ == 0)
spa_pod_builder_int(b, 16000);
spa_pod_builder_int(b, 16000);
}
if (i > 1)
choice->body.type = SPA_CHOICE_Enum;
spa_pod_builder_pop(b, &f[1]);
if (conf.channel_mode & SBC_CHANNEL_MODE_MONO &&
conf.channel_mode & (SBC_CHANNEL_MODE_JOINT_STEREO |
SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_DUAL_CHANNEL)) {
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(2, 1, 2),
0);
} else if (conf.channel_mode & SBC_CHANNEL_MODE_MONO) {
position[0] = SPA_AUDIO_CHANNEL_MONO;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(1),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 1, position), SPA_TYPE_Id, info.info.raw.channels, info.info.raw.position),
0); 0);
} else {
position[0] = SPA_AUDIO_CHANNEL_FL;
position[1] = SPA_AUDIO_CHANNEL_FR;
spa_pod_builder_add(b,
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(2),
SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t),
SPA_TYPE_Id, 2, position),
0);
}
*param = spa_pod_builder_pop(b, &f[0]); *param = spa_pod_builder_pop(b, &f[0]);
return *param == NULL ? -EIO : 1; return *param == NULL ? -EIO : 1;
} }

View file

@ -1969,6 +1969,9 @@ static void hfp_hf_remove_disconnected_calls(struct rfcomm *rfcomm)
struct updated_call *updated_call; struct updated_call *updated_call;
bool found; bool found;
if (!rfcomm->telephony_ag)
return;
spa_list_for_each_safe(call, call_tmp, &rfcomm->telephony_ag->call_list, link) { spa_list_for_each_safe(call, call_tmp, &rfcomm->telephony_ag->call_list, link) {
found = false; found = false;
spa_list_for_each(updated_call, &rfcomm->updated_call_list, link) { spa_list_for_each(updated_call, &rfcomm->updated_call_list, link) {
@ -2097,6 +2100,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (spa_streq(rfcomm->hf_indicators[indicator], "battchg")) { if (spa_streq(rfcomm->hf_indicators[indicator], "battchg")) {
spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5); spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5);
} else if (!rfcomm->telephony_ag) {
/* noop */
} else if (spa_streq(rfcomm->hf_indicators[indicator], "callsetup")) { } else if (spa_streq(rfcomm->hf_indicators[indicator], "callsetup")) {
if (rfcomm->hfp_hf_clcc) { if (rfcomm->hfp_hf_clcc) {
rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC"); rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC");
@ -2245,7 +2250,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
rfcomm->hfp_hf_in_progress = false; rfcomm->hfp_hf_in_progress = false;
} }
} }
} else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2) { } else if (sscanf(token, "+CLIP: \"%16[^\"]\",%u", number, &type) == 2
&& rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call; struct spa_bt_telephony_call *call;
spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_INCOMING && !spa_streq(number, call->line_identification)) { if (call->state == CALL_STATE_INCOMING && !spa_streq(number, call->line_identification)) {
@ -2256,7 +2262,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break; break;
} }
} }
} else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2) { } else if (sscanf(token, "+CCWA: \"%16[^\"]\",%u", number, &type) == 2
&& rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call; struct spa_bt_telephony_call *call;
bool found = false; bool found = false;
@ -2273,7 +2280,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
if (call == NULL) if (call == NULL)
spa_log_warn(backend->log, "failed to create waiting call"); spa_log_warn(backend->log, "failed to create waiting call");
} }
} else if (spa_strstartswith(token, "+CLCC:")) { } else if (spa_strstartswith(token, "+CLCC:") && rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call; struct spa_bt_telephony_call *call;
size_t pos; size_t pos;
char *token_end; char *token_end;
@ -2421,6 +2428,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
} }
} }
if (backend->telephony) {
rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0); rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0);
rfcomm->telephony_ag->address = strdup(rfcomm->device->address); rfcomm->telephony_ag->address = strdup(rfcomm->device->address);
rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume; rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume;
@ -2432,6 +2440,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
rfcomm->telephony_ag->transport.state = rfcomm->transport->state; rfcomm->telephony_ag->transport.state = rfcomm->transport->state;
} }
telephony_ag_register(rfcomm->telephony_ag); telephony_ag_register(rfcomm->telephony_ag);
}
rfcomm_send_cmd(rfcomm, hfp_hf_clip, NULL, "AT+CLIP=1"); rfcomm_send_cmd(rfcomm, hfp_hf_clip, NULL, "AT+CLIP=1");
break; break;
@ -2478,7 +2487,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
break; break;
case hfp_hf_chld1_hangup: case hfp_hf_chld1_hangup:
/* For HFP/HF/TWC/BV-03-C - see 0e92ab9307e05758b3f70b4c0648e29c1d1e50be */ /* For HFP/HF/TWC/BV-03-C - see 0e92ab9307e05758b3f70b4c0648e29c1d1e50be */
if (!rfcomm->hfp_hf_clcc) { if (!rfcomm->hfp_hf_clcc && rfcomm->telephony_ag) {
struct spa_bt_telephony_call *call, *tcall; struct spa_bt_telephony_call *call, *tcall;
spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) { spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) {
if (call->state == CALL_STATE_ACTIVE) { if (call->state == CALL_STATE_ACTIVE) {

View file

@ -126,22 +126,22 @@ static const struct bap_qos bap_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27, "low-latency"), /* 48_6_1 */ BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 5, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 5.2; high-reliability */ /* BAP v1.0.1 Table 5.2; high-reliability */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliabilty"), /* 8_1_2 */ BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 13, 75, 40000, 10, "high-reliability"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliabilty"), /* 8_2_2 */ BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 13, 95, 40000, 0, "high-reliability"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliabilty"), /* 16_1_2 */ BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 13, 75, 40000, 11, "high-reliability"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliabilty"), /* 16_2_2 */ BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 13, 95, 40000, 1, "high-reliability"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliabilty"), /* 24_1_2 */ BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 13, 75, 40000, 12, "high-reliability"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliabilty"), /* 24_2_2 */ BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 13, 95, 40000, 2, "high-reliability"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliabilty"), /* 32_1_2 */ BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 13, 75, 40000, 13, "high-reliability"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliabilty"), /* 32_2_2 */ BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 13, 95, 40000, 3, "high-reliability"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliabilty"), /* 441_1_2 */ BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 13, 80, 40000, 54, "high-reliability"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliabilty"), /* 441_2_2 */ BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 13, 85, 40000, 44, "high-reliability"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliabilty"), /* 48_1_2 */ BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 13, 75, 40000, 55, "high-reliability"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliabilty"), /* 48_2_2 */ BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 13, 95, 40000, 45, "high-reliability"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliabilty"), /* 48_3_2 */ BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 13, 75, 40000, 56, "high-reliability"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliabilty"), /* 48_4_2 */ BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 13, 100, 40000, 46, "high-reliability"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliabilty"), /* 48_5_2 */ BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 13, 75, 40000, 57, "high-reliability"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliabilty"), /* 48_6_2 */ BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 13, 100, 40000, 47, "high-reliability"), /* 48_6_2 */
}; };
static const struct bap_qos bap_bcast_qos_configs[] = { static const struct bap_qos bap_bcast_qos_configs[] = {
@ -167,22 +167,22 @@ static const struct bap_qos bap_bcast_qos_configs[] = {
BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27, "low-latency"), /* 48_6_1 */ BAP_QOS("48_6_1", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 20, 40000, 27, "low-latency"), /* 48_6_1 */
/* BAP v1.0.1 Table 6.4; high-reliability */ /* BAP v1.0.1 Table 6.4; high-reliability */
BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliabilty"), /* 8_1_2 */ BAP_QOS("8_1_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_7_5, false, 26, 4, 45, 40000, 10, "high-reliability"), /* 8_1_2 */
BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliabilty"), /* 8_2_2 */ BAP_QOS("8_2_2", LC3_CONFIG_FREQ_8KHZ, LC3_CONFIG_DURATION_10, false, 30, 4, 60, 40000, 0, "high-reliability"), /* 8_2_2 */
BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliabilty"), /* 16_1_2 */ BAP_QOS("16_1_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_7_5, false, 30, 4, 45, 40000, 11, "high-reliability"), /* 16_1_2 */
BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliabilty"), /* 16_2_2 */ BAP_QOS("16_2_2", LC3_CONFIG_FREQ_16KHZ, LC3_CONFIG_DURATION_10, false, 40, 4, 60, 40000, 1, "high-reliability"), /* 16_2_2 */
BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliabilty"), /* 24_1_2 */ BAP_QOS("24_1_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_7_5, false, 45, 4, 45, 40000, 12, "high-reliability"), /* 24_1_2 */
BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliabilty"), /* 24_2_2 */ BAP_QOS("24_2_2", LC3_CONFIG_FREQ_24KHZ, LC3_CONFIG_DURATION_10, false, 60, 4, 60, 40000, 2, "high-reliability"), /* 24_2_2 */
BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliabilty"), /* 32_1_2 */ BAP_QOS("32_1_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_7_5, false, 60, 4, 45, 40000, 13, "high-reliability"), /* 32_1_2 */
BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliabilty"), /* 32_2_2 */ BAP_QOS("32_2_2", LC3_CONFIG_FREQ_32KHZ, LC3_CONFIG_DURATION_10, false, 80, 4, 60, 40000, 3, "high-reliability"), /* 32_2_2 */
BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliabilty"), /* 441_1_2 */ BAP_QOS("441_1_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_7_5, true, 97, 4, 54, 40000, 14, "high-reliability"), /* 441_1_2 */
BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliabilty"), /* 441_2_2 */ BAP_QOS("441_2_2", LC3_CONFIG_FREQ_44KHZ, LC3_CONFIG_DURATION_10, true, 130, 4, 60, 40000, 4, "high-reliability"), /* 441_2_2 */
BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliabilty"), /* 48_1_2 */ BAP_QOS("48_1_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 75, 4, 50, 40000, 15, "high-reliability"), /* 48_1_2 */
BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliabilty"), /* 48_2_2 */ BAP_QOS("48_2_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 100, 4, 65, 40000, 5, "high-reliability"), /* 48_2_2 */
BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliabilty"), /* 48_3_2 */ BAP_QOS("48_3_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 90, 4, 50, 40000, 16, "high-reliability"), /* 48_3_2 */
BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliabilty"), /* 48_4_2 */ BAP_QOS("48_4_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 120, 4, 65, 40000, 6, "high-reliability"), /* 48_4_2 */
BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliabilty"), /* 48_5_2 */ BAP_QOS("48_5_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_7_5, false, 117, 4, 50, 40000, 17, "high-reliability"), /* 48_5_2 */
BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliabilty"), /* 48_6_2 */ BAP_QOS("48_6_2", LC3_CONFIG_FREQ_48KHZ, LC3_CONFIG_DURATION_10, false, 155, 4, 65, 40000, 7, "high-reliability"), /* 48_6_2 */
}; };
static unsigned int get_rate_mask(uint8_t rate) { static unsigned int get_rate_mask(uint8_t rate) {

View file

@ -587,18 +587,35 @@ static enum spa_bt_profile get_codec_profile(const struct media_codec *codec,
{ {
switch (direction) { switch (direction) {
case SPA_BT_MEDIA_SOURCE: case SPA_BT_MEDIA_SOURCE:
return codec->kind == MEDIA_CODEC_BAP ? SPA_BT_PROFILE_BAP_SOURCE : SPA_BT_PROFILE_A2DP_SOURCE; if (codec->kind == MEDIA_CODEC_A2DP)
return SPA_BT_PROFILE_A2DP_SOURCE;
else if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_SOURCE;
else if (codec->kind == MEDIA_CODEC_HFP)
return SPA_BT_PROFILE_HEADSET_AUDIO;
else
return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SINK: case SPA_BT_MEDIA_SINK:
if (codec->kind == MEDIA_CODEC_ASHA) if (codec->kind == MEDIA_CODEC_A2DP)
return SPA_BT_PROFILE_A2DP_SINK;
else if (codec->kind == MEDIA_CODEC_ASHA)
return SPA_BT_PROFILE_ASHA_SINK; return SPA_BT_PROFILE_ASHA_SINK;
else if (codec->kind == MEDIA_CODEC_BAP) else if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_SINK; return SPA_BT_PROFILE_BAP_SINK;
else if (codec->kind == MEDIA_CODEC_HFP)
return SPA_BT_PROFILE_HEADSET_AUDIO;
else else
return SPA_BT_PROFILE_A2DP_SINK; return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SOURCE_BROADCAST: case SPA_BT_MEDIA_SOURCE_BROADCAST:
if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE; return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE;
else
return SPA_BT_PROFILE_NULL;
case SPA_BT_MEDIA_SINK_BROADCAST: case SPA_BT_MEDIA_SINK_BROADCAST:
if (codec->kind == MEDIA_CODEC_BAP)
return SPA_BT_PROFILE_BAP_BROADCAST_SINK; return SPA_BT_PROFILE_BAP_BROADCAST_SINK;
else
return SPA_BT_PROFILE_NULL;
default: default:
spa_assert_not_reached(); spa_assert_not_reached();
} }
@ -2777,16 +2794,18 @@ bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const stru
bool is_bap = codec->kind == MEDIA_CODEC_BAP; bool is_bap = codec->kind == MEDIA_CODEC_BAP;
size_t i; size_t i;
codec_target_profile = get_codec_target_profile(monitor, codec);
if (!codec_target_profile)
return false;
if (codec->kind == MEDIA_CODEC_HFP) { if (codec->kind == MEDIA_CODEC_HFP) {
if (!(profile & SPA_BT_PROFILE_HEADSET_AUDIO)) if (!(profile & SPA_BT_PROFILE_HEADSET_AUDIO))
return false; return false;
if (!is_media_codec_enabled(monitor, codec))
return false;
return spa_bt_backend_supports_codec(monitor->backend, device, codec->codec_id) == 1; return spa_bt_backend_supports_codec(monitor->backend, device, codec->codec_id) == 1;
} }
codec_target_profile = get_codec_target_profile(monitor, codec);
if (!codec_target_profile)
return false;
if (!device->adapter->a2dp_application_registered && is_a2dp) { if (!device->adapter->a2dp_application_registered && is_a2dp) {
/* Codec switching not supported: only plain SBC allowed */ /* Codec switching not supported: only plain SBC allowed */
return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc") && return (codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc") &&
@ -7078,7 +7097,7 @@ static void parse_broadcast_source_config(struct spa_bt_monitor *monitor, const
memcpy(big_entry->broadcast_code, bcode, strlen(bcode)); memcpy(big_entry->broadcast_code, bcode, strlen(bcode));
spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code); spa_log_debug(monitor->log, "big_entry->broadcast_code %s", big_entry->broadcast_code);
} else if (spa_streq(key, "adapter")) { } else if (spa_streq(key, "adapter")) {
if (spa_json_get_string(&it[1], big_entry->adapter, sizeof(big_entry->adapter)) <= 0) if (spa_json_get_string(&it[0], big_entry->adapter, sizeof(big_entry->adapter)) <= 0)
goto parse_failed; goto parse_failed;
spa_log_debug(monitor->log, "big_entry->adapter %s", big_entry->adapter); spa_log_debug(monitor->log, "big_entry->adapter %s", big_entry->adapter);
} else if (spa_streq(key, "encryption")) { } else if (spa_streq(key, "encryption")) {

View file

@ -1784,7 +1784,7 @@ static uint32_t get_samples(struct impl *this, int64_t *duration_ns)
static void update_target_latency(struct impl *this) static void update_target_latency(struct impl *this)
{ {
struct port *port = &this->port; struct port *port = &this->port;
int32_t target; int32_t target = 0;
int samples; int samples;
if (this->transport == NULL || !port->have_format) if (this->transport == NULL || !port->have_format)
@ -1803,7 +1803,7 @@ static void update_target_latency(struct impl *this)
*/ */
if (this->decode_buffer_target) if (this->decode_buffer_target)
target = this->decode_buffer_target; target = this->decode_buffer_target;
else else if (this->transport->iso_io)
target = spa_bt_iso_io_get_source_target_latency(this->transport->iso_io); target = spa_bt_iso_io_get_source_target_latency(this->transport->iso_io);
spa_bt_decode_buffer_set_target_latency(&port->buffer, target); spa_bt_decode_buffer_set_target_latency(&port->buffer, target);