component/bt: Add AVRCP medadata attribute support so A2DP can show track title, album, etc.

Merges https://github.com/espressif/esp-idf/pull/1078
This commit is contained in:
pufstudio 2017-10-03 04:56:13 +02:00 committed by wangmengyang
parent 582b731c23
commit 86fa1820b7
8 changed files with 577 additions and 40 deletions

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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(&param[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, &param[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, &param);
}
/***************************************************************************
* 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);
}

View file

@ -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. */

View file

@ -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) ) {

View file

@ -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);}

View file

@ -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;