From 7d07ad44cd88873c53910a549fc1023e08112ce9 Mon Sep 17 00:00:00 2001 From: yangfeng Date: Fri, 10 Jul 2026 11:06:47 +0800 Subject: [PATCH] fix(bt): Fix compatibility with A2DP API legacy usage methods - Modify the timing of API calls in the A2DP example - Add a2dp_stream_aac test case - Closes https://github.com/espressif/esp-idf/issues/18786 --- .../bt/host/bluedroid/api/esp_a2dp_api.c | 65 +++++++++++++--- .../classic_bt/a2dp_sink_stream/main/main.c | 71 ++++++++++++------ .../a2dp_sink_stream_aac/main/main.c | 20 +++-- .../classic_bt/a2dp_source_aac/main/main.c | 38 ++++++++-- .../avrcp_absolute_volume/main/main.c | 74 +++++++++++++------ .../avrcp_ct_cover_art/main/bt_app_av.c | 52 ++++++++++++- .../classic_bt/avrcp_ct_cover_art/main/main.c | 24 +----- .../classic_bt/avrcp_ct_metadata/main/main.c | 74 +++++++++++++------ .../classic_bt/pytest_classic_bt_test.py | 34 +++++++++ .../coex/a2dp_gatts_coex/main/bt_app_av.c | 29 +++++++- .../coex/a2dp_gatts_coex/main/main.c | 3 - 11 files changed, 365 insertions(+), 119 deletions(-) diff --git a/components/bt/host/bluedroid/api/esp_a2dp_api.c b/components/bt/host/bluedroid/api/esp_a2dp_api.c index 83c04ed731a..509bbe49f90 100644 --- a/components/bt/host/bluedroid/api/esp_a2dp_api.c +++ b/components/bt/host/bluedroid/api/esp_a2dp_api.c @@ -14,6 +14,13 @@ #if BTC_AV_INCLUDED +// indicate the first time to call the register SEP function (for sink) +static bool s_a2dp_sink_first_call_reg_sep[ESP_A2D_MAX_SEPS] = { [0 ... (ESP_A2D_MAX_SEPS - 1)] = true }; +// indicate the first time to call the register SEP function (for source) +static bool s_a2dp_src_first_call_reg_sep[ESP_A2D_MAX_SEPS] = { [0 ... (ESP_A2D_MAX_SEPS - 1)] = true }; +// indicate the first time to call the get delay value function +static bool s_a2dp_first_call_get_delay = true; + esp_a2d_audio_buff_t *esp_a2d_audio_buff_alloc(uint16_t size) { if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { @@ -73,14 +80,21 @@ esp_err_t esp_a2d_sink_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_ return ESP_ERR_INVALID_STATE; } - if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { - return ESP_ERR_INVALID_STATE; - } - if (mcc == NULL || seid >= ESP_A2D_MAX_SEPS) { return ESP_ERR_INVALID_ARG; } + /* To be compatible with the legacy usage methods, + use a flag indicating whether this is the first time the function has been called. */ + if (s_a2dp_sink_first_call_reg_sep[seid]) { + s_a2dp_sink_first_call_reg_sep[seid] = false; + if (g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + } else if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; @@ -114,7 +128,15 @@ esp_err_t esp_a2d_sink_deinit(void) /* Switch to BTC context */ bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); - return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; + if (stat == BT_STATUS_SUCCESS) { + for (int i = 0; i < ESP_A2D_MAX_SEPS; i++) { + s_a2dp_sink_first_call_reg_sep[i] = false; + } + s_a2dp_first_call_get_delay = false; + return ESP_OK; + } else { + return ESP_FAIL; + } } esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback) @@ -248,7 +270,14 @@ esp_err_t esp_a2d_sink_get_delay_value(void) return ESP_ERR_INVALID_STATE; } - if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { + /* To be compatible with the legacy usage methods, + use a flag indicating whether this is the first time the function has been called. */ + if (s_a2dp_first_call_get_delay) { + s_a2dp_first_call_get_delay = false; + if (g_a2dp_sink_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + } else if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) { return ESP_ERR_INVALID_STATE; } @@ -385,14 +414,21 @@ esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mc return ESP_ERR_INVALID_STATE; } - if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { - return ESP_ERR_INVALID_STATE; - } - if (mcc == NULL || seid >= ESP_A2D_MAX_SEPS) { return ESP_ERR_INVALID_ARG; } + /* To be compatible with the legacy usage methods, + use a flag indicating whether this is the first time the function has been called. */ + if (s_a2dp_src_first_call_reg_sep[seid]) { + s_a2dp_src_first_call_reg_sep[seid] = false; + if (g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + } else if (g_a2dp_on_deinit || g_a2dp_source_ongoing_deinit) { + return ESP_ERR_INVALID_STATE; + } + btc_msg_t msg; msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_A2DP; @@ -426,7 +462,14 @@ esp_err_t esp_a2d_source_deinit(void) /* Switch to BTC context */ bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL); - return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; + if (stat == BT_STATUS_SUCCESS) { + for (int i = 0; i < ESP_A2D_MAX_SEPS; i++) { + s_a2dp_src_first_call_reg_sep[i] = false; + } + return ESP_OK; + } else { + return ESP_FAIL; + } } esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda) diff --git a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream/main/main.c b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream/main/main.c index 3241b5f5033..0dff2d171df 100644 --- a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream/main/main.c @@ -68,10 +68,57 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa bredr_app_gap_evt_def_hdl(event, param); } +static void bt_app_a2d_evt_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(param); + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); +#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == TRUE + esp_a2d_mcc_t mcc = {0}; + mcc.type = ESP_A2D_MCT_SBC; + mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | + ESP_A2D_SBC_CIE_SF_32K | + ESP_A2D_SBC_CIE_SF_44K | + ESP_A2D_SBC_CIE_SF_48K; + mcc.cie.sbc_info.ch_mode = 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; + mcc.cie.sbc_info.block_len = 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; + mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; + mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; + mcc.cie.sbc_info.max_bitpool = 250; + mcc.cie.sbc_info.min_bitpool = 2; + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(0, &mcc)); +#endif + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + /* others */ + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { - case ESP_A2D_PROF_STATE_EVT: + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_evt_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { @@ -124,31 +171,9 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) #if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); #else - esp_a2d_mcc_t mcc = {0}; - mcc.type = ESP_A2D_MCT_SBC; - mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | - ESP_A2D_SBC_CIE_SF_32K | - ESP_A2D_SBC_CIE_SF_44K | - ESP_A2D_SBC_CIE_SF_48K; - mcc.cie.sbc_info.ch_mode = 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; - mcc.cie.sbc_info.block_len = 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; - mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; - mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; - mcc.cie.sbc_info.max_bitpool = 250; - mcc.cie.sbc_info.min_bitpool = 2; - /* register stream end point, only support SBC currently */ - esp_a2d_sink_register_stream_endpoint(0, &mcc); esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb); #endif - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); /* Get local device name */ esp_bt_gap_get_device_name(); diff --git a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/main/main.c b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/main/main.c index 487090177f6..7628d47c2d5 100644 --- a/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/a2dp_sink_stream_aac/main/main.c @@ -369,7 +369,7 @@ static void bt_app_register_a2dp_sink_seps(void) mcc_aac.cie.m24_info.br1 = 0x7F & ESP_A2D_M24_CIE_BR1_MSK; mcc_aac.cie.m24_info.br2 = 0xFF & ESP_A2D_M24_CIE_BR2_MSK; mcc_aac.cie.m24_info.br3 = 0xFF & ESP_A2D_M24_CIE_BR3_MSK; - esp_a2d_sink_register_stream_endpoint(0, &mcc_aac); + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(0, &mcc_aac)); esp_a2d_mcc_t mcc_sbc = {0}; mcc_sbc.type = ESP_A2D_MCT_SBC; @@ -389,7 +389,7 @@ static void bt_app_register_a2dp_sink_seps(void) mcc_sbc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; mcc_sbc.cie.sbc_info.min_bitpool = 2; mcc_sbc.cie.sbc_info.max_bitpool = 250; - esp_a2d_sink_register_stream_endpoint(1, &mcc_sbc); + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(1, &mcc_sbc)); } static void bt_app_dev_cb(esp_bt_dev_cb_event_t event, esp_bt_dev_cb_param_t *param) @@ -407,7 +407,18 @@ static void bt_app_a2d_hdl(uint16_t event, void *param) esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)param; switch (event) { - case ESP_A2D_PROF_STATE_EVT: + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); + bt_app_register_a2dp_sink_seps(); + + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { @@ -504,11 +515,8 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) esp_a2d_register_callback(&bt_app_a2d_cb); assert(esp_a2d_sink_init() == ESP_OK); - bt_app_register_a2dp_sink_seps(); esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb); - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); /* Get local device name */ esp_bt_gap_get_device_name(); diff --git a/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/main/main.c b/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/main/main.c index 35ad386ab19..3bedb3a9ef8 100644 --- a/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/a2dp_source_aac/main/main.c @@ -338,7 +338,7 @@ static void bt_app_register_a2dp_src_seps(void) mcc_aac.cie.m24_info.br1 = 0x02 & ESP_A2D_M24_CIE_BR1_MSK; mcc_aac.cie.m24_info.br2 = 0x71 & ESP_A2D_M24_CIE_BR2_MSK; mcc_aac.cie.m24_info.br3 = 0x00 & ESP_A2D_M24_CIE_BR3_MSK; - esp_a2d_source_register_stream_endpoint(0, &mcc_aac); + ESP_ERROR_CHECK(esp_a2d_source_register_stream_endpoint(0, &mcc_aac)); esp_a2d_mcc_t mcc_sbc = {0}; mcc_sbc.type = ESP_A2D_MCT_SBC; @@ -358,7 +358,28 @@ static void bt_app_register_a2dp_src_seps(void) mcc_sbc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; mcc_sbc.cie.sbc_info.min_bitpool = 2; mcc_sbc.cie.sbc_info.max_bitpool = 250; - esp_a2d_source_register_stream_endpoint(1, &mcc_sbc); + ESP_ERROR_CHECK(esp_a2d_source_register_stream_endpoint(1, &mcc_sbc)); +} + +static void bt_app_a2d_prof_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)param; + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); + bt_app_register_a2dp_src_seps(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } } static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) @@ -378,8 +399,6 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) esp_a2d_source_init(); esp_a2d_register_callback(&bt_app_a2d_cb); - bt_app_register_a2dp_src_seps(); - /* Avoid the state error of s_a2d_state caused by the connection initiated by the peer device. */ esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); esp_bt_gap_get_device_name(); @@ -407,7 +426,16 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { - bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + switch (event) { + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_prof_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } + default: { + bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } + } } static void bt_app_a2d_heart_beat(TimerHandle_t arg) diff --git a/examples/bluetooth/bluedroid/classic_bt/avrcp_absolute_volume/main/main.c b/examples/bluetooth/bluedroid/classic_bt/avrcp_absolute_volume/main/main.c index 7ef35189bf5..723104e4cf5 100644 --- a/examples/bluetooth/bluedroid/classic_bt/avrcp_absolute_volume/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/avrcp_absolute_volume/main/main.c @@ -47,6 +47,7 @@ static void bt_app_dev_cb(esp_bt_dev_cb_event_t event, esp_bt_dev_cb_param_t *pa static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param); /* callback function for A2DP sink */ +static void bt_app_a2d_evt_hdl(uint16_t event, void *param); static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); #if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE @@ -82,10 +83,59 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa bredr_app_gap_evt_def_hdl(event, param); } +static void bt_app_a2d_evt_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(param); + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); +#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE +#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == TRUE + esp_a2d_mcc_t mcc = {0}; + mcc.type = ESP_A2D_MCT_SBC; + mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | + ESP_A2D_SBC_CIE_SF_32K | + ESP_A2D_SBC_CIE_SF_44K | + ESP_A2D_SBC_CIE_SF_48K; + mcc.cie.sbc_info.ch_mode = 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; + mcc.cie.sbc_info.block_len = 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; + mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; + mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; + mcc.cie.sbc_info.max_bitpool = 250; + mcc.cie.sbc_info.min_bitpool = 2; + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(0, &mcc)); +#endif +#endif + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + /* others */ + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { - case ESP_A2D_PROF_STATE_EVT: + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_evt_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { @@ -194,32 +244,10 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) #if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); #else - esp_a2d_mcc_t mcc = {0}; - mcc.type = ESP_A2D_MCT_SBC; - mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | - ESP_A2D_SBC_CIE_SF_32K | - ESP_A2D_SBC_CIE_SF_44K | - ESP_A2D_SBC_CIE_SF_48K; - mcc.cie.sbc_info.ch_mode = 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; - mcc.cie.sbc_info.block_len = 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; - mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; - mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; - mcc.cie.sbc_info.max_bitpool = 250; - mcc.cie.sbc_info.min_bitpool = 2; - /* register stream end point, only support SBC currently */ - esp_a2d_sink_register_stream_endpoint(0, &mcc); esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb); #endif #endif - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); /* Get local device name */ esp_bt_gap_get_device_name(); diff --git a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/bt_app_av.c b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/bt_app_av.c index eff24138899..41f5f4ad479 100644 --- a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/bt_app_av.c +++ b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/bt_app_av.c @@ -13,6 +13,7 @@ #include "esp_log.h" #include "esp_bt_device.h" #include "esp_gap_bt_api.h" +#include "esp_a2dp_api.h" #include "esp_avrc_api.h" #include "freertos/FreeRTOS.h" @@ -108,10 +109,59 @@ static void bt_app_avrc_ct_evt_hdl(uint16_t event, void *param) * EXTERNAL FUNCTION DEFINITIONS *******************************/ +static void bt_app_a2d_evt_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(param); + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); +#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE +#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == TRUE + esp_a2d_mcc_t mcc = {0}; + mcc.type = ESP_A2D_MCT_SBC; + mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | + ESP_A2D_SBC_CIE_SF_32K | + ESP_A2D_SBC_CIE_SF_44K | + ESP_A2D_SBC_CIE_SF_48K; + mcc.cie.sbc_info.ch_mode = 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; + mcc.cie.sbc_info.block_len = 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; + mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; + mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; + mcc.cie.sbc_info.max_bitpool = 250; + mcc.cie.sbc_info.min_bitpool = 2; + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(0, &mcc)); +#endif +#endif + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + /* others */ + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { - case ESP_A2D_PROF_STATE_EVT: + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_evt_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { diff --git a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/main.c b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/main.c index 87e32a7d887..756991daf7d 100644 --- a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_cover_art/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -79,32 +79,10 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) #if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); #else - esp_a2d_mcc_t mcc = {0}; - mcc.type = ESP_A2D_MCT_SBC; - mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | - ESP_A2D_SBC_CIE_SF_32K | - ESP_A2D_SBC_CIE_SF_44K | - ESP_A2D_SBC_CIE_SF_48K; - mcc.cie.sbc_info.ch_mode = 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; - mcc.cie.sbc_info.block_len = 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; - mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; - mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; - mcc.cie.sbc_info.max_bitpool = 250; - mcc.cie.sbc_info.min_bitpool = 2; - /* register stream end point, only support SBC currently */ - esp_a2d_sink_register_stream_endpoint(0, &mcc); esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb); #endif #endif - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); /* Get local device name */ esp_bt_gap_get_device_name(); diff --git a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_metadata/main/main.c b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_metadata/main/main.c index 0d094121305..ee8baf6a22e 100644 --- a/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_metadata/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/avrcp_ct_metadata/main/main.c @@ -47,6 +47,7 @@ static void bt_app_dev_cb(esp_bt_dev_cb_event_t event, esp_bt_dev_cb_param_t *pa static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param); /* callback function for A2DP sink */ +static void bt_app_a2d_evt_hdl(uint16_t event, void *param); static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); #if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE @@ -85,10 +86,59 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa bredr_app_gap_evt_def_hdl(event, param); } +static void bt_app_a2d_evt_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(param); + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); +#if CONFIG_EXAMPLE_A2DP_SINK_STREAM_ENABLE +#if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == TRUE + esp_a2d_mcc_t mcc = {0}; + mcc.type = ESP_A2D_MCT_SBC; + mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | + ESP_A2D_SBC_CIE_SF_32K | + ESP_A2D_SBC_CIE_SF_44K | + ESP_A2D_SBC_CIE_SF_48K; + mcc.cie.sbc_info.ch_mode = 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; + mcc.cie.sbc_info.block_len = 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; + mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; + mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; + mcc.cie.sbc_info.max_bitpool = 250; + mcc.cie.sbc_info.min_bitpool = 2; + ESP_ERROR_CHECK(esp_a2d_sink_register_stream_endpoint(0, &mcc)); +#endif +#endif + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + /* others */ + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { - case ESP_A2D_PROF_STATE_EVT: + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_evt_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { @@ -242,32 +292,10 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) #if CONFIG_EXAMPLE_A2DP_SINK_USE_EXTERNAL_CODEC == FALSE esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); #else - esp_a2d_mcc_t mcc = {0}; - mcc.type = ESP_A2D_MCT_SBC; - mcc.cie.sbc_info.samp_freq = ESP_A2D_SBC_CIE_SF_16K | - ESP_A2D_SBC_CIE_SF_32K | - ESP_A2D_SBC_CIE_SF_44K | - ESP_A2D_SBC_CIE_SF_48K; - mcc.cie.sbc_info.ch_mode = 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; - mcc.cie.sbc_info.block_len = 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; - mcc.cie.sbc_info.num_subbands = ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 | ESP_A2D_SBC_CIE_NUM_SUBBANDS_8; - mcc.cie.sbc_info.alloc_mthd = ESP_A2D_SBC_CIE_ALLOC_MTHD_SNR | ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS; - mcc.cie.sbc_info.max_bitpool = 250; - mcc.cie.sbc_info.min_bitpool = 2; - /* register stream end point, only support SBC currently */ - esp_a2d_sink_register_stream_endpoint(0, &mcc); esp_a2d_sink_register_audio_data_callback(bt_app_a2d_audio_data_cb); #endif #endif - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); /* Get local device name */ esp_bt_gap_get_device_name(); diff --git a/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py b/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py index ce84f642f2a..8d5b4046744 100644 --- a/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py +++ b/examples/bluetooth/bluedroid/classic_bt/pytest_classic_bt_test.py @@ -210,3 +210,37 @@ def test_bt_avrcp_absolute_volume(app_path: str, dut: tuple[IdfDut, IdfDut]) -> sink_dut.expect_exact(f'AVRC conn_state event: state 1, [{source_dut_mac}]', timeout=30) sink_dut.expect_exact(f'AVRC conn_state evt: state 1, [{source_dut_mac}]', timeout=30) sink_dut.expect_exact('start volume change simulation', timeout=30) + + +# case 8: A2DP Stream AAC +@pytest.mark.two_duts +@pytest.mark.parametrize( + 'count, app_path, target, config', + [ + ( + 2, + f'{str(CUR_DIR / "a2dp_sink_stream_aac")}|{str(CUR_DIR / "a2dp_source_aac")}', + 'esp32|esp32', + 'test', + ), + ], + indirect=True, +) +def test_bt_a2dp_stream_aac(app_path: str, dut: tuple[IdfDut, IdfDut]) -> None: + sink_dut = dut[0] + source_dut = dut[1] + source_dut_mac = source_dut.expect(r'Bluetooth MAC: (([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})').group(1).decode('utf8') + sink_dut.expect_exact('A2DP PROF STATE: Init Complete', timeout=30) + source_dut.expect_exact('A2DP PROF STATE: Init Complete', timeout=30) + sink_dut.expect_exact('A2DP register SEP success, seid: 0', timeout=30) + sink_dut.expect_exact('A2DP register SEP success, seid: 1', timeout=30) + + source_dut.expect_exact('Found a target device', timeout=60) + source_dut.expect_exact('a2dp connecting to peer:', timeout=30) + source_dut.expect_exact('A2DP audio stream configuration, codec type: 2', timeout=30) + source_dut.expect_exact('a2dp connected', timeout=30) + source_dut.expect_exact('a2dp media start successfully.', timeout=60) + + sink_dut.expect_exact('A2DP audio stream configuration, codec type: 2', timeout=30) + sink_dut.expect_exact(f'A2DP connection state: Connected, [{source_dut_mac}]', timeout=30) + sink_dut.expect_exact('A2DP audio state: Started', timeout=60) diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c index 754ea42a82b..2a7085f729e 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c @@ -82,6 +82,30 @@ static void bt_app_avrc_ct_evt_hdl(uint16_t event, void *param) } } +/* handler for A2DP callback events */ +static void bt_app_a2d_evt_hdl(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = (esp_a2d_cb_param_t *)(param); + + switch (event) { + /* when a2dp init or deinit completed, this event comes */ + case ESP_A2D_PROF_STATE_EVT: { + if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); + /* Get the default value of the delay value */ + esp_a2d_sink_get_delay_value(); + } else { + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); + } + break; + } + /* others */ + default: + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + /******************************** * EXTERNAL FUNCTION DEFINITIONS *******************************/ @@ -89,13 +113,16 @@ static void bt_app_avrc_ct_evt_hdl(uint16_t event, void *param) void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) { switch (event) { + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_app_a2d_evt_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); + break; + } case ESP_A2D_CONNECTION_STATE_EVT: case ESP_A2D_AUDIO_STATE_EVT: case ESP_A2D_AUDIO_CFG_EVT: { bt_app_work_dispatch(bt_a2d_evt_int_codec_hdl, event, param, sizeof(esp_a2d_cb_param_t), NULL, NULL); break; } - case ESP_A2D_PROF_STATE_EVT: case ESP_A2D_SNK_PSC_CFG_EVT: case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: { diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c index 75cddcbcc29..0761e1c3594 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c @@ -644,9 +644,6 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) esp_a2d_register_callback(&bt_app_a2d_cb); esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); - /* Get the default value of the delay value */ - esp_a2d_sink_get_delay_value(); - /* set discoverable and connectable mode, wait to be connected */ esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); break;