diff --git a/components/bt/bluedroid/api/esp_avrc_api.c b/components/bt/bluedroid/api/esp_avrc_api.c index cf4a8e003..2523665c2 100644 --- a/components/bt/bluedroid/api/esp_avrc_api.c +++ b/components/bt/bluedroid/api/esp_avrc_api.c @@ -27,7 +27,7 @@ esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback) if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } - + if (callback == NULL) { return ESP_FAIL; } @@ -43,7 +43,7 @@ esp_err_t esp_avrc_ct_init(void) } btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_AVRC; msg.act = BTC_AVRC_CTRL_API_INIT_EVT; @@ -60,7 +60,7 @@ esp_err_t esp_avrc_ct_deinit(void) } btc_msg_t msg; - + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_AVRC; msg.act = BTC_AVRC_CTRL_API_DEINIT_EVT; @@ -70,12 +70,97 @@ esp_err_t esp_avrc_ct_deinit(void) return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; } +esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl >= 16 || attr_id > ESP_AVRC_PS_MAX_ATTR - 1) { + return ESP_FAIL; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC; + msg.act = BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.ps_cmd.tl = tl; + arg.ps_cmd.attr_id = attr_id; + arg.ps_cmd.value_id = value_id; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + + +esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl >= 16 || event_id > ESP_AVRC_RN_MAX_EVT - 1) { + return ESP_FAIL; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC; + msg.act = BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.rn_cmd.tl = tl; + arg.rn_cmd.event_id = event_id; + arg.rn_cmd.event_parameter = event_parameter; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask) +{ + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_ERR_INVALID_STATE; + } + + if (tl >= 16) { + return ESP_FAIL; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_AVRC; + msg.act = BTC_AVRC_STATUS_API_SND_META_EVT; + + btc_avrc_args_t arg; + memset(&arg, 0, sizeof(btc_avrc_args_t)); + + arg.md_cmd.tl = tl; + arg.md_cmd.attr_mask = attr_mask; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state) { if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } + if (tl >= 16 || key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) { + return ESP_FAIL; + } + btc_msg_t msg; msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_AVRC; @@ -87,7 +172,7 @@ esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t arg.pt_cmd.tl = tl; arg.pt_cmd.key_code = key_code; arg.pt_cmd.key_state = key_state; - + /* Switch to BTC context */ bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL); return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; diff --git a/components/bt/bluedroid/api/include/esp_avrc_api.h b/components/bt/bluedroid/api/include/esp_avrc_api.h index e30742098..df320b80a 100644 --- a/components/bt/bluedroid/api/include/esp_avrc_api.h +++ b/components/bt/bluedroid/api/include/esp_avrc_api.h @@ -40,7 +40,9 @@ typedef enum { ESP_AVRC_PT_CMD_STOP = 0x45, /*!< stop */ ESP_AVRC_PT_CMD_PAUSE = 0x46, /*!< pause */ ESP_AVRC_PT_CMD_FORWARD = 0x4B, /*!< forward */ - ESP_AVRC_PT_CMD_BACKWARD = 0x4C /*!< backward */ + ESP_AVRC_PT_CMD_BACKWARD = 0x4C, /*!< backward */ + ESP_AVRC_PT_CMD_REWIND = 0x48, /*!< rewind */ + ESP_AVRC_PT_CMD_FAST_FORWARD = 0x49 /*!< fast forward */ } esp_avrc_pt_cmd_t; /// AVRC passthrough command state @@ -53,9 +55,73 @@ typedef enum { typedef enum { ESP_AVRC_CT_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */ ESP_AVRC_CT_PASSTHROUGH_RSP_EVT = 1, /*!< passthrough response event */ + ESP_AVRC_CT_METADATA_RSP_EVT = 2, /*!< metadata response event */ + ESP_AVRC_CT_PLAY_STATUS_RSP_EVT = 3, /*!< play status response event */ + ESP_AVRC_CT_CHANGE_NOTIFY_EVT = 4, /*!< notification event */ ESP_AVRC_CT_MAX_EVT } esp_avrc_ct_cb_event_t; +//AVRC metadata attribute mask +typedef enum { + ESP_AVRC_MD_ATTR_TITLE = 0x1, /*!< title of the playing track */ + ESP_AVRC_MD_ATTR_ARTIST = 0x2, /*!< track artist */ + ESP_AVRC_MD_ATTR_ALBUM = 0x4, /*!< album name */ + ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8, /*!< track position on the album */ + ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10, /*!< number of tracks on the album */ + ESP_AVRC_MD_ATTR_GENRE = 0x20, /*!< track genre */ + ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40 /*!< total album playing time in miliseconds */ +} esp_avrc_md_attr_mask_t; + +//AVRC event notification ids +typedef enum { + ESP_AVRC_RN_PLAY_STATUS_CHANGE = 0x01, /*!< track status change, eg. from playing to paused */ + ESP_AVRC_RN_TRACK_CHANGE = 0x02, /*!< new track is loaded */ + ESP_AVRC_RN_TRACK_REACHED_END = 0x03, /*!< current track reached end */ + ESP_AVRC_RN_TRACK_REACHED_START = 0x04, /*!< current track reached start position */ + ESP_AVRC_RN_PLAY_POS_CHANGED = 0x05, /*!< track playing position changed */ + ESP_AVRC_RN_BATTERY_STATUS_CHANGE = 0x06, /*!< battery status changed */ + ESP_AVRC_RN_SYSTEM_STATUS_CHANGE = 0x07, /*!< system status changed */ + ESP_AVRC_RN_APP_SETTING_CHANGE = 0x08, /*!< application settings changed */ + ESP_AVRC_RN_MAX_EVT +} esp_avrc_rn_event_ids_t; + +//AVRC player setting ids +typedef enum { + ESP_AVRC_PS_EQUALIZER = 0x01, /*!< equalizer, on or off */ + ESP_AVRC_PS_REPEAT_MODE = 0x02, /*!< repeat mode */ + ESP_AVRC_PS_SHUFFLE_MODE = 0x03, /*!< shuffle mode */ + ESP_AVRC_PS_SCAN_MODE = 0x04, /*!< scan mode on or off */ + ESP_AVRC_PS_MAX_ATTR +} esp_avrc_ps_attr_ids_t; + +//AVRC equalizer modes +typedef enum { + ESP_AVRC_PS_EQUALIZER_OFF = 0x1, + ESP_AVRC_PS_EQUALIZER_ON = 0x2 +} esp_avrc_ps_eq_value_ids_t; + +//AVRC repeat modes +typedef enum { + ESP_AVRC_PS_REPEAT_OFF = 0x1, + ESP_AVRC_PS_REPEAT_SINGLE = 0x2, + ESP_AVRC_PS_REPEAT_GROUP = 0x3 +} esp_avrc_ps_rpt_value_ids_t; + + +//AVRC shuffle modes +typedef enum { + ESP_AVRC_PS_SHUFFLE_OFF = 0x1, + ESP_AVRC_PS_SHUFFLE_ALL = 0x2, + ESP_AVRC_PS_SHUFFLE_GROUP = 0x3 +} esp_avrc_ps_shf_value_ids_t; + +//AVRC scan modes +typedef enum { + ESP_AVRC_PS_SCAN_OFF = 0x1, + ESP_AVRC_PS_SCAN_ALL = 0x2, + ESP_AVRC_PS_SCAN_GROUP = 0x3 +} esp_avrc_ps_scn_value_ids_t; + /// AVRC controller callback parameters typedef union { /** @@ -63,10 +129,10 @@ typedef union { */ struct avrc_ct_conn_stat_param { bool connected; /*!< whether AVRC connection is set up */ - uint32_t feat_mask; /*!< AVRC feature mask of remote device */ + uint32_t feat_mask; /*!< AVRC feature mask of remote device */ esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */ } conn_stat; /*!< AVRC connection status */ - + /** * @brief ESP_AVRC_CT_PASSTHROUGH_RSP_EVT */ @@ -75,6 +141,24 @@ typedef union { uint8_t key_code; /*!< passthrough command code */ uint8_t key_state; /*!< 0 for PRESSED, 1 for RELEASED */ } psth_rsp; /*!< passthrough command response */ + + /** + * @brief ESP_AVRC_CT_METADATA_RSP_EVT + */ + struct avrc_ct_meta_rsp_param { + uint8_t attr_id; /*!< id of metadata attribute */ + uint8_t *attr_text; /*!< attribute itself */ + int attr_length; /*!< attribute character length */ + } meta_rsp; /*!< metadata attributes response */ + + /** + * @brief ESP_AVRC_CT_CHANGE_NOTIFY_EVT + */ + struct avrc_ct_change_notify_param { + uint8_t event_id; /*!< id of AVRC event notification */ + uint32_t event_parameter; /*!< event notification parameter */ + } change_ntf; /*!< notifications */ + } esp_avrc_ct_cb_param_t; @@ -88,9 +172,9 @@ typedef void (* esp_avrc_ct_cb_t)(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_p /** * @brief Register application callbacks to AVRCP module; for now only AVRCP Controller - * role is supported. This function should be called after esp_bluedroid_enable() + * role is supported. This function should be called after esp_bluedroid_enable() * completes successfully - * + * * @param[in] callback: AVRCP controller callback function * * @return @@ -107,7 +191,7 @@ esp_err_t esp_avrc_ct_register_callback(esp_avrc_ct_cb_t callback); * @brief Initialize the bluetooth AVRCP controller module, This function should be called * after esp_bluedroid_enable() completes successfully * - * @return + * @return * - ESP_OK: success * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others @@ -121,13 +205,45 @@ esp_err_t esp_avrc_ct_init(void); * @brief De-initialize AVRCP controller module. This function should be called after * after esp_bluedroid_enable() completes successfully * - * @return + * @return * - ESP_OK: success * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others */ esp_err_t esp_avrc_ct_deinit(void); +/* add description */ +esp_err_t esp_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id); + +/** + * @brief Send register notification command to AVRCP target, This function should be called after + * ESP_AVRC_CT_CONNECTION_STATE_EVT is received and AVRCP connection is established + * + * @param[in] tl : transaction label, 0 to 15, consecutive commands should use different values. + * @param[in] event_id : id of events, e.g. ESP_AVRC_RN_PLAY_STATUS_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, etc. + * @param[in] event_parameter : special parameters, eg. playback interval for ESP_AVRC_RN_PLAY_POS_CHANGED + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter); + + +/** + * @brief Send metadata command to AVRCP target, This function should be called after + * ESP_AVRC_CT_CONNECTION_STATE_EVT is received and AVRCP connection is established + * + * @param[in] tl : transaction label, 0 to 15, consecutive commands should use different values. + * @param[in] attr_mask : mask of attributes, e.g. ESP_AVRC_MD_ATTR_ID_TITLE | ESP_AVRC_MD_ATTR_ID_ARTIST. + * + * @return + * - ESP_OK: success + * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled + * - ESP_FAIL: others + */ +esp_err_t esp_avrc_ct_send_metadata_cmd(uint8_t tl, uint8_t attr_mask); + /** * @brief Send passthrough command to AVRCP target, This function should be called after @@ -138,7 +254,7 @@ esp_err_t esp_avrc_ct_deinit(void); * @param[in] key_state : passthrough command key state, ESP_AVRC_PT_CMD_STATE_PRESSED or * ESP_AVRC_PT_CMD_STATE_RELEASED * - * @return + * @return * - ESP_OK: success * - ESP_INVALID_STATE: if bluetooth stack is not yet enabled * - ESP_FAIL: others diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c index b376e0bd1..7c04cbfe0 100644 --- a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c @@ -1120,7 +1120,7 @@ bt_status_t btc_av_execute_service(BOOLEAN b_enable) * auto-suspend av streaming on AG events(SCO or Call). The suspend shall * be initiated by the app/audioflinger layers */ BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_NO_SCO_SSPD) - // | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR + | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL, bte_av_callback); BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos); diff --git a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c index 838183970..8c4462357 100644 --- a/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -31,6 +31,7 @@ #include "btc_manage.h" #include "esp_avrc_api.h" #include "mutex.h" +#include "allocator.h" #if BTC_AV_INCLUDED @@ -96,6 +97,7 @@ rc_device_t device; static void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open); static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close); static void handle_rc_passthrough_rsp ( tBTA_AV_REMOTE_RSP *p_remote_rsp); +static void handle_rc_metadata_rsp ( tBTA_AV_META_MSG *p_remote_rsp); /***************************************************************************** ** Static variables @@ -250,6 +252,86 @@ static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close) #endif } +static void handle_rc_attributes_rsp ( tAVRC_MSG_VENDOR *vendor_msg) +{ + uint8_t attr_count = vendor_msg->p_vendor_data[4]; + int attr_index = 5; + int attr_length = 0; + uint32_t attr_id = 0; + + //Check if there are any attributes + if (attr_count < 1) { + return; + } + + esp_avrc_ct_cb_param_t param[attr_count]; + memset(¶m[0], 0, sizeof(esp_avrc_ct_cb_param_t) * attr_count); + + for (int i = 0; i < attr_count; i++) { + attr_length = (int) vendor_msg->p_vendor_data[7 + attr_index] | vendor_msg->p_vendor_data[6 + attr_index] << 8; + + //Received attribute text is not null terminated, so it's useful to know it's length + param[i].meta_rsp.attr_length = attr_length; + param[i].meta_rsp.attr_text = &vendor_msg->p_vendor_data[8 + attr_index]; + + attr_id = vendor_msg->p_vendor_data[3 + attr_index] | + vendor_msg->p_vendor_data[2 + attr_index] << 8 | vendor_msg->p_vendor_data[1 + attr_index] << 16 | + vendor_msg->p_vendor_data[attr_index] << 24; + + //Convert to mask id + param[i].meta_rsp.attr_id = (1 << (attr_id - 1)); + + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_METADATA_RSP_EVT, ¶m[i]); + + attr_index += (int) vendor_msg->p_vendor_data[7 + attr_index] + 8; + } +} + +static void handle_rc_notification_rsp ( tAVRC_MSG_VENDOR *vendor_msg) +{ + esp_avrc_ct_cb_param_t param; + + param.change_ntf.event_id = vendor_msg->p_vendor_data[4]; + + param.change_ntf.event_parameter = vendor_msg->p_vendor_data[5] << 24 | vendor_msg->p_vendor_data[6] << 16 | + vendor_msg->p_vendor_data[7] << 8 | vendor_msg->p_vendor_data[8]; + + btc_avrc_ct_cb_to_app(ESP_AVRC_CT_CHANGE_NOTIFY_EVT, ¶m); +} + +/*************************************************************************** + * Function handle_rc_metadata_rsp + * + * - Argument: tBTA_AV_META_MSG metadata command response + * + * - Description: Vendor metadata response handler + * + ***************************************************************************/ +static void handle_rc_metadata_rsp ( tBTA_AV_META_MSG *p_remote_rsp) +{ +#if (AVRC_METADATA_INCLUDED == TRUE) + tAVRC_MSG *avrc_msg = p_remote_rsp->p_msg; + tAVRC_MSG_VENDOR *vendor_msg = &avrc_msg->vendor; + + //Check what type of metadata was received + switch (vendor_msg->hdr.ctype) { + case AVRC_RSP_CHANGED: + if (vendor_msg->p_vendor_data[0] == AVRC_PDU_REGISTER_NOTIFICATION) { + handle_rc_notification_rsp(vendor_msg); + } + break; + + case AVRC_RSP_IMPL_STBL: + if (vendor_msg->p_vendor_data[0] == AVRC_PDU_GET_ELEMENT_ATTR) { + handle_rc_attributes_rsp(vendor_msg); + } + break; + } +#else + LOG_ERROR("%s AVRCP metadata is not enabled", __FUNCTION__); +#endif +} + /*************************************************************************** * Function handle_rc_passthrough_rsp * @@ -326,8 +408,12 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) } break; + case BTA_AV_META_MSG_EVT: { + handle_rc_metadata_rsp(&(p_data->meta_msg)); + } + break; + // below events are not handled for now - case BTA_AV_META_MSG_EVT: case BTA_AV_REMOTE_CMD_EVT: default: LOG_DEBUG("Unhandled RC event : 0x%x", event); @@ -390,13 +476,124 @@ static void btc_avrc_ct_deinit(void) LOG_INFO("## %s ## completed", __FUNCTION__); } +static bt_status_t btc_avrc_ct_send_set_player_value_cmd(uint8_t tl, uint8_t attr_id, uint8_t value_id) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + tAVRC_APP_SETTING values = {0}; + + values.attr_id = attr_id; + values.attr_val = value_id; + + avrc_cmd.set_app_val.opcode = AVRC_OP_VENDOR; + avrc_cmd.set_app_val.status = AVRC_STS_NO_ERROR; + avrc_cmd.set_app_val.num_val = 1; + avrc_cmd.set_app_val.p_vals = &values; + avrc_cmd.set_app_val.pdu = AVRC_PDU_SET_PLAYER_APP_VALUE; + + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + if (btc_rc_vb.rc_features & BTA_AV_FEAT_METADATA) { + BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, BTA_AV_CMD_CTRL, p_msg); + status = BT_STATUS_SUCCESS; + } else { + status = BT_STATUS_FAIL; + LOG_DEBUG("%s: feature not supported", __FUNCTION__); + } + } + +#else + LOG_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_register_notification_cmd(uint8_t tl, uint8_t event_id, uint32_t event_parameter) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.reg_notif.opcode = AVRC_OP_VENDOR; + avrc_cmd.reg_notif.status = AVRC_STS_NO_ERROR; + avrc_cmd.reg_notif.event_id = event_id; + avrc_cmd.reg_notif.param = event_parameter; + avrc_cmd.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION; + + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + if (btc_rc_vb.rc_features & BTA_AV_FEAT_METADATA) { + BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, AVRC_CMD_NOTIF, p_msg); + status = BT_STATUS_SUCCESS; + } else { + status = BT_STATUS_FAIL; + LOG_DEBUG("%s: feature not supported", __FUNCTION__); + } + } + +#else + LOG_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + +static bt_status_t btc_avrc_ct_send_metadata_cmd (uint8_t tl, uint8_t attr_mask) +{ + tAVRC_STS status = BT_STATUS_UNSUPPORTED; + +#if (AVRC_METADATA_INCLUDED == TRUE) + CHECK_ESP_RC_CONNECTED; + uint32_t index = 0; + + tAVRC_COMMAND avrc_cmd = {0}; + BT_HDR *p_msg = NULL; + + avrc_cmd.get_elem_attrs.opcode = AVRC_OP_VENDOR; + avrc_cmd.get_elem_attrs.status = AVRC_STS_NO_ERROR; + avrc_cmd.get_elem_attrs.pdu = AVRC_PDU_GET_ELEMENT_ATTR; + + for (int count = 0; count < AVRC_MAX_ELEM_ATTR_SIZE; count++) { + if ((attr_mask & (1 << count)) > 0) { + avrc_cmd.get_elem_attrs.attrs[index] = count + 1; + index++; + } + } + + avrc_cmd.get_elem_attrs.num_attr = index; + + status = AVRC_BldCommand(&avrc_cmd, &p_msg); + if (status == AVRC_STS_NO_ERROR) { + if (btc_rc_vb.rc_features & BTA_AV_FEAT_METADATA) { + BTA_AvMetaCmd(btc_rc_vb.rc_handle, tl, AVRC_CMD_STATUS, p_msg); + status = BT_STATUS_SUCCESS; + } else { + status = BT_STATUS_FAIL; + LOG_DEBUG("%s: feature not supported", __FUNCTION__); + } + } + +#else + LOG_DEBUG("%s: feature not enabled", __FUNCTION__); +#endif + + return status; +} + static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state) { tAVRC_STS status = BT_STATUS_UNSUPPORTED; - if (tl >= 16 || - key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) { - return ESP_ERR_INVALID_ARG; - } + #if (AVRC_CTLR_INCLUDED == TRUE) CHECK_ESP_RC_CONNECTED; LOG_DEBUG("%s: key-code: %d, key-state: %d", __FUNCTION__, @@ -436,6 +633,18 @@ void btc_avrc_call_handler(btc_msg_t *msg) // todo: callback to application break; } + case BTC_AVRC_STATUS_API_SND_META_EVT: { + btc_avrc_ct_send_metadata_cmd(arg->md_cmd.tl, arg->md_cmd.attr_mask); + break; + } + case BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT: { + btc_avrc_ct_send_register_notification_cmd(arg->rn_cmd.tl, arg->rn_cmd.event_id, arg->rn_cmd.event_parameter); + break; + } + case BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT: { + btc_avrc_ct_send_set_player_value_cmd(arg->ps_cmd.tl, arg->ps_cmd.attr_id, arg->ps_cmd.value_id); + break; + } default: LOG_WARN("%s : unhandled event: %d\n", __FUNCTION__, msg->act); } diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h index 3f16d10f3..1ea3c2dbb 100644 --- a/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h +++ b/components/bt/bluedroid/btc/profile/std/include/btc_avrc.h @@ -39,7 +39,11 @@ typedef enum { typedef enum { BTC_AVRC_CTRL_API_INIT_EVT = 0, BTC_AVRC_CTRL_API_DEINIT_EVT, - BTC_AVRC_CTRL_API_SND_PTCMD_EVT + BTC_AVRC_CTRL_API_SND_PTCMD_EVT, + BTC_AVRC_STATUS_API_SND_META_EVT, + BTC_AVRC_STATUS_API_SND_PLAY_STATUS_EVT, + BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT, + BTC_AVRC_CTRL_API_SET_PLAYER_SETTING_EVT } btc_avrc_act_t; typedef struct { @@ -48,14 +52,29 @@ typedef struct { uint8_t key_state; } pt_cmd_t; +typedef struct { + uint8_t tl; + uint8_t attr_mask; +} md_cmd_t; + +typedef struct { + uint8_t tl; + uint8_t event_id; + uint32_t event_parameter; +} rn_cmd_t; + +typedef struct { + uint8_t tl; + uint8_t attr_id; + uint8_t value_id; +} ps_cmd_t; + /* btc_avrc_args_t */ typedef union { - // BTC_AVRC_CTRL_API_SND_PT_CMD_EVT - struct { - uint8_t tl; - uint8_t key_code; - uint8_t key_state; - } pt_cmd; + pt_cmd_t pt_cmd; + md_cmd_t md_cmd; + rn_cmd_t rn_cmd; + ps_cmd_t ps_cmd; } btc_avrc_args_t; /** BT-RC Controller callback structure. */ diff --git a/components/bt/bluedroid/stack/avrc/avrc_bld_ct.c b/components/bt/bluedroid/stack/avrc/avrc_bld_ct.c index 41ca87bf8..ecd804388 100644 --- a/components/bt/bluedroid/stack/avrc/avrc_bld_ct.c +++ b/components/bt/bluedroid/stack/avrc/avrc_bld_ct.c @@ -98,7 +98,7 @@ static tAVRC_STS avrc_bld_set_abs_volume_cmd (tAVRC_SET_VOLUME_CMD *p_cmd, BT_HD ** Otherwise, the error code. ** *******************************************************************************/ -static tAVRC_STS avrc_bld_vol_change_notfn(BT_HDR *p_pkt) +static tAVRC_STS avrc_bld_register_change_notfn(UINT8 event_id, UINT32 event_parameter, BT_HDR *p_pkt) { UINT8 *p_data, *p_start; @@ -109,8 +109,8 @@ static tAVRC_STS avrc_bld_vol_change_notfn(BT_HDR *p_pkt) p_data = p_start + 2; /* pdu + rsvd */ /* add fixed length 5 -*/ UINT16_TO_BE_STREAM(p_data, 5); - UINT8_TO_BE_STREAM(p_data, AVRC_EVT_VOLUME_CHANGE); - UINT32_TO_BE_STREAM(p_data, 0); + UINT8_TO_BE_STREAM(p_data, event_id); + UINT32_TO_BE_STREAM(p_data, event_parameter); p_pkt->len = (p_data - p_start); return AVRC_STS_NO_ERROR; } @@ -132,8 +132,7 @@ static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) AVRC_TRACE_API("avrc_bld_init_cmd_buffer: pdu=%x, opcode=%x", p_cmd->pdu, opcode); UINT16 offset = 0; - switch (opcode) - { + switch (opcode) { case AVRC_OP_PASS_THRU: offset = AVRC_MSG_PASS_THRU_OFFSET; break; @@ -148,7 +147,7 @@ static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) if (p_pkt) { UINT8 *p_data, *p_start; - p_pkt->layer_specific = AVCT_DATA_CTRL; + p_pkt->layer_specific = AVCT_DATA_CTRL; p_pkt->event = opcode; p_pkt->offset = offset; p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; @@ -175,6 +174,69 @@ static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd) return p_pkt; } +/******************************************************************************* +** +** Function avrc_bld_set_player_value_cmd +** +** Description This function builds the Set Player Application Value command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_set_player_value_cmd(tAVRC_SET_APP_VALUE_CMD *p_cmd, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add length */ + UINT16_TO_BE_STREAM(p_data, 3); + /* Number of attributes */ + UINT8_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_id); + UINT8_TO_BE_STREAM(p_data, p_cmd->p_vals->attr_val); + + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** +** Function avrc_bld_get_element_attr_cmd +** +** Description This function builds the Get Element Attribute command. +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_element_attr_cmd (tAVRC_GET_ELEM_ATTRS_CMD *p_cmd, BT_HDR *p_pkt) +{ + int i; + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_get_element_attr_cmd num_attr: %d", p_cmd->num_attr); + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start + 2; /* pdu + rsvd */ + /* add length */ + UINT16_TO_BE_STREAM(p_data, 8 + 1 /* id + attr count */ + p_cmd->num_attr * sizeof(UINT32)); + /* Identifier 0x0 (PLAYING) */ + UINT64_TO_BE_STREAM(p_data, (UINT64)(0)); + /* Attribute count */ + UINT8_TO_BE_STREAM(p_data, p_cmd->num_attr); + + for (i = 0; i < p_cmd->num_attr; i++) { + AVRC_TRACE_API("avrc_bld_get_element_attr_cmd attr_id: %d", p_cmd->attrs[i]); + UINT32_TO_BE_STREAM(p_data, p_cmd->attrs[i]); + } + + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + /******************************************************************************* ** ** Function AVRC_BldCommand @@ -223,14 +285,17 @@ tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt) break; #endif - case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ -#if (AVRC_ADV_CTRL_INCLUDED == TRUE) - if (AVRC_EVT_VOLUME_CHANGE == p_cmd->reg_notif.event_id) { - status = avrc_bld_vol_change_notfn(p_pkt); - } -#endif + case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */ + status = avrc_bld_set_player_value_cmd(&p_cmd->set_app_val, p_pkt); break; + case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */ + status = avrc_bld_get_element_attr_cmd(&p_cmd->get_elem_attrs, p_pkt); + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ + status = avrc_bld_register_change_notfn(p_cmd->reg_notif.event_id, p_cmd->reg_notif.param, p_pkt); + break; } if (alloc && (status != AVRC_STS_NO_ERROR) ) { diff --git a/components/bt/bluedroid/stack/include/bt_types.h b/components/bt/bluedroid/stack/include/bt_types.h index c60fd3542..1f3f60d8c 100644 --- a/components/bt/bluedroid/stack/include/bt_types.h +++ b/components/bt/bluedroid/stack/include/bt_types.h @@ -276,6 +276,7 @@ typedef struct { /******************************************************************************** ** Macros to get and put bytes to and from a stream (Big Endian format) */ +#define UINT64_TO_BE_STREAM(p, u64) {*(p)++ = (UINT8)((u64) >> 56); *(p)++ = (UINT8)((u64) >> 48); *(p)++ = (UINT8)((u64) >> 40); *(p)++ = (UINT8)((u64) >> 32); *(p)++ = (UINT8)((u64) >> 24); *(p)++ = (UINT8)((u64) >> 16); *(p)++ = (UINT8)((u64) >> 8); *(p)++ = (UINT8)(u64); } #define UINT32_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 24); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } #define UINT24_TO_BE_STREAM(p, u24) {*(p)++ = (UINT8)((u24) >> 16); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)(u24);} #define UINT16_TO_BE_STREAM(p, u16) {*(p)++ = (UINT8)((u16) >> 8); *(p)++ = (UINT8)(u16);} diff --git a/examples/bluetooth/a2dp_sink/main/bt_app_av.c b/examples/bluetooth/a2dp_sink/main/bt_app_av.c index bb6bb90c7..f8fbc39f5 100644 --- a/examples/bluetooth/a2dp_sink/main/bt_app_av.c +++ b/examples/bluetooth/a2dp_sink/main/bt_app_av.c @@ -31,7 +31,6 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param); /* avrc event handler */ static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param); - static uint32_t m_pkt_cnt = 0; static esp_a2d_audio_state_t m_audio_state = ESP_A2D_AUDIO_STATE_STOPPED; @@ -58,11 +57,24 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) } } +void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param) +{ + esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param); + uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1); + memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length); + attr_text[rc->meta_rsp.attr_length] = 0; + + rc->meta_rsp.attr_text = attr_text; +} + void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) { switch (event) { + case ESP_AVRC_CT_METADATA_RSP_EVT: + bt_app_alloc_meta_buffer(param); case ESP_AVRC_CT_CONNECTION_STATE_EVT: - case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: { + case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: + case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: { bt_app_work_dispatch(bt_av_hdl_avrc_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL); break; } @@ -106,6 +118,22 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) } } +static void bt_av_new_track() +{ + //Register notifications and request metadata + esp_avrc_ct_send_metadata_cmd(0, ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE); + esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_TRACK_CHANGE, 0); +} + +void bt_av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter) +{ + switch (event_id) { + case ESP_AVRC_RN_TRACK_CHANGE: + bt_av_new_track(); + break; + } +} + static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param) { ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); @@ -114,13 +142,27 @@ static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param) case ESP_AVRC_CT_CONNECTION_STATE_EVT: { uint8_t *bda = rc->conn_stat.remote_bda; ESP_LOGI(BT_AV_TAG, "avrc conn_state evt: state %d, feature 0x%x, [%02x:%02x:%02x:%02x:%02x:%02x]", - rc->conn_stat.connected, rc->conn_stat.feat_mask, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + rc->conn_stat.connected, rc->conn_stat.feat_mask, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + if (rc->conn_stat.connected) { + bt_av_new_track(); + } break; } case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: { ESP_LOGI(BT_AV_TAG, "avrc passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state); break; } + case ESP_AVRC_CT_METADATA_RSP_EVT: { + ESP_LOGI(BT_AV_TAG, "avrc metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text); + free(rc->meta_rsp.attr_text); + break; + } + case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: { + ESP_LOGI(BT_AV_TAG, "avrc event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter); + bt_av_notify_evt_handler(rc->change_ntf.event_id, rc->change_ntf.event_parameter); + break; + } default: ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); break;