mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'bugfix/a2dp_aac_br_v6.1' into 'release/v6.1'
fix(bt): Fix some bugs of A2DP AAC (v6.1) See merge request espressif/esp-idf!52186
This commit is contained in:
@@ -257,6 +257,7 @@ typedef enum {
|
||||
ESP_A2D_SEP_REG_FAIL, /*!< A2DP stream endpoint register generic fail */
|
||||
ESP_A2D_SEP_REG_UNSUPPORTED, /*!< A2DP stream endpoint register fail, unsupported codec type or param */
|
||||
ESP_A2D_SEP_REG_INVALID_STATE, /*!< A2DP stream endpoint register fail, invalid state */
|
||||
ESP_A2D_SEP_REG_SBC_REQUIRED, /*!< A2DP stream endpoint register fail, at least one SBC SEP must remain */
|
||||
} esp_a2d_sep_reg_state_t;
|
||||
|
||||
/**
|
||||
@@ -511,7 +512,11 @@ esp_err_t esp_a2d_sink_init(void);
|
||||
* SEP index repeatedly will overwrite the old one.
|
||||
* It is necessary to set BT_A2DP_USE_EXTERNAL_CODEC to y.
|
||||
*
|
||||
* @note The SEID determines the priority of negotiating the configuration with the peer for initiator.
|
||||
* @note After A2DP sink init, every SEID is pre-registered with the mandatory default SBC SEP.
|
||||
* Calling this API overwrites that SEID. A2DP requires at least one SBC SEP; registering a
|
||||
* non-SBC codec that would overwrite the last SBC SEP fails with ESP_A2D_SEP_REG_SBC_REQUIRED.
|
||||
* Therefore using AAC (or other non-SBC codecs) requires ESP_A2D_MAX_SEPS >= 2.
|
||||
* The SEID determines the priority of negotiating the configuration with the peer for initiator.
|
||||
* The lower the SEID, the higher the priority of the codec capability.
|
||||
*
|
||||
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
|
||||
@@ -667,7 +672,11 @@ esp_err_t esp_a2d_source_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d
|
||||
* SEP index repeatedly will overwrite the old one.
|
||||
* It is necessary to set BT_A2DP_USE_EXTERNAL_CODEC to y.
|
||||
*
|
||||
* @note The SEID determines the priority of negotiating the configuration with the peer for initiator.
|
||||
* @note After A2DP source init, every SEID is pre-registered with the mandatory default SBC SEP.
|
||||
* Calling this API overwrites that SEID. A2DP requires at least one SBC SEP; registering a
|
||||
* non-SBC codec that would overwrite the last SBC SEP fails with ESP_A2D_SEP_REG_SBC_REQUIRED.
|
||||
* Therefore using AAC (or other non-SBC codecs) requires ESP_A2D_MAX_SEPS >= 2.
|
||||
* The SEID determines the priority of negotiating the configuration with the peer for initiator.
|
||||
* The lower the SEID, the higher the priority of the codec capability.
|
||||
*
|
||||
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
|
||||
|
||||
@@ -53,6 +53,18 @@ void bta_av_m24_set_br_info(UINT8 *codec_info, UINT32 bit_rate)
|
||||
codec_info[BTA_AV_M24_BR3_OFF] = (UINT8)(bit_rate & A2D_M24_IE_BR3_MSK);
|
||||
}
|
||||
|
||||
UINT32 bta_av_m24_br_min(UINT32 br1, UINT32 br2)
|
||||
{
|
||||
/* A2DP MPEG-2/4 AAC: bit rate 0 means "not known", not an upper bound of 0. */
|
||||
if (br1 == 0) {
|
||||
return br2;
|
||||
}
|
||||
if (br2 == 0) {
|
||||
return br1;
|
||||
}
|
||||
return (br1 < br2) ? br1 : br2;
|
||||
}
|
||||
|
||||
UINT8 bta_av_m24_cap_matches_cap(UINT8 *p_cap1, UINT8 *p_cap2)
|
||||
{
|
||||
tA2D_M24_CIE cap1;
|
||||
@@ -62,8 +74,6 @@ UINT8 bta_av_m24_cap_matches_cap(UINT8 *p_cap1, UINT8 *p_cap2)
|
||||
UINT8 cap2_sf1;
|
||||
UINT8 cap1_sf2;
|
||||
UINT8 cap2_sf2;
|
||||
UINT32 cap1_br;
|
||||
UINT32 cap2_br;
|
||||
|
||||
st = A2D_ParsM24Info(&cap1, p_cap1, TRUE);
|
||||
if (st != A2D_SUCCESS) {
|
||||
@@ -90,13 +100,6 @@ UINT8 bta_av_m24_cap_matches_cap(UINT8 *p_cap1, UINT8 *p_cap2)
|
||||
return A2D_NS_CHANNEL;
|
||||
}
|
||||
|
||||
cap1_br = bta_av_m24_br(&cap1);
|
||||
cap2_br = bta_av_m24_br(&cap2);
|
||||
|
||||
if (!cap1_br || !cap2_br) {
|
||||
return A2D_NS_BIT_RATE;
|
||||
}
|
||||
|
||||
return A2D_SUCCESS;
|
||||
|
||||
}
|
||||
@@ -147,7 +150,8 @@ UINT8 bta_av_m24_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap)
|
||||
|
||||
cfg_br = bta_av_m24_br(&cfg);
|
||||
cap_br = bta_av_m24_br(&cap);
|
||||
if (cfg_br > cap_br) {
|
||||
/* Cap 0 = unknown/unlimited; cfg 0 = bit rate not known in SetConfiguration. */
|
||||
if (cap_br != 0 && cfg_br > cap_br) {
|
||||
return A2D_NS_BIT_RATE;
|
||||
}
|
||||
|
||||
@@ -158,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,
|
||||
@@ -191,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) &&
|
||||
@@ -242,12 +246,7 @@ got_sf:
|
||||
}
|
||||
|
||||
src_br_max = bta_av_m24_br(src_cap);
|
||||
p_pref_br = bta_av_m24_br(p_pref);
|
||||
if (p_pref_br > src_br_max) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -542,10 +542,8 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
|
||||
tAVDT_CS cs;
|
||||
char *p_service_name;
|
||||
tBTA_UTL_COD cod;
|
||||
#if (BTA_AV_EXT_CODEC == FALSE)
|
||||
tBTA_AV_CODEC codec_type;
|
||||
UINT8 index = 0;
|
||||
#endif
|
||||
char p_avk_service_name[BTA_SERVICE_NAME_LEN + 1];
|
||||
BCM_STRLCPY_S(p_avk_service_name, BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN + 1);
|
||||
|
||||
@@ -667,10 +665,25 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
|
||||
|
||||
/* keep the configuration in the stream control block */
|
||||
memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
|
||||
#if (BTA_AV_EXT_CODEC == FALSE)
|
||||
while (index < BTA_AV_MAX_SEPS &&
|
||||
(p_scb->p_cos->init)(index, &codec_type, cs.cfg.codec_info,
|
||||
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg.tsep) == TRUE) {
|
||||
/*
|
||||
* Create local SEPs:
|
||||
* - Internal codec: init() supplies SBC (typically one SEP).
|
||||
* - External codec: A2DP mandates SBC, so pre-fill every SEID with
|
||||
* the default SBC SEP; app register_stream_endpoint() may overwrite.
|
||||
*/
|
||||
while (index < BTA_AV_MAX_SEPS) {
|
||||
#if (BTA_AV_EXT_CODEC == TRUE)
|
||||
if (bta_av_co_audio_build_sbc_default(p_data->api_reg.tsep, &codec_type,
|
||||
cs.cfg.codec_info) != TRUE) {
|
||||
APPL_TRACE_ERROR("failed to build default SBC SEP for seid %d", index);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if ((p_scb->p_cos->init)(index, &codec_type, cs.cfg.codec_info,
|
||||
&cs.cfg.num_protect, cs.cfg.protect_info,
|
||||
p_data->api_reg.tsep) != TRUE) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if (BTA_AV_SINK_INCLUDED == TRUE)
|
||||
if (p_data->api_reg.tsep == AVDT_TSEP_SNK) {
|
||||
@@ -698,7 +711,6 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bta_av_cb.reg_audio) {
|
||||
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
|
||||
|
||||
@@ -87,6 +87,19 @@ typedef struct {
|
||||
extern BOOLEAN bta_av_co_audio_init(UINT8 seid, UINT8 *p_codec_type, UINT8 *p_codec_info,
|
||||
UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 tsep);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_co_audio_build_sbc_default
|
||||
**
|
||||
** Description Build the mandatory default SBC codec capability for the
|
||||
** given SEP type (source or sink).
|
||||
**
|
||||
** Returns TRUE if built successfully, FALSE otherwise.
|
||||
**
|
||||
*******************************************************************************/
|
||||
extern BOOLEAN bta_av_co_audio_build_sbc_default(UINT8 tsep, UINT8 *p_codec_type,
|
||||
UINT8 *p_codec_info);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_co_audio_disc_res
|
||||
|
||||
@@ -41,6 +41,19 @@ extern UINT32 bta_av_m24_br_info(const UINT8 *codec_info);
|
||||
*******************************************************************************/
|
||||
extern void bta_av_m24_set_br_info(UINT8 *codec_info, UINT32 bit_rate);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_m24_br_min
|
||||
**
|
||||
** Description Select the negotiated AAC bit rate from two values.
|
||||
** Per A2DP, bit rate 0 means "not known" (no upper bound),
|
||||
** so it is ignored when the other value is non-zero.
|
||||
**
|
||||
** Returns Negotiated bit rate (0 only if both inputs are 0).
|
||||
**
|
||||
*******************************************************************************/
|
||||
extern UINT32 bta_av_m24_br_min(UINT32 br1, UINT32 br2);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_m24_cap_matches_cap
|
||||
|
||||
@@ -298,6 +298,37 @@ static void bta_av_co_report_peer_all_snk_codec_caps(tBTA_AV_HNDL hndl)
|
||||
}
|
||||
#endif /* BTC_AV_SRC_INCLUDED */
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_co_audio_build_sbc_default
|
||||
**
|
||||
** Description Build the mandatory default SBC codec capability for the
|
||||
** given SEP type (source or sink).
|
||||
**
|
||||
** Returns TRUE if built successfully, FALSE otherwise.
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN bta_av_co_audio_build_sbc_default(UINT8 tsep, UINT8 *p_codec_type, UINT8 *p_codec_info)
|
||||
{
|
||||
FUNC_TRACE();
|
||||
|
||||
if (p_codec_type == NULL || p_codec_info == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*p_codec_type = BTA_AV_CODEC_SBC;
|
||||
if (tsep == AVDT_TSEP_SRC) {
|
||||
return (A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *)&bta_av_co_sbc_caps,
|
||||
p_codec_info) == A2D_SUCCESS);
|
||||
} else if (tsep == AVDT_TSEP_SNK) {
|
||||
return (A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps,
|
||||
p_codec_info) == A2D_SUCCESS);
|
||||
}
|
||||
|
||||
APPL_TRACE_WARNING("bta_av_co_audio_build_sbc_default invalid SEP type %d", tsep);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_co_audio_init
|
||||
@@ -604,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
|
||||
@@ -626,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]);
|
||||
@@ -638,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++;
|
||||
@@ -666,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]);
|
||||
@@ -678,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();
|
||||
@@ -696,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]);
|
||||
@@ -718,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 */
|
||||
@@ -1286,17 +1447,21 @@ static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UIN
|
||||
case BTC_AV_CODEC_M24: {
|
||||
UINT32 cfg_bit_rate;
|
||||
UINT32 cap_bit_rate;
|
||||
UINT32 nego_bit_rate;
|
||||
|
||||
/* LOSC(1) + media(1) + codec(1) + CIE(6) = 9 octets */
|
||||
memcpy(p_codec_cfg, bta_av_co_cb.codec_cfg.info, BTA_AV_CO_M24_INFO_LEN);
|
||||
|
||||
cfg_bit_rate = bta_av_m24_br_info(p_codec_cfg);
|
||||
cap_bit_rate = bta_av_m24_br_info(p_codec_caps);
|
||||
/* Bit rate 0 means "not known" per A2DP; do not treat it as min=0. */
|
||||
nego_bit_rate = bta_av_m24_br_min(cfg_bit_rate, cap_bit_rate);
|
||||
|
||||
bta_av_m24_set_br_info(p_codec_cfg, BTA_AV_CO_MIN(cfg_bit_rate, cap_bit_rate));
|
||||
bta_av_m24_set_br_info(p_codec_cfg, nego_bit_rate);
|
||||
p_codec_cfg[BTA_AV_CO_M24_VBR_BR1_OFF] &= (p_codec_caps[BTA_AV_CO_M24_VBR_BR1_OFF] & A2D_M24_IE_VBR_MSK) | ~A2D_M24_IE_VBR_MSK;
|
||||
|
||||
APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config bit rate: 0x%u(cfg_br: %u/cap_br: %u, min: %u)", bta_av_m24_br_info(p_codec_cfg), cfg_bit_rate, cap_bit_rate, BTA_AV_CO_MIN(cfg_bit_rate, cap_bit_rate));
|
||||
APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config bit rate: %u(cfg_br: %u/cap_br: %u, nego: %u)",
|
||||
bta_av_m24_br_info(p_codec_cfg), cfg_bit_rate, cap_bit_rate, nego_bit_rate);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -85,6 +85,24 @@ typedef enum {
|
||||
#define BTC_AV_M24_CIE_OFFSET 3
|
||||
#define BTC_AV_M24_CIE_LEN 6
|
||||
|
||||
#define BTC_AV_SBC_SNK_HQ_BITPOOL 53
|
||||
#define BTC_AV_SBC_CH_MODE_ALL (ESP_A2D_SBC_CIE_CH_MODE_MONO | \
|
||||
ESP_A2D_SBC_CIE_CH_MODE_DUAL_CHANNEL | \
|
||||
ESP_A2D_SBC_CIE_CH_MODE_STEREO | \
|
||||
ESP_A2D_SBC_CIE_CH_MODE_JOINT_STEREO)
|
||||
#define BTC_AV_SBC_CH_MODE_SRC_C1 (ESP_A2D_SBC_CIE_CH_MODE_DUAL_CHANNEL | \
|
||||
ESP_A2D_SBC_CIE_CH_MODE_STEREO | \
|
||||
ESP_A2D_SBC_CIE_CH_MODE_JOINT_STEREO)
|
||||
#define BTC_AV_SBC_BLOCK_LEN_ALL (ESP_A2D_SBC_CIE_BLOCK_LEN_4 | \
|
||||
ESP_A2D_SBC_CIE_BLOCK_LEN_8 | \
|
||||
ESP_A2D_SBC_CIE_BLOCK_LEN_12 | \
|
||||
ESP_A2D_SBC_CIE_BLOCK_LEN_16)
|
||||
#define BTC_AV_SBC_NUM_SUBBANDS_ALL (ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | \
|
||||
ESP_A2D_SBC_CIE_NUM_SUBBANDS_8)
|
||||
#define BTC_AV_SBC_ALLOC_MTHD_ALL (ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | \
|
||||
ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS)
|
||||
#define BTC_AV_M24_CH_1_2 (ESP_A2D_M24_CIE_CH_1 | ESP_A2D_M24_CIE_CH_2)
|
||||
|
||||
/*****************************************************************************
|
||||
** Local type definitions
|
||||
******************************************************************************/
|
||||
@@ -1242,9 +1260,15 @@ static bt_status_t btc_av_init(int service_id)
|
||||
|
||||
#if (BTC_AV_EXT_CODEC == TRUE)
|
||||
UINT8 index;
|
||||
UINT8 tsep = (service_id == BTA_A2DP_SINK_SERVICE_ID) ? AVDT_TSEP_SNK : AVDT_TSEP_SRC;
|
||||
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
|
||||
btc_av_cb.codec_caps[index].id = BTC_AV_CODEC_NONE;
|
||||
memset(btc_av_cb.codec_caps[index].info, 0, AVDT_CODEC_SIZE);
|
||||
/* A2DP mandates SBC: fill every SEID with the default SBC capability */
|
||||
if (bta_av_co_audio_build_sbc_default(tsep, &btc_av_cb.codec_caps[index].id,
|
||||
btc_av_cb.codec_caps[index].info) != TRUE) {
|
||||
BTC_TRACE_ERROR("%s: failed to build default SBC for seid %d", __func__, index);
|
||||
btc_av_cb.codec_caps[index].id = BTC_AV_CODEC_NONE;
|
||||
memset(btc_av_cb.codec_caps[index].info, 0, AVDT_CODEC_SIZE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1624,11 +1648,73 @@ tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void)
|
||||
return btc_av_cb.codec_caps;
|
||||
}
|
||||
|
||||
static BOOLEAN btc_av_sep_mcc_caps_valid(BOOLEAN is_sink, const esp_a2d_mcc_t *mcc)
|
||||
{
|
||||
/* Mandatory bits per A2DP v1.4.1 Get All Capabilities (Tables 4.2–4.7, 4.14–4.20). */
|
||||
if (mcc->type == ESP_A2D_MCT_SBC) {
|
||||
const esp_a2d_cie_sbc_t *sbc = &mcc->cie.sbc_info;
|
||||
if (!sbc->samp_freq || !sbc->ch_mode || !sbc->block_len ||
|
||||
!sbc->num_subbands || !sbc->alloc_mthd ||
|
||||
sbc->min_bitpool < A2D_SBC_IE_MIN_BITPOOL || sbc->max_bitpool > A2D_SBC_IE_MAX_BITPOOL ||
|
||||
sbc->max_bitpool < sbc->min_bitpool) {
|
||||
return FALSE;
|
||||
}
|
||||
/* Table 4.4: block length 4/8/12/16 mandatory for SRC and SNK. */
|
||||
if ((sbc->block_len & BTC_AV_SBC_BLOCK_LEN_ALL) != BTC_AV_SBC_BLOCK_LEN_ALL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (is_sink) {
|
||||
/* Tables 4.2, 4.3, 4.5, 4.6, 4.7: 44.1+48, all channel modes,
|
||||
* both subbands, SNR+Loudness, min bitpool 2, max >= HQ JS 44.1. */
|
||||
return ((sbc->samp_freq & (ESP_A2D_SBC_CIE_SF_44K | ESP_A2D_SBC_CIE_SF_48K)) ==
|
||||
(ESP_A2D_SBC_CIE_SF_44K | ESP_A2D_SBC_CIE_SF_48K)) &&
|
||||
((sbc->ch_mode & BTC_AV_SBC_CH_MODE_ALL) == BTC_AV_SBC_CH_MODE_ALL) &&
|
||||
((sbc->num_subbands & BTC_AV_SBC_NUM_SUBBANDS_ALL) == BTC_AV_SBC_NUM_SUBBANDS_ALL) &&
|
||||
((sbc->alloc_mthd & BTC_AV_SBC_ALLOC_MTHD_ALL) == BTC_AV_SBC_ALLOC_MTHD_ALL) &&
|
||||
(sbc->min_bitpool == A2D_SBC_IE_MIN_BITPOOL) &&
|
||||
(sbc->max_bitpool >= BTC_AV_SBC_SNK_HQ_BITPOOL);
|
||||
}
|
||||
/* SRC: Table 4.2 C1 (44.1 or 48), Table 4.3 Mono + C1 stereo family,
|
||||
* Table 4.5 subbands 8, Table 4.6 Loudness. */
|
||||
return ((sbc->samp_freq & (ESP_A2D_SBC_CIE_SF_44K | ESP_A2D_SBC_CIE_SF_48K)) != 0) &&
|
||||
(sbc->ch_mode & ESP_A2D_SBC_CIE_CH_MODE_MONO) &&
|
||||
(sbc->ch_mode & BTC_AV_SBC_CH_MODE_SRC_C1) &&
|
||||
(sbc->num_subbands & ESP_A2D_SBC_CIE_NUM_SUBBANDS_8) &&
|
||||
(sbc->alloc_mthd & ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS);
|
||||
}
|
||||
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
|
||||
if (mcc->type == ESP_A2D_MCT_M24) {
|
||||
const esp_a2d_cie_m24_t *m24 = &mcc->cie.m24_info;
|
||||
/* Table 4.14: MPEG-2 AAC LC mandatory. Table 4.16: DRC not with MPEG-2 AAC LC only. */
|
||||
if (!(m24->obj_type & ESP_A2D_M24_CIE_OBJ_TYPE_2_AAC_LC) ||
|
||||
!(m24->samp_freq1 | m24->samp_freq2) || !m24->ch) {
|
||||
return FALSE;
|
||||
}
|
||||
if (m24->obj_type == ESP_A2D_M24_CIE_OBJ_TYPE_2_AAC_LC && m24->drc) {
|
||||
return FALSE;
|
||||
}
|
||||
if (is_sink) {
|
||||
/* Tables 4.17, 4.18, 4.20: 44.1+48, ch 1+2, VBR. */
|
||||
return (m24->samp_freq1 & ESP_A2D_M24_CIE_SF1_44K) &&
|
||||
(m24->samp_freq2 & ESP_A2D_M24_CIE_SF2_48K) &&
|
||||
((m24->ch & BTC_AV_M24_CH_1_2) == BTC_AV_M24_CH_1_2) &&
|
||||
m24->vbr;
|
||||
}
|
||||
/* SRC: Table 4.17 C1 (44.1 or 48), Table 4.18 C1 (1 or 2 ch). */
|
||||
return ((m24->samp_freq1 & ESP_A2D_M24_CIE_SF1_44K) ||
|
||||
(m24->samp_freq2 & ESP_A2D_M24_CIE_SF2_48K)) &&
|
||||
(m24->ch & BTC_AV_M24_CH_1_2);
|
||||
}
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc)
|
||||
{
|
||||
tBTA_AV_DATA_CBACK *p_data_cback = NULL;
|
||||
esp_a2d_cb_param_t param;
|
||||
tBTA_AV_CODEC codec_type = ESP_A2D_MCT_NON_A2DP;
|
||||
UINT8 index;
|
||||
|
||||
param.a2d_sep_reg_stat.seid = seid;
|
||||
if (btc_av_cb.sm_handle == NULL || btc_sm_get_state(btc_av_cb.sm_handle) != BTC_AV_STATE_IDLE) {
|
||||
@@ -1638,6 +1724,35 @@ static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!btc_av_sep_mcc_caps_valid((tsep == AVDT_TSEP_SNK), mcc)) {
|
||||
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_UNSUPPORTED;
|
||||
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, ¶m);
|
||||
BTC_TRACE_WARNING("%s: refuse seid %d codec 0x%02x, A2DP capability check failed",
|
||||
__func__, seid, mcc->type);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A2DP requires at least one SBC SEP. Reject a non-SBC registration that
|
||||
* would overwrite the last remaining SBC capability.
|
||||
*/
|
||||
if (mcc->type != ESP_A2D_MCT_SBC) {
|
||||
BOOLEAN has_other_sbc = FALSE;
|
||||
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
|
||||
if (index != seid && btc_av_cb.codec_caps[index].id == BTC_AV_CODEC_SBC) {
|
||||
has_other_sbc = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_other_sbc) {
|
||||
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_SBC_REQUIRED;
|
||||
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, ¶m);
|
||||
BTC_TRACE_WARNING("%s: refuse seid %d codec 0x%02x, at least one SBC SEP is required",
|
||||
__func__, seid, mcc->type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (tsep == AVDT_TSEP_SNK) {
|
||||
p_data_cback = bte_av_media_sink_callback;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,31 @@ idf.py menuconfig
|
||||
|
||||
* Choose external I2S codec or internal DAC for audio output, and configure the output PINs under A2DP Sink Internal Codec Example Configuration.
|
||||
|
||||
### Stream Endpoint (SEP) registration
|
||||
|
||||
This example registers codec capabilities with `esp_a2d_sink_register_stream_endpoint()` after A2DP sink init and before connecting. A2DP requires **at least one SBC SEP**; using AAC therefore needs `ESP_A2D_MAX_SEPS >= 2`. Capabilities that do not meet A2DP Profile (SNK) fail with `ESP_A2D_SEP_REG_UNSUPPORTED` in `ESP_A2D_SEP_REG_STATE_EVT`. Overwriting the last SBC SEP fails with `ESP_A2D_SEP_REG_SBC_REQUIRED`.
|
||||
|
||||
**SBC (SNK)**
|
||||
|
||||
| Field | Mandatory |
|
||||
| :---- | :-------- |
|
||||
| Sampling frequency | 44.1 kHz and 48 kHz |
|
||||
| Channel mode | Mono, Dual Channel, Stereo, and Joint Stereo |
|
||||
| Block length | 4, 8, 12, and 16 |
|
||||
| Subbands | 4 and 8 |
|
||||
| Allocation method | SNR and Loudness |
|
||||
| Bitpool | min = 2; max ≥ 53 (High Quality Joint Stereo 44.1 kHz, A2DP Table 4.7); max ≤ 250 |
|
||||
|
||||
**AAC (SNK)**
|
||||
|
||||
| Field | Mandatory |
|
||||
| :---- | :-------- |
|
||||
| Object type | MPEG-2 AAC LC |
|
||||
| MPEG-D DRC | Must be 0 if only MPEG-2 AAC LC is advertised |
|
||||
| Sampling frequency | 44.1 kHz and 48 kHz |
|
||||
| Channels | 1 and 2 |
|
||||
| VBR | Supported |
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output.
|
||||
|
||||
@@ -28,6 +28,31 @@ idf.py menuconfig
|
||||
|
||||
* Enable Classic Bluetooth and A2DP under Component config --> Bluetooth --> Bluedroid Enable
|
||||
|
||||
### Stream Endpoint (SEP) registration
|
||||
|
||||
This example registers codec capabilities with `esp_a2d_source_register_stream_endpoint()` after A2DP source init and before connecting. A2DP requires **at least one SBC SEP**; using AAC therefore needs `ESP_A2D_MAX_SEPS >= 2`. Capabilities that do not meet A2DP Profile (SRC) fail with `ESP_A2D_SEP_REG_UNSUPPORTED` in `ESP_A2D_SEP_REG_STATE_EVT`. Overwriting the last SBC SEP fails with `ESP_A2D_SEP_REG_SBC_REQUIRED`.
|
||||
|
||||
**SBC (SRC)**
|
||||
|
||||
| Field | Mandatory |
|
||||
| :---- | :-------- |
|
||||
| Sampling frequency | At least one of 44.1 kHz and 48 kHz |
|
||||
| Channel mode | Mono, and at least one of Dual Channel / Stereo / Joint Stereo |
|
||||
| Block length | 4, 8, 12, and 16 |
|
||||
| Subbands | 8 |
|
||||
| Allocation method | Loudness |
|
||||
| Bitpool | min 2–250, max 2–250, min ≤ max |
|
||||
|
||||
**AAC (SRC)**
|
||||
|
||||
| Field | Mandatory |
|
||||
| :---- | :-------- |
|
||||
| Object type | MPEG-2 AAC LC |
|
||||
| MPEG-D DRC | Must be 0 if only MPEG-2 AAC LC is advertised |
|
||||
| Sampling frequency | At least one of 44.1 kHz and 48 kHz |
|
||||
| Channels | At least one of 1 and 2 |
|
||||
| VBR | Optional |
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output.
|
||||
|
||||
Reference in New Issue
Block a user