feat(bt/bluedroid): Add API to set encoder parameters for A2DP source

- Add API to set encoder parameters for A2DP source
- Add the use of the API for setting encoding parameters in the a2dp_source example
This commit is contained in:
yangfeng
2026-01-12 16:41:21 +08:00
parent bc753bb88b
commit e83cf43b6a
10 changed files with 733 additions and 32 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -349,6 +349,35 @@ esp_err_t esp_a2d_source_init(void)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d_mcc_t *pref_mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
if (conn_hdl == 0 || pref_mcc == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SRC_API_SET_PREF_MCC_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.set_pref_mcc.conn_hdl = conn_hdl;
memcpy(&arg.set_pref_mcc.pref_mcc, pref_mcc, sizeof(esp_a2d_mcc_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -243,6 +243,7 @@ typedef enum {
ESP_A2D_SNK_GET_DELAY_VALUE_EVT, /*!< indicate a2dp sink get delay report value complete, only used for A2DP SINK */
ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, /*!< report delay value, only used for A2DP SRC */
ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT, /*!< report sink codec capabilities, only used for A2DP SRC */
ESP_A2D_SRC_SET_PREF_MCC_EVT, /*!< indicate a2dp source set preferred media codec configuration status, only used for A2DP SRC */
} esp_a2d_cb_event_t;
/**
@@ -349,6 +350,16 @@ typedef union {
esp_a2d_mcc_t mcc; /*!< A2DP sink media codec capability information */
} a2d_report_snk_codec_caps_stat; /*!< A2DP source received sink codec capabilities */
/**
* @brief ESP_A2D_SRC_SET_PREF_MCC_EVT
*/
struct a2d_set_pref_mcc_param {
esp_bt_status_t set_status; /*!< set status.
@note Possible values: ESP_BT_STATUS_SUCCESS, ESP_BT_STATUS_FAIL,
ESP_BT_STATUS_NOT_READY, ESP_BT_STATUS_BUSY, ESP_BT_STATUS_UNSUPPORTED. */
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
} a2d_set_pref_mcc_stat; /*!< A2DP source set preferred media codec configuration */
} esp_a2d_cb_param_t;
/**
@@ -570,6 +581,23 @@ esp_err_t esp_a2d_get_profile_status(esp_a2d_profile_status_t *profile_status);
*/
esp_err_t esp_a2d_source_init(void);
/**
*
* @brief Set the preferred A2DP source media codec configuration after establishing A2DP connection
* and before starting A2DP stream.
*
* @param[in] conn_hdl : connection handle
* @param[in] pref_mcc : preferred media codec configuration
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_source_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d_mcc_t *pref_mcc);
/**
* @brief Register a a2dp source Stream Endpoint (SEP) with specific codec capability, shall register
* SEP after a2dp source initializing and before a2dp connection establishing. Register the same
@@ -121,6 +121,7 @@ static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT
static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg);
static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg);
static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index);
static BOOLEAN bta_av_co_audio_src_supports_pref_cfg(void);
/*******************************************************************************
@@ -1409,6 +1410,28 @@ static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8
return status;
}
/*******************************************************************************
**
** Function bta_av_co_audio_src_supports_pref_cfg
**
** Description Check if the media source codec capability supports the preferred configuration
**
** Returns TRUE if the media source supports preferred config, FALSE otherwise
**
*******************************************************************************/
static BOOLEAN bta_av_co_audio_src_supports_pref_cfg(void)
{
#if (BTC_AV_EXT_CODEC == TRUE)
return (bta_av_co_cb.codec_pref_cfg.id == bta_av_co_cb.codec_caps.id &&
bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
(UINT8 *)&bta_av_co_cb.codec_caps.info) == A2D_SUCCESS);
#else
return (bta_av_co_cb.codec_pref_cfg.id == BTC_AV_CODEC_SBC &&
bta_av_sbc_cfg_in_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
(tA2D_SBC_CIE *)&bta_av_co_sbc_caps) == A2D_SUCCESS);
#endif
}
/*******************************************************************************
**
** Function bta_av_co_audio_codec_supported
@@ -1493,6 +1516,219 @@ BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status)
return TRUE;
}
/*******************************************************************************
**
** Function bta_av_co_audio_set_pref_mcc
**
** Description Set preferred codec configuration for a specific connection
** and initiate reconfiguration if the connection is open.
**
** Note: This function returns TRUE if the preferred config is
** supported. The actual success
** or failure of reconfiguration will be reported asynchronously
** via BTA_AV_RECONFIG_EVT.
**
** Returns TRUE if the preferred config is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_av_co_audio_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref_mcc)
{
#if (BTC_AV_SRC_INCLUDED == TRUE)
tBTA_AV_CO_PEER *p_peer;
tBTA_AV_CO_SINK *p_sink;
UINT8 codec_cfg[AVDT_CODEC_SIZE];
UINT8 num_protect = 0;
UINT8 snk_index;
BOOLEAN pref_cfg_supported = FALSE;
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
BOOLEAN cp_active;
#endif
if (pref_mcc) {
bta_av_co_cb.codec_pref_cfg.id = pref_mcc->id;
memcpy(bta_av_co_cb.codec_pref_cfg.info, pref_mcc->info, AVDT_CODEC_SIZE);
pref_cfg_supported = bta_av_co_audio_src_supports_pref_cfg();
if (pref_cfg_supported) {
/* Retrieve the peer info for the specified handle */
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL) {
APPL_TRACE_ERROR("bta_av_co_audio_set_pref_mcc could not find peer entry for handle 0x%x", hndl);
return FALSE;
}
if (!p_peer->opened) {
APPL_TRACE_WARNING("bta_av_co_audio_set_pref_mcc connection 0x%x is not opened", hndl);
return FALSE;
}
/* Update the current codec configuration */
bta_av_co_audio_codec_reset();
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_lock();
/* Try to use the currently selected sink first. */
if (p_peer->p_snk != NULL &&
p_peer->p_snk->codec_type == bta_av_co_cb.codec_cfg.id &&
bta_av_co_audio_codec_match(p_peer->p_snk->codec_caps)) {
/* Current sink supports the new preferred config, use it */
p_sink = p_peer->p_snk;
} else if (bta_av_co_audio_peer_supports_codec(p_peer, &snk_index)) {
/* Current sink doesn't support, find another one that does */
p_sink = &p_peer->snks[snk_index];
/* Check that this sink is compatible with the CP */
if (!bta_av_co_audio_sink_supports_cp(p_sink)) {
APPL_TRACE_DEBUG("bta_av_co_audio_set_pref_mcc connection 0x%x sink %d doesn't support cp", hndl, snk_index);
osi_mutex_global_unlock();
bta_av_co_audio_clear_pref_mcc(hndl);
return FALSE;
}
} else {
/* No sink supports the new preferred config */
APPL_TRACE_DEBUG("bta_av_co_audio_set_pref_mcc connection 0x%x has no sink supporting preferred config", hndl);
osi_mutex_global_unlock();
bta_av_co_audio_clear_pref_mcc(hndl);
return FALSE;
}
/* Build the codec configuration for this sink */
if (bta_av_co_audio_codec_build_config(p_sink->codec_caps, codec_cfg)) {
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
cp_active = bta_av_co_audio_sink_has_scmst(p_sink);
#endif
/* Check if configuration actually changed:
* 1. Sink changed
* 2. Codec configuration changed
* 3. CP state changed (if applicable)
*/
if (p_sink != p_peer->p_snk || memcmp(codec_cfg, p_peer->codec_cfg, AVDT_CODEC_SIZE)
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
|| (p_peer->cp_active != cp_active)
#endif
) {
/* Update peer configuration with the new codec config based on preferred config. */
p_peer->p_snk = p_sink;
memcpy(p_peer->codec_cfg, codec_cfg, AVDT_CODEC_SIZE);
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
p_peer->cp_active = cp_active;
bta_av_co_cb.cp.active = cp_active;
if (cp_active) {
num_protect = BTA_AV_CP_INFO_LEN;
}
#endif
/* Reconfigure the connection to apply preferred codec configuration.
* This is an asynchronous operation. Success/failure will be reported
* via BTA_AV_RECONFIG_EVT. */
APPL_TRACE_DEBUG("bta_av_co_audio_set_pref_mcc call BTA_AvReconfig(0x%x) to apply preferred config", hndl);
p_peer->pref_mcc_reconfig_initiated = TRUE;
BTA_AvReconfig(hndl, TRUE, p_sink->sep_info_idx, p_peer->codec_cfg, num_protect, (UINT8 *)bta_av_co_cp_scmst);
} else {
/* Configuration hasn't changed, no need to reconfigure. */
APPL_TRACE_DEBUG("bta_av_co_audio_set_pref_mcc no change, hndl:0x%x", hndl);
p_peer->pref_mcc_reconfig_initiated = FALSE;
osi_mutex_global_unlock();
/* Return TRUE to indicate preferred config is set. */
return TRUE;
}
} else {
APPL_TRACE_ERROR("bta_av_co_audio_set_pref_mcc build cfg failed, hndl:0x%x", hndl);
osi_mutex_global_unlock();
bta_av_co_audio_clear_pref_mcc(hndl);
return FALSE;
}
osi_mutex_global_unlock();
}
return pref_cfg_supported;
} else {
APPL_TRACE_ERROR("bta_av_co_audio_set_pref_mcc pref_mcc NULL");
return FALSE;
}
#else
UNUSED(hndl);
UNUSED(pref_mcc);
return FALSE;
#endif /* BTC_AV_SRC_INCLUDED == TRUE */
}
/*******************************************************************************
**
** Function bta_av_co_audio_clear_pref_mcc
**
** Description Clear the preferred codec configuration
**
** Returns void
**
*******************************************************************************/
void bta_av_co_audio_clear_pref_mcc(tBTA_AV_HNDL hndl)
{
UNUSED(hndl);
#if (BTC_AV_SRC_INCLUDED == TRUE)
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
memset(bta_av_co_cb.codec_pref_cfg.info, 0, AVDT_CODEC_SIZE);
/* reset codec configuration */
bta_av_co_audio_codec_reset();
#endif
}
/*******************************************************************************
**
** Function bta_av_co_audio_pref_mcc_reconfig_initiated
**
** Description Check if a reconfig was actually initiated by the last call
** to bta_av_co_audio_set_pref_mcc(). This is used to determine
** if we need to wait for BTA_AV_RECONFIG_EVT or can notify
** immediately (when config unchanged).
**
** Returns TRUE if reconfig was initiated, FALSE if config unchanged
**
*******************************************************************************/
BOOLEAN bta_av_co_audio_pref_mcc_reconfig_initiated(tBTA_AV_HNDL hndl)
{
#if (BTC_AV_SRC_INCLUDED == TRUE)
tBTA_AV_CO_PEER *p_peer;
/* Retrieve the peer info for the specified handle */
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL || !p_peer->opened) {
return FALSE;
}
return p_peer->pref_mcc_reconfig_initiated;
#else
UNUSED(hndl);
return FALSE;
#endif /* BTC_AV_SRC_INCLUDED == TRUE */
}
/*******************************************************************************
**
** Function bta_av_co_audio_pref_mcc_reconfig_clear
**
** Description Clear the pref_mcc_reconfig_initiated flag. This should be
** called after processing a BTA_AV_RECONFIG_EVT for preferred
** codec config change.
**
** Returns void
**
*******************************************************************************/
void bta_av_co_audio_pref_mcc_reconfig_clear(tBTA_AV_HNDL hndl)
{
#if (BTC_AV_SRC_INCLUDED == TRUE)
tBTA_AV_CO_PEER *p_peer;
/* Retrieve the peer info for the specified handle */
p_peer = bta_av_co_get_peer(hndl);
if (p_peer != NULL) {
p_peer->pref_mcc_reconfig_initiated = FALSE;
}
#else
UNUSED(hndl);
#endif /* BTC_AV_SRC_INCLUDED == TRUE */
}
/*******************************************************************************
**
** Function bta_av_co_audio_codec_reset
@@ -1504,20 +1740,48 @@ BOOLEAN bta_av_co_audio_codec_supported(tBTC_AV_STATUS *p_status)
*******************************************************************************/
void bta_av_co_audio_codec_reset(void)
{
BOOLEAN pref_cfg_supported;
osi_mutex_global_lock();
FUNC_TRACE();
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.codec_cfg.id = bta_av_co_cb.codec_caps.id;
bta_av_build_src_cfg(bta_av_co_cb.codec_cfg.info, bta_av_co_cb.codec_caps.info);
#else
/* Reset the current configuration to SBC */
bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC;
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, bta_av_co_cb.codec_cfg.info) != A2D_SUCCESS) {
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset A2D_BldSbcInfo failed");
pref_cfg_supported = FALSE;
#if (BTC_AV_SRC_INCLUDED == TRUE)
switch (bta_av_co_cb.codec_pref_cfg.id) {
case BTC_AV_CODEC_NONE: {
APPL_TRACE_DEBUG("bta_av_co_audio_codec_reset preferred codec configuration is not set");
break;
}
case BTC_AV_CODEC_SBC: {
if (bta_av_co_audio_src_supports_pref_cfg()) {
pref_cfg_supported = TRUE;
} else {
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset unsupported pref cfg");
}
break;
}
default:
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset bad pref cfg id %d", bta_av_co_cb.codec_pref_cfg.id);
break;
}
#endif
if (pref_cfg_supported) {
bta_av_co_cb.codec_cfg = bta_av_co_cb.codec_pref_cfg;
} else {
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.codec_cfg.id = bta_av_co_cb.codec_caps.id;
bta_av_build_src_cfg(bta_av_co_cb.codec_cfg.info, bta_av_co_cb.codec_caps.info);
#else
/* Reset the current configuration to SBC */
bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC;
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, bta_av_co_cb.codec_cfg.info) != A2D_SUCCESS) {
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset A2D_BldSbcInfo failed");
}
#endif
}
osi_mutex_global_unlock();
}
@@ -1549,7 +1813,15 @@ BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_
case BTC_AV_CODEC_PCM:
new_cfg.id = BTC_AV_CODEC_SBC;
sbc_config = btc_av_sbc_default_config;
if (bta_av_co_audio_src_supports_pref_cfg()) {
if (A2D_ParsSbcInfo((tA2D_SBC_CIE *)&sbc_config, bta_av_co_cb.codec_pref_cfg.info, FALSE) != A2D_SUCCESS) {
APPL_TRACE_ERROR("bta_av_co_audio_set_codec A2D_ParsSbcInfo failed");
return FALSE;
}
} else {
sbc_config = btc_av_sbc_default_config;
}
if ((p_feeding->cfg.pcm.num_channel != 1) &&
(p_feeding->cfg.pcm.num_channel != 2)) {
APPL_TRACE_ERROR("bta_av_co_audio_set_codec PCM channel number unsupported");
@@ -1561,11 +1833,16 @@ BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_
return FALSE;
}
switch (p_feeding->cfg.pcm.sampling_freq) {
/* SBC supports 16/32/44.1/48kHz */
case 16000:
sbc_config.samp_freq = A2D_SBC_IE_SAMP_FREQ_16;
break;
case 32000:
sbc_config.samp_freq = A2D_SBC_IE_SAMP_FREQ_32;
break;
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 48000:
sbc_config.samp_freq = A2D_SBC_IE_SAMP_FREQ_48;
break;
@@ -1708,6 +1985,7 @@ void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps)
if (codec_caps) {
memcpy(&bta_av_co_cb.codec_caps, codec_caps, sizeof(tBTC_AV_CODEC_INFO));
}
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -430,17 +430,44 @@ void btc_a2dp_source_setup_codec(void)
{
tBTC_AV_MEDIA_FEEDINGS media_feeding;
tBTC_AV_STATUS status;
UINT16 minmtu;
tA2D_SBC_CIE sbc_config;
APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n");
osi_mutex_global_lock();
/* for now hardcode 44.1 khz 16 bit stereo PCM format */
media_feeding.cfg.pcm.sampling_freq = 44100;
bta_av_co_audio_get_sbc_config(&sbc_config, &minmtu);
/* derive PCM feeding from SBC configuration */
switch (sbc_config.samp_freq) {
case A2D_SBC_IE_SAMP_FREQ_48:
media_feeding.cfg.pcm.sampling_freq = 48000;
break;
case A2D_SBC_IE_SAMP_FREQ_32:
media_feeding.cfg.pcm.sampling_freq = 32000;
break;
case A2D_SBC_IE_SAMP_FREQ_16:
media_feeding.cfg.pcm.sampling_freq = 16000;
break;
case A2D_SBC_IE_SAMP_FREQ_44:
media_feeding.cfg.pcm.sampling_freq = 44100;
break;
default:
media_feeding.cfg.pcm.sampling_freq = 44100;
APPL_TRACE_WARNING("btc_a2dp_source_setup_codec unsupported SBC sampling frequency: %d", sbc_config.samp_freq);
break;
}
media_feeding.cfg.pcm.num_channel = (sbc_config.ch_mode == A2D_SBC_IE_CH_MD_MONO) ? 1 : 2;
media_feeding.cfg.pcm.bit_per_sample = 16;
media_feeding.cfg.pcm.num_channel = 2;
media_feeding.format = BTC_AV_CODEC_PCM;
APPL_TRACE_DEBUG("btc_a2dp_source_setup_codec PCM feeding derived from SBC: freq=%d, bits=%d, ch=%d",
media_feeding.cfg.pcm.sampling_freq,
media_feeding.cfg.pcm.bit_per_sample,
media_feeding.cfg.pcm.num_channel);
if (bta_av_co_audio_set_codec(&media_feeding, &status)) {
tBTC_MEDIA_INIT_AUDIO_FEEDING mfeed;
@@ -970,11 +997,29 @@ static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feedin
/* Check the PCM feeding sampling_freq */
switch (p_feeding->feeding.cfg.pcm.sampling_freq) {
case 16000:
/* For this sampling_freq the AV connection must be 16000 */
if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf16000) {
/* Reconfiguration needed at 16000 */
APPL_TRACE_DEBUG("SBC Reconfiguration needed at 16000");
a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf16000;
reconfig_needed = TRUE;
}
break;
case 32000:
/* For this sampling_freq the AV connection must be 32000 */
if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf32000) {
/* Reconfiguration needed at 32000 */
APPL_TRACE_DEBUG("SBC Reconfiguration needed at 32000");
a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq = SBC_sf32000;
reconfig_needed = TRUE;
}
break;
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 48000:
/* For these sampling_freq the AV connection must be 48000 */
if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16SamplingFreq != SBC_sf48000) {
@@ -1001,13 +1046,6 @@ static void btc_a2dp_source_pcm2sbc_init(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_feedin
break;
}
/* Some AV Headsets do not support Mono => always ask for Stereo */
if (a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode == SBC_MONO) {
APPL_TRACE_DEBUG("SBC Reconfiguration needed in Stereo");
a2dp_source_local_param.btc_aa_src_cb.encoder.s16ChannelMode = SBC_JOINT_STEREO;
reconfig_needed = TRUE;
}
if (reconfig_needed != FALSE) {
APPL_TRACE_DEBUG("%s :: mtu %d", __FUNCTION__, a2dp_source_local_param.btc_aa_src_cb.TxAaMtuSize);
APPL_TRACE_DEBUG("ch mode %d, nbsubd %d, nb %d, alloc %d, rate %d, freq %d",
@@ -1642,4 +1680,18 @@ static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context)
a2dp_source_local_param.btc_aa_src_cb.poll_data = NULL;
}
/*******************************************************************************
**
** Function btc_a2dp_source_set_pref_mcc
**
** Description
**
** Returns TRUE if the preferred config is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref_mcc)
{
return bta_av_co_audio_set_pref_mcc(hndl, pref_mcc);
}
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE) */
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -232,4 +232,18 @@ void btc_a2dp_source_shutdown(void)
#endif
}
/*******************************************************************************
**
** Function btc_a2dp_source_set_pref_mcc
**
** Description
**
** Returns TRUE if the preferred config is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref_mcc)
{
return bta_av_co_audio_set_pref_mcc(hndl, pref_mcc);
}
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE) */
@@ -96,6 +96,7 @@ typedef struct {
UINT8 peer_sep; /* sep type of peer device */
tBTC_AV_CODEC_INFO codec_caps;
#if BTC_AV_SRC_INCLUDED
tBTC_AV_CODEC_INFO pref_mcc; /* preferred media codec configuration */
osi_alarm_t *tle_av_open_on_rc;
#endif /* BTC_AV_SRC_INCLUDED */
} btc_av_cb_t;
@@ -152,6 +153,7 @@ static BOOLEAN btc_a2d_deinit_if_ongoing(void);
#if BTC_AV_SRC_INCLUDED
static bt_status_t btc_a2d_src_init(void);
static void btc_a2d_src_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_mcc_t *pref_mcc);
static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda);
static void btc_a2d_src_deinit(void);
#endif /* BTC_AV_SRC_INCLUDED */
@@ -837,6 +839,15 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
/* pending start flag will be cleared when exit current state */
}
/* Check if this connection has a pending preferred config change */
if (bta_av_co_audio_pref_mcc_reconfig_initiated(btc_av_cb.bta_handle)) {
bta_av_co_audio_pref_mcc_reconfig_clear(btc_av_cb.bta_handle);
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_FAIL;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
BTC_TRACE_DEBUG("pref cfg pending, closed, h:0x%x", btc_av_cb.bta_handle);
}
/* change state to idle, send acknowledgement if start is pending */
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
break;
@@ -851,6 +862,24 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START;
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
}
/* Handle user-initiated preferred codec config change.
* Check if this reconfig was triggered by preferred codec config change */
if (bta_av_co_audio_pref_mcc_reconfig_initiated(p_av->reconfig.hndl)) {
bta_av_co_audio_pref_mcc_reconfig_clear(p_av->reconfig.hndl);
if (p_av->reconfig.status == BTA_AV_SUCCESS) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_SUCCESS;
BTC_TRACE_DEBUG("pref cfg reconfig ok, h:0x%x", p_av->reconfig.hndl);
} else {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_FAIL;
BTC_TRACE_DEBUG("pref cfg reconfig fail, h:0x%x st:%d",
p_av->reconfig.hndl, p_av->reconfig.status);
bta_av_co_audio_clear_pref_mcc(p_av->reconfig.hndl);
}
param.a2d_set_pref_mcc_stat.conn_hdl = p_av->reconfig.hndl;
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
break;
case BTC_AV_CONNECT_REQ_EVT:
@@ -1745,6 +1774,10 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_a2d_src_init();
break;
}
case BTC_AV_SRC_API_SET_PREF_MCC_EVT: {
btc_a2d_src_set_pref_mcc(arg->set_pref_mcc.conn_hdl, &arg->set_pref_mcc.pref_mcc);
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SRC_API_REG_SEP_EVT: {
btc_av_reg_sep(AVDT_TSEP_SRC, arg->reg_sep.seid, &arg->reg_sep.mcc);
@@ -1923,6 +1956,86 @@ static void btc_a2d_src_deinit(void)
}
}
static void btc_a2d_src_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_mcc_t *pref_mcc)
{
UNUSED(conn_hdl);
UNUSED(pref_mcc);
#if A2D_DYNAMIC_MEMORY == TRUE
if (btc_av_cb_ptr)
#endif
{
esp_a2d_cb_param_t param;
if (btc_av_cb.sm_handle == NULL || btc_sm_get_state(btc_av_cb.sm_handle) != BTC_AV_STATE_OPENED) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_NOT_READY;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc not ready");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
return;
}
if (conn_hdl != btc_av_cb.bta_handle) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc bad hndl %d", conn_hdl);
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
return;
}
if (bta_av_co_audio_pref_mcc_reconfig_initiated(conn_hdl)) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_BUSY;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc reconfig busy");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
return;
}
switch (pref_mcc->type) {
case ESP_A2D_MCT_SBC: {
btc_av_cb.pref_mcc.id = BTC_AV_CODEC_SBC;
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, btc_av_cb.pref_mcc.info) == A2D_SUCCESS) {
/* overwrite sbc cie */
memcpy(btc_av_cb.pref_mcc.info + A2D_SBC_CIE_OFF, &pref_mcc->cie, A2D_SBC_CIE_LEN);
/* Note: Return value only indicates if the config is supported and reconfig is initiated.
* Actual success/failure will be reported via ESP_A2D_SRC_SET_PREF_MCC_EVT
* when BTA_AV_RECONFIG_EVT is received. */
if (!btc_a2dp_source_set_pref_mcc(conn_hdl, &btc_av_cb.pref_mcc)) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc bad params");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
} else {
/* Check if reconfig was actually initiated */
if (!bta_av_co_audio_pref_mcc_reconfig_initiated(conn_hdl)) {
/* Configuration unchanged, no reconfig needed.
* Preferred config is already in use, notify success immediately. */
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_SUCCESS;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc already active");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
}
} else {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_FAIL;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc build SBC info failed");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
break;
}
default: {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc bad codec type %d", pref_mcc->type);
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
break;
}
}
}
}
static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda)
{
BTC_TRACE_DEBUG("%s\n", __FUNCTION__);
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -54,6 +54,7 @@ typedef struct {
UINT16 mtu; /* maximum transmit unit size */
UINT16 uuid_to_connect; /* uuid of peer device */
BOOLEAN got_disc_res; /* got the results of initiating discovery */
BOOLEAN pref_mcc_reconfig_initiated; /* TRUE if last bta_av_co_audio_set_pref_mcc() initiated reconfig for this peer */
} tBTA_AV_CO_PEER;
typedef struct {
@@ -67,6 +68,7 @@ typedef struct {
tBTC_AV_CODEC_INFO codec_caps;
/* Current codec configuration - access to this variable must be protected */
tBTC_AV_CODEC_INFO codec_cfg;
tBTC_AV_CODEC_INFO codec_pref_cfg; /* preferred media codec configuration for source */
tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */
tBTA_AV_CO_CP cp;
@@ -122,6 +124,61 @@ UINT8 bta_av_co_cp_get_flag(void);
*******************************************************************************/
BOOLEAN bta_av_co_cp_set_flag(UINT8 cp_flag);
/*******************************************************************************
**
** Function bta_av_co_audio_set_pref_mcc
**
** Description Set preferred codec configuration for a specific connection
** and initiate reconfiguration if the connection is open.
**
** Note: This function returns TRUE if the preferred config is
** supported. The actual success
** or failure of reconfiguration will be reported asynchronously
** via BTA_AV_RECONFIG_EVT.
**
** Returns TRUE if the preferred config is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_av_co_audio_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref_mcc);
/*******************************************************************************
**
** Function bta_av_co_audio_clear_pref_mcc
**
** Description Clear the preferred codec configuration
**
** Returns void
**
*******************************************************************************/
void bta_av_co_audio_clear_pref_mcc(tBTA_AV_HNDL hndl);
/*******************************************************************************
**
** Function bta_av_co_audio_pref_mcc_reconfig_initiated
**
** Description Check if a reconfig was actually initiated by the last call
** to bta_av_co_audio_set_pref_mcc(). This is used to determine
** if we need to wait for BTA_AV_RECONFIG_EVT or can notify
** immediately (when config unchanged).
**
** Returns TRUE if reconfig was initiated, FALSE if config unchanged
**
*******************************************************************************/
BOOLEAN bta_av_co_audio_pref_mcc_reconfig_initiated(tBTA_AV_HNDL hndl);
/*******************************************************************************
**
** Function bta_av_co_audio_pref_mcc_reconfig_clear
**
** Description Clear the pref_mcc_reconfig_initiated flag for a specific
** connection. This should be called after processing a
** BTA_AV_RECONFIG_EVT for preferred codec config change.
**
** Returns void
**
*******************************************************************************/
void bta_av_co_audio_pref_mcc_reconfig_clear(tBTA_AV_HNDL hndl);
/*******************************************************************************
**
** Function bta_av_co_audio_codec_reset
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -150,6 +150,17 @@ void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av);
*******************************************************************************/
void btc_a2dp_source_set_tx_flush(BOOLEAN enable);
/*******************************************************************************
**
** Function btc_a2dp_source_set_pref_mcc
**
** Description set preferred codec configuration
**
** Returns TRUE if the preferred config is supported, FALSE otherwise
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref_mcc);
#if (BTC_AV_EXT_CODEC == FALSE)
/*******************************************************************************
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -78,6 +78,7 @@ typedef enum {
#endif /* BTC_AV_SINK_INCLUDED */
#if BTC_AV_SRC_INCLUDED
BTC_AV_SRC_API_INIT_EVT,
BTC_AV_SRC_API_SET_PREF_MCC_EVT,
BTC_AV_SRC_API_REG_SEP_EVT,
BTC_AV_SRC_API_DEINIT_EVT,
BTC_AV_SRC_API_CONNECT_EVT,
@@ -108,6 +109,11 @@ typedef union {
bt_bdaddr_t src_connect;
// BTC_AV_SRC_API_DISCONNECT_EVT
bt_bdaddr_t src_disconn;
// BTC_AV_SRC_API_SET_PREF_MCC_EVT
struct {
esp_a2d_conn_hdl_t conn_hdl;
esp_a2d_mcc_t pref_mcc;
} set_pref_mcc;
#endif /* BTC_AV_SRC_INCLUDED */
// BTC_AV_CONFIG_EVT
esp_a2d_mcc_t mcc;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -91,6 +91,12 @@ static void bt_app_av_sm_hdlr(uint16_t event, void *param);
/* utils for transfer BLuetooth Deveice Address into string form */
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size);
/* check preferred codec configuration against sink capabilities */
static bool check_pref_mcc_against_sink_caps(const esp_a2d_mcc_t *sink_caps, const esp_a2d_mcc_t *pref_mcc);
/* set preferred codec configuration */
static void bt_app_a2d_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d_mcc_t *sink_caps);
/* A2DP application state machine handler for each state */
static void bt_app_av_state_unconnected_hdlr(uint16_t event, void *param);
static void bt_app_av_state_connecting_hdlr(uint16_t event, void *param);
@@ -404,6 +410,77 @@ static void bt_app_av_sm_hdlr(uint16_t event, void *param)
}
}
static bool is_one_bit_set_u8(uint8_t v)
{
return (v != 0) && ((v & (uint8_t)(v - 1)) == 0);
}
static bool check_pref_mcc_against_sink_caps(const esp_a2d_mcc_t *sink_caps, const esp_a2d_mcc_t *pref_mcc)
{
const esp_a2d_cie_sbc_t *caps;
const esp_a2d_cie_sbc_t *cfg;
if (sink_caps == NULL || pref_mcc == NULL) {
return false;
}
if (sink_caps->type != pref_mcc->type) {
return false;
}
if (pref_mcc->type != ESP_A2D_MCT_SBC) {
return false;
}
caps = &sink_caps->cie.sbc_info;
cfg = &pref_mcc->cie.sbc_info;
/* For preferred configuration, each field should select a single value (one bit) */
if (!is_one_bit_set_u8(cfg->samp_freq) || ((cfg->samp_freq & caps->samp_freq) != cfg->samp_freq)) {
return false;
}
if (!is_one_bit_set_u8(cfg->ch_mode) || ((cfg->ch_mode & caps->ch_mode) != cfg->ch_mode)) {
return false;
}
if (!is_one_bit_set_u8(cfg->block_len) || ((cfg->block_len & caps->block_len) != cfg->block_len)) {
return false;
}
if (!is_one_bit_set_u8(cfg->num_subbands) || ((cfg->num_subbands & caps->num_subbands) != cfg->num_subbands)) {
return false;
}
if (!is_one_bit_set_u8(cfg->alloc_mthd) || ((cfg->alloc_mthd & caps->alloc_mthd) != cfg->alloc_mthd)) {
return false;
}
if (cfg->min_bitpool < caps->min_bitpool || cfg->max_bitpool > caps->max_bitpool || cfg->min_bitpool > cfg->max_bitpool) {
return false;
}
return true;
}
static void bt_app_a2d_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d_mcc_t *sink_caps)
{
esp_a2d_mcc_t pref_mcc;
memset(&pref_mcc, 0, sizeof(pref_mcc));
pref_mcc.type = ESP_A2D_MCT_SBC;
pref_mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_44K;
pref_mcc.cie.sbc_info.ch_mode = ESP_A2D_SBC_CIE_CH_MODE_MONO; // Joint Stereo --> Mono
pref_mcc.cie.sbc_info.block_len = ESP_A2D_SBC_CIE_BLOCK_LEN_16;
pref_mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_8;
pref_mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS;
pref_mcc.cie.sbc_info.min_bitpool = 2;
pref_mcc.cie.sbc_info.max_bitpool = 35; // 53 --> 35
if (!check_pref_mcc_against_sink_caps(sink_caps, &pref_mcc)) {
ESP_LOGW(BT_AV_TAG, "pref_mcc not supported by sink");
return;
}
esp_err_t ret = esp_a2d_source_set_pref_mcc(conn_hdl, &pref_mcc);
ESP_LOGD(BT_AV_TAG, "Set pref_mcc result: %s", esp_err_to_name(ret));
}
static void bt_app_av_state_unconnected_hdlr(uint16_t event, void *param)
{
esp_a2d_cb_param_t *a2d = NULL;
@@ -581,6 +658,30 @@ static void bt_app_av_state_connected_hdlr(uint16_t event, void *param)
ESP_LOGI(BT_AV_TAG, "%s, delay value: %u * 1/10 ms", __func__, a2d->a2d_report_delay_value_stat.delay_value);
break;
}
case ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT: {
a2d = (esp_a2d_cb_param_t *)(param);
esp_a2d_mcc_t *sink_mcc = &a2d->a2d_report_snk_codec_caps_stat.mcc;
ESP_LOGI(BT_AV_TAG, "sink codec type: %d", sink_mcc->type);
/* for now only SBC stream is supported */
if (sink_mcc->type == ESP_A2D_MCT_SBC) {
ESP_LOGI(BT_AV_TAG, "sink codec capabilities: 0x%x-0x%x-0x%x-0x%x-0x%x-%d-%d",
sink_mcc->cie.sbc_info.samp_freq,
sink_mcc->cie.sbc_info.ch_mode,
sink_mcc->cie.sbc_info.block_len,
sink_mcc->cie.sbc_info.num_subbands,
sink_mcc->cie.sbc_info.alloc_mthd,
sink_mcc->cie.sbc_info.min_bitpool,
sink_mcc->cie.sbc_info.max_bitpool);
}
bt_app_a2d_set_pref_mcc(a2d->a2d_report_snk_codec_caps_stat.conn_hdl, sink_mcc);
break;
}
case ESP_A2D_SRC_SET_PREF_MCC_EVT: {
a2d = (esp_a2d_cb_param_t *)(param);
ESP_LOGI(BT_AV_TAG, "Set preferred media codec config result: conn_hdl: %d, set_status: %d",
a2d->a2d_set_pref_mcc_stat.conn_hdl, a2d->a2d_set_pref_mcc_stat.set_status);
break;
}
default: {
ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event);
break;
@@ -629,7 +730,8 @@ static void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT: {
case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT:
case ESP_AVRC_CT_PROF_STATE_EVT: {
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
break;
}
@@ -722,6 +824,17 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
ESP_LOGI(BT_RC_CT_TAG, "Set absolute volume response: volume %d", rc->set_volume_rsp.volume);
break;
}
/* when avrcp controller init or deinit completed, this event comes */
case ESP_AVRC_CT_PROF_STATE_EVT: {
if (ESP_AVRC_INIT_SUCCESS == rc->avrc_ct_init_stat.state) {
ESP_LOGI(BT_RC_CT_TAG, "AVRCP CT STATE: Init Complete");
} else if (ESP_AVRC_DEINIT_SUCCESS == rc->avrc_ct_init_stat.state) {
ESP_LOGI(BT_RC_CT_TAG, "AVRCP CT STATE: Deinit Complete");
} else {
ESP_LOGE(BT_RC_CT_TAG, "AVRCP CT STATE error: %d", rc->avrc_ct_init_stat.state);
}
break;
}
/* other */
default: {
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event);