diff --git a/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h b/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h index cf8165c7f32..7ebb72ee270 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h @@ -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 diff --git a/components/bt/host/bluedroid/bta/av/bta_av_m24.c b/components/bt/host/bluedroid/bta/av/bta_av_m24.c index fc5330e302b..3939b94682d 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_m24.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_m24.c @@ -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; } diff --git a/components/bt/host/bluedroid/bta/av/bta_av_main.c b/components/bt/host/bluedroid/bta/av/bta_av_main.c index 16b428edd69..95694306b2a 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_main.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_main.c @@ -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) { diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h index 1a1def9a036..111be266046 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h @@ -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 diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h index a67eb0c3711..5754f7abff6 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h @@ -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 diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c index 6f94a19d997..bd816748c63 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c @@ -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 diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c index 3ad23fb1404..6e6d5d99c41 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -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; } diff --git a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/README.md b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/README.md index b7677198539..4f2db53956d 100644 --- a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/README.md @@ -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. diff --git a/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/README.md b/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/README.md index 83915e46f29..1f3686deb9b 100644 --- a/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/README.md @@ -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.