fix(bt): Build the optimal config when registering A2DP SEP

This commit is contained in:
yangfeng
2026-08-31 09:40:51 +08:00
parent 139d047883
commit cafa07904e
2 changed files with 146 additions and 19 deletions
@@ -162,7 +162,6 @@ tA2D_STATUS bta_av_m24_pick_pref_from_src_cap(const tA2D_M24_CIE *src_cap, tA2D_
{
UINT8 i;
UINT32 src_br_max;
UINT32 p_pref_br;
static const UINT8 sf2_order[] = {
A2D_M24_IE_SAMP_FREQ2_96,
A2D_M24_IE_SAMP_FREQ2_88,
@@ -195,12 +194,13 @@ tA2D_STATUS bta_av_m24_pick_pref_from_src_cap(const tA2D_M24_CIE *src_cap, tA2D_
}
}
if ((p_pref->drc & A2D_M24_IE_DRC_MSK) && !(src_cap->drc & A2D_M24_IE_DRC_MSK)) {
p_pref->drc = A2D_M24_IE_DRC_NS;
}
/* DRC: follow registered SEP capability (MPEG-2 AAC LC has no DRC) */
if ((p_pref->obj_type & A2D_M24_IE_OBJ_TYPE_MSK) == A2D_M24_IE_OBJ_TYPE_2_AAC_LC) {
p_pref->drc = A2D_M24_IE_DRC_NS;
} else if (src_cap->drc & A2D_M24_IE_DRC_MSK) {
p_pref->drc = A2D_M24_IE_DRC_SUPPORT;
} else {
p_pref->drc = A2D_M24_IE_DRC_NS;
}
if (!((p_pref->samp_freq1 & src_cap->samp_freq1) & A2D_M24_IE_SAMP_FREQ1_MSK) &&
@@ -246,10 +246,7 @@ got_sf:
}
src_br_max = bta_av_m24_br(src_cap);
p_pref_br = bta_av_m24_br(p_pref);
p_pref_br = bta_av_m24_br_min(p_pref_br, src_br_max);
bta_av_m24_set_br(p_pref, p_pref_br, (UINT8)(p_pref->vbr & A2D_M24_IE_VBR_MSK));
bta_av_m24_set_br(p_pref, src_br_max, (UINT8)(p_pref->vbr & A2D_M24_IE_VBR_MSK));
return A2D_SUCCESS;
}
@@ -635,6 +635,133 @@ void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
}
}
/*******************************************************************************
**
** Function bta_av_co_audio_isect_src_caps
**
** Description Intersect the codec capabilities of the peer source with
** the capabilities of the currently selected local sink SEP.
** The result is a capability byte sequence that both sides
** support, so a configuration built from it never exceeds
** the local sink capabilities.
**
** Returns TRUE if a non-empty intersection was built, FALSE otherwise.
**
*******************************************************************************/
static BOOLEAN bta_av_co_audio_isect_src_caps(UINT8 *p_src_caps, UINT8 *p_isect_caps)
{
UINT8 *p_snk_caps;
UINT8 codec_type;
#if (BTC_AV_EXT_CODEC == FALSE)
UINT8 snk_caps_info[AVDT_CODEC_SIZE];
#endif
FUNC_TRACE();
if (p_src_caps == NULL || p_isect_caps == NULL) {
return FALSE;
}
#if (BTC_AV_EXT_CODEC == TRUE)
if (bta_av_co_cb.cur_seid >= BTA_AV_MAX_SEPS) {
APPL_TRACE_ERROR("isect_src_caps OOB cur_seid: %d", bta_av_co_cb.cur_seid);
return FALSE;
}
p_snk_caps = bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].info;
#else
if (A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps, snk_caps_info) != A2D_SUCCESS) {
APPL_TRACE_ERROR("isect_src_caps A2D_BldSbcInfo failed");
return FALSE;
}
p_snk_caps = snk_caps_info;
#endif
codec_type = p_src_caps[BTA_AV_CODEC_TYPE_IDX];
if (codec_type != p_snk_caps[BTA_AV_CODEC_TYPE_IDX]) {
APPL_TRACE_ERROR("isect_src_caps codec mismatch src: %d snk: %d",
codec_type, p_snk_caps[BTA_AV_CODEC_TYPE_IDX]);
return FALSE;
}
switch (codec_type) {
case A2D_MEDIA_CT_SBC: {
tA2D_SBC_CIE src_cie;
tA2D_SBC_CIE snk_cie;
tA2D_SBC_CIE isect_cie;
if ((A2D_ParsSbcInfo(&src_cie, p_src_caps, TRUE) != A2D_SUCCESS) ||
(A2D_ParsSbcInfo(&snk_cie, p_snk_caps, TRUE) != A2D_SUCCESS)) {
APPL_TRACE_ERROR("isect_src_caps A2D_ParsSbcInfo failed");
return FALSE;
}
memset(&isect_cie, 0, sizeof(tA2D_SBC_CIE));
isect_cie.samp_freq = src_cie.samp_freq & snk_cie.samp_freq & A2D_SBC_IE_SAMP_FREQ_MSK;
isect_cie.ch_mode = src_cie.ch_mode & snk_cie.ch_mode & A2D_SBC_IE_CH_MD_MSK;
isect_cie.block_len = src_cie.block_len & snk_cie.block_len & A2D_SBC_IE_BLOCKS_MSK;
isect_cie.num_subbands = src_cie.num_subbands & snk_cie.num_subbands & A2D_SBC_IE_SUBBAND_MSK;
isect_cie.alloc_mthd = src_cie.alloc_mthd & snk_cie.alloc_mthd & A2D_SBC_IE_ALLOC_MD_MSK;
if (!isect_cie.samp_freq || !isect_cie.ch_mode || !isect_cie.block_len ||
!isect_cie.num_subbands || !isect_cie.alloc_mthd) {
APPL_TRACE_ERROR("isect_src_caps empty SBC intersection");
return FALSE;
}
/* bitpool: the tightest boundaries accepted by both sides */
isect_cie.min_bitpool = BTA_AV_CO_MAX(src_cie.min_bitpool, snk_cie.min_bitpool);
isect_cie.max_bitpool = BTA_AV_CO_MIN(src_cie.max_bitpool, snk_cie.max_bitpool);
if (isect_cie.min_bitpool > isect_cie.max_bitpool) {
APPL_TRACE_ERROR("isect_src_caps SBC bitpool mismatch min: %d max: %d",
isect_cie.min_bitpool, isect_cie.max_bitpool);
return FALSE;
}
return (A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, &isect_cie, p_isect_caps) == A2D_SUCCESS);
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case A2D_MEDIA_CT_M24: {
tA2D_M24_CIE src_cie;
tA2D_M24_CIE snk_cie;
tA2D_M24_CIE isect_cie;
UINT32 isect_br;
if ((A2D_ParsM24Info(&src_cie, p_src_caps, TRUE) != A2D_SUCCESS) ||
(A2D_ParsM24Info(&snk_cie, p_snk_caps, TRUE) != A2D_SUCCESS)) {
APPL_TRACE_ERROR("isect_src_caps A2D_ParsM24Info failed");
return FALSE;
}
memset(&isect_cie, 0, sizeof(tA2D_M24_CIE));
isect_cie.obj_type = src_cie.obj_type & snk_cie.obj_type & A2D_M24_IE_OBJ_TYPE_MSK;
isect_cie.drc = src_cie.drc & snk_cie.drc & A2D_M24_IE_DRC_MSK;
isect_cie.samp_freq1 = src_cie.samp_freq1 & snk_cie.samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK;
isect_cie.samp_freq2 = src_cie.samp_freq2 & snk_cie.samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK;
isect_cie.ch = src_cie.ch & snk_cie.ch & A2D_M24_IE_CH_MSK;
isect_cie.vbr = src_cie.vbr & snk_cie.vbr & A2D_M24_IE_VBR_MSK;
if (!isect_cie.obj_type || !isect_cie.ch ||
(!isect_cie.samp_freq1 && !isect_cie.samp_freq2)) {
APPL_TRACE_ERROR("isect_src_caps empty M24 intersection");
return FALSE;
}
/* bit rate in capabilities is an upper bound, 0 means "not known" */
isect_br = bta_av_m24_br_min(bta_av_m24_br_info(p_src_caps), bta_av_m24_br_info(p_snk_caps));
isect_cie.br1 = (UINT8)((isect_br >> 16) & A2D_M24_IE_BR1_MSK);
isect_cie.br2 = (UINT8)((isect_br >> 8) & A2D_M24_IE_BR2_MSK);
isect_cie.br3 = (UINT8)(isect_br & A2D_M24_IE_BR3_MSK);
return (A2D_BldM24Info(AVDT_MEDIA_AUDIO, &isect_cie, p_isect_caps) == A2D_SUCCESS);
}
#endif
default:
APPL_TRACE_ERROR("isect_src_caps unsup_codec type: %d", codec_type);
return FALSE;
}
}
/*******************************************************************************
**
** Function bta_av_audio_sink_getconfig
@@ -657,11 +784,12 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
tBTA_AV_CO_PEER *p_peer;
tBTA_AV_CO_SINK *p_src;
UINT8 pref_cfg[AVDT_CODEC_SIZE];
UINT8 isect_caps[AVDT_CODEC_SIZE];
UINT8 index;
FUNC_TRACE();
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig handle:0x%x codec_type:%d seid:%d",
APPL_TRACE_DEBUG("sink_getconfig handle:0x%x codec_type:%d seid:%d",
hndl, codec_type, seid);
APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x",
*p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]);
@@ -669,11 +797,11 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
/* Retrieve the peer info */
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL) {
APPL_TRACE_ERROR("bta_av_audio_sink_getconfig could not find peer entry");
APPL_TRACE_ERROR("sink_getconfig could not find peer entry");
return A2D_FAIL;
}
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)",
APPL_TRACE_DEBUG("sink_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)",
p_peer->opened, p_peer->num_srcs, p_peer->num_rx_srcs, p_peer->num_sup_srcs);
p_peer->num_rx_srcs++;
@@ -697,7 +825,7 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
if (p_peer->num_sup_srcs < BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs)) {
p_src = &p_peer->srcs[p_peer->num_sup_srcs++];
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig saved caps[%x:%x:%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("sink_getconfig saved caps[%x:%x:%x:%x:%x:%x:%x:%x]",
p_codec_info[1], p_codec_info[2], p_codec_info[3],
p_codec_info[4], p_codec_info[5], p_codec_info[6],
p_codec_info[7], p_codec_info[8]);
@@ -709,14 +837,14 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
p_src->num_protect = *p_num_protect;
memcpy(p_src->protect_info, p_protect_info, BTA_AV_CP_INFO_LEN);
} else {
APPL_TRACE_ERROR("bta_av_audio_sink_getconfig no more room for SRC info");
APPL_TRACE_ERROR("sink_getconfig no more room for SRC info");
}
}
/* If last SNK get capabilities or all supported codec caps retrieved */
if ((p_peer->num_rx_srcs == p_peer->num_srcs) ||
(p_peer->num_sup_srcs == BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs))) {
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig last SRC reached");
APPL_TRACE_DEBUG("sink_getconfig last SRC reached");
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_lock();
@@ -727,14 +855,14 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
p_src = &p_peer->srcs[index];
/* Build the codec configuration for this sink */
{
if (bta_av_co_audio_isect_src_caps(p_src->codec_caps, isect_caps)) {
/* Save the new configuration */
p_peer->p_src = p_src;
/* get preferred config from src_caps */
bta_av_build_src_cfg(pref_cfg, p_src->codec_caps);
/* get preferred config from the caps supported by both peer src and local sink */
bta_av_build_src_cfg(pref_cfg, isect_caps);
memcpy(p_peer->codec_cfg, pref_cfg, AVDT_CODEC_SIZE);
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x:%x:%x]",
p_peer->codec_cfg[1], p_peer->codec_cfg[2], p_peer->codec_cfg[3],
p_peer->codec_cfg[4], p_peer->codec_cfg[5], p_peer->codec_cfg[6],
p_peer->codec_cfg[7], p_peer->codec_cfg[8]);
@@ -749,6 +877,8 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
*p_sep_info_idx = p_src->sep_info_idx;
memcpy(p_codec_info, p_peer->codec_cfg, AVDT_CODEC_SIZE);
result = A2D_SUCCESS;
} else {
APPL_TRACE_ERROR("sink_getconfig no common caps with peer src");
}
}
/* Protect access to bta_av_co_cb.codec_cfg */