]> granicus.if.org Git - esp-idf/commitdiff
component/bt: Add AVRCP medadata attribute support so A2DP can show track title,...
authorpufstudio <18217237+pufstudio@users.noreply.github.com>
Tue, 3 Oct 2017 02:56:13 +0000 (04:56 +0200)
committerwangmengyang <wangmengyang@espressif.com>
Fri, 1 Dec 2017 11:28:52 +0000 (19:28 +0800)
Merges https://github.com/espressif/esp-idf/pull/1078

components/bt/bluedroid/api/esp_avrc_api.c
components/bt/bluedroid/api/include/esp_avrc_api.h
components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c
components/bt/bluedroid/btc/profile/std/avrc/btc_avrc.c
components/bt/bluedroid/btc/profile/std/include/btc_avrc.h
components/bt/bluedroid/stack/avrc/avrc_bld_ct.c
components/bt/bluedroid/stack/include/bt_types.h
examples/bluetooth/a2dp_sink/main/bt_app_av.c

index cf4a8e003db7a835e55313285f745141fc95df55..2523665c2061a2f1baf51acc7f6f493b3c069094 100644 (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;
index e30742098f851ac5b8864ea0d39dade60c1828d1..df320b80ae67cd16acb112d2d84abc8509078e4e 100644 (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
index b376e0bd1c64a6cbe0144850bf12945ce5a3555e..7c04cbfe09f6a689f94b41b567956f39636f0869 100644 (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);
index 838183970b2cb81f13277c51554e839e085fd27c..8c446235706db038117fafd522e493513bd104ab 100644 (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_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t key_state)
+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 (tl >= 16 ||
-            key_state > ESP_AVRC_PT_CMD_STATE_RELEASED) {
-        return ESP_ERR_INVALID_ARG;
+
+#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 (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);
     }
index 3f16d10f3cca7362d7c72e2ebead6e17537475c5..1ea3c2dbbf076c96995f0ab0761c05bf957bf6fd 100644 (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. */
index 41ca87bf857ed57e3de6a20eb8f293f715df37c8..ecd804388cf602604e730eb5fc9f6a5b3ac191d0 100644 (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) ) {
index c60fd354272fc38ebfb8862709eb86410c0847e0..1f3f60d8caa9b80c12e28c7b2a1ab63347ebd5a7 100644 (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);}
index bb6bb90c7cd9c1a16b48e589194289a82efb3298..f8fbc39f55a6d8bf2882de8bc8598642d4e0ee2a 100644 (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;