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_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/btc/profile/std/a2dp/bta_av_co.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c index 874bc0945ee..bdada89a80e 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 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..e7cc2f2fdf5 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 @@ -1242,9 +1242,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 @@ -1629,6 +1635,7 @@ 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 +1645,27 @@ static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc) 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; }