mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(bt/bluedroid): Support AVRCP Cover Art feature in a2dp_sink demo
This commit is contained in:
parent
0de83be0fe
commit
147a249d7a
@ -36,7 +36,7 @@ idf.py menuconfig
|
||||
|
||||
* Choose external I2S codec or internal DAC for audio output, and configure the output PINs under A2DP Example Configuration
|
||||
|
||||
* Enable Classic Bluetooth and A2DP under **Component config --> Bluetooth --> Bluedroid Enable**
|
||||
* For AVRCP CT Cover Art feature, is enabled by default, we can disable it by unselecting menuconfig option `Component config --> Bluetooth --> Bluedroid Options --> Classic Bluetooth --> AVRCP Features --> AVRCP CT Cover Art`. This example will try to use AVRCP CT Cover Art feature, get cover art image and count the image size if peer device support, this can be disable in `A2DP Example Configuration --> Use AVRCP CT Cover Art Feature`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
@ -66,7 +66,13 @@ I (122697) BT_AV: Audio packet count 100
|
||||
I (124697) BT_AV: Audio packet count 200
|
||||
I (126697) BT_AV: Audio packet count 300
|
||||
I (128697) BT_AV: Audio packet count 400
|
||||
```
|
||||
|
||||
The output when receiving a cover art image:
|
||||
|
||||
```
|
||||
I (53349) RC_CT: AVRC metadata rsp: attribute id 0x80, 1000748
|
||||
I (53639) RC_CT: Cover Art Client final data event, image size: 14118 bytes
|
||||
```
|
||||
|
||||
Also, the sound will be heard if a loudspeaker is connected and possible external I2S codec is correctly configured. For ESP32 A2DP source example, the sound is noise as the audio source generates the samples with a random sequence.
|
||||
|
@ -53,4 +53,12 @@ menu "A2DP Example Configuration"
|
||||
default "ESP_SPEAKER"
|
||||
help
|
||||
Use this option to set local device name.
|
||||
|
||||
config EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
bool "Use AVRCP CT Cover Art Feature"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default y
|
||||
help
|
||||
This enables the AVRCP Cover Art feature in example and try to get cover art image from peer device.
|
||||
|
||||
endmenu
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -93,6 +93,13 @@ i2s_chan_handle_t tx_chan = NULL;
|
||||
dac_continuous_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
static bool cover_art_connected = false;
|
||||
static bool cover_art_getting = false;
|
||||
static uint32_t cover_art_image_size = 0;
|
||||
static uint8_t image_handle_old[7];
|
||||
#endif
|
||||
|
||||
/********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
*******************************/
|
||||
@ -107,6 +114,18 @@ static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
|
||||
rc->meta_rsp.attr_text = attr_text;
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
static bool image_handle_check(uint8_t *image_handle, int len)
|
||||
{
|
||||
/* Image handle length must be 7 */
|
||||
if (len == 7 && memcmp(image_handle_old, image_handle, 7) != 0) {
|
||||
memcpy(image_handle_old, image_handle, 7);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bt_av_new_track(void)
|
||||
{
|
||||
/* request metadata */
|
||||
@ -114,6 +133,11 @@ static void bt_av_new_track(void)
|
||||
ESP_AVRC_MD_ATTR_ARTIST |
|
||||
ESP_AVRC_MD_ATTR_ALBUM |
|
||||
ESP_AVRC_MD_ATTR_GENRE;
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if (cover_art_connected) {
|
||||
attr_mask |= ESP_AVRC_MD_ATTR_COVER_ART;
|
||||
}
|
||||
#endif
|
||||
esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask);
|
||||
|
||||
/* register notification if peer support the event_id */
|
||||
@ -364,7 +388,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
||||
if (a2d->a2d_psc_cfg_stat.psc_mask & ESP_A2D_PSC_DELAY_RPT) {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device support delay reporting");
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device unsupport delay reporting");
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device unsupported delay reporting");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -415,15 +439,24 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when passthrough responsed, this event comes */
|
||||
/* when passthrough response, this event comes */
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d, rsp_code %d", rc->psth_rsp.key_code,
|
||||
rc->psth_rsp.key_state, rc->psth_rsp.rsp_code);
|
||||
break;
|
||||
}
|
||||
/* when metadata responsed, this event comes */
|
||||
/* when metadata response, this event comes */
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if(rc->meta_rsp.attr_id == 0x80 && cover_art_connected && cover_art_getting == false) {
|
||||
/* check image handle is valid and different with last one, wo dont want to get an image repeatedly */
|
||||
if(image_handle_check(rc->meta_rsp.attr_text, rc->meta_rsp.attr_length)) {
|
||||
esp_avrc_ct_cover_art_get_linked_thumbnail(rc->meta_rsp.attr_text);
|
||||
cover_art_getting = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free(rc->meta_rsp.attr_text);
|
||||
break;
|
||||
}
|
||||
@ -436,6 +469,13 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
/* when feature of remote device indicated, this event comes */
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %"PRIx32", TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if ((rc->rmt_feats.tg_feat_flag & ESP_AVRC_FEAT_FLAG_TG_COVER_ART) && !cover_art_connected) {
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Peer support Cover Art feature, start connection...");
|
||||
/* set mtu to zero to use a default value */
|
||||
esp_avrc_ct_cover_art_connect(0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
/* when notification capability of peer device got, this event comes */
|
||||
@ -448,6 +488,36 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
bt_av_play_pos_changed();
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_COVER_ART_STATE_EVT: {
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if (rc->cover_art_state.state == ESP_AVRC_COVER_ART_CONNECTED) {
|
||||
cover_art_connected = true;
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Cover Art Client connected");
|
||||
}
|
||||
else {
|
||||
cover_art_connected = false;
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Cover Art Client disconnected, reason:%d", rc->cover_art_state.reason);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_COVER_ART_DATA_EVT: {
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
/* when rc->cover_art_data.final is true, it means we have received the entire image or get operation failed */
|
||||
if (rc->cover_art_data.final) {
|
||||
if(rc->cover_art_data.status == ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "Cover Art Client final data event, image size: %lu bytes", cover_art_image_size);
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(BT_RC_CT_TAG, "Cover Art Client get operation failed");
|
||||
}
|
||||
cover_art_image_size = 0;
|
||||
/* set the getting state to false, we can get next image now */
|
||||
cover_art_getting = false;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event);
|
||||
@ -545,6 +615,14 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
|
||||
|
||||
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
/* we must handle ESP_AVRC_CT_COVER_ART_DATA_EVT in this callback, copy image data to other buff before return if need */
|
||||
if (event == ESP_AVRC_CT_COVER_ART_DATA_EVT && param->cover_art_data.status == ESP_BT_STATUS_SUCCESS) {
|
||||
cover_art_image_size += param->cover_art_data.data_len;
|
||||
/* copy image data to other place */
|
||||
/* memcpy(p_buf, param->cover_art_data.p_data, param->cover_art_data.data_len); */
|
||||
}
|
||||
#endif
|
||||
switch (event) {
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT:
|
||||
bt_app_alloc_meta_buffer(param);
|
||||
@ -553,7 +631,9 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
|
||||
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_GET_RN_CAPABILITIES_RSP_EVT:
|
||||
case ESP_AVRC_CT_COVER_ART_STATE_EVT:
|
||||
case ESP_AVRC_CT_COVER_ART_DATA_EVT: {
|
||||
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
|
@ -7,4 +7,5 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED=y
|
||||
CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=n
|
||||
|
Loading…
Reference in New Issue
Block a user