avrcp browsing: add set browsed player

This commit is contained in:
Milanka Ringwald 2017-11-22 15:05:11 +01:00
parent 3c90b2d907
commit db79553a49
4 changed files with 441 additions and 83 deletions

View File

@ -66,6 +66,10 @@
#include "btstack_stdin.h"
#endif
#define AVRCP_BROWSING_MAX_PLAYERS 10
#define AVRCP_BROWSING_MAX_FOLDERS 10
#define AVRCP_BROWSING_MAX_FOLDER_NAME_LEN 30
#ifdef HAVE_BTSTACK_STDIN
// mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
// pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5};
@ -87,6 +91,27 @@ static uint8_t sdp_avrcp_browsing_controller_service_buffer[200];
static uint8_t browsing_query_active = 0;
static avrcp_media_item_context_t media_item_context;
typedef struct {
uint16_t charset;
uint8_t depth;
uint16_t name_len;
char name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
} avrcp_browsing_root_folder_t;
typedef struct {
uint8_t uid[8];
uint16_t name_len;
char name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
} avrcp_browsing_folders_t;
static uint8_t parent_folder_set = 0;
static uint8_t parent_folder_uid[8];
static char parent_folder_name[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
static avrcp_browsing_folders_t folders[AVRCP_BROWSING_MAX_FOLDERS];
static int folder_index = -1;
static uint16_t players[AVRCP_BROWSING_MAX_PLAYERS];
static int player_index = -1;
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint8_t ertm_buffer[10000];
@ -101,6 +126,24 @@ static l2cap_ertm_config_t ertm_config = {
};
static inline int next_index(int * index, int max_value){
if ((*index) < max_value){
(*index)++;
} else {
(*index) = 0;
}
return (*index);
}
static int next_folder_index(){
return next_index(&folder_index, AVRCP_BROWSING_MAX_FOLDERS);
}
static int next_player_index(){
return next_index(&player_index, AVRCP_BROWSING_MAX_PLAYERS);
}
/* @section Main Application Setup
*
* @text The Listing MainConfiguration shows how to setup AVRCP Controller Browsing service.
@ -180,9 +223,21 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
pos += 2; // length
switch (data_type){
case AVRCP_BROWSING_MEDIA_ROOT_FOLDER:{
avrcp_browsing_root_folder_t root_folder;
root_folder.charset = big_endian_read_16(packet, pos);
pos += 2;
root_folder.depth = packet[pos++];
root_folder.name_len = big_endian_read_16(packet, pos);
pos += 2;
root_folder.name_len = btstack_max(big_endian_read_16(packet, pos), AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
memset(root_folder.name, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
memcpy(root_folder.name, packet+pos, root_folder.name_len);
printf("Found root folder: name %s, depth %d \n", (char *)root_folder.name, root_folder.depth);
break;
}
case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{
printf("AVRCP Browsing Client: Received media player item \n");
printf("Received media player: ");
uint16_t player_id = big_endian_read_16(packet, pos);
pos += 2;
avrcp_browsing_media_player_major_type_t major_type = packet[pos++];
@ -192,16 +247,17 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
uint8_t feature_bitmask[16];
memcpy(feature_bitmask, packet, 16);
pos += 16;
printf("player ID 0x%04x\n, major_type %d, subtype %d, status %d\n", player_id, major_type, subtype, status);
printf_hexdump(feature_bitmask, 16);
printf("player ID 0x%04x, major_type %d, subtype %d, status %d\n", player_id, major_type, subtype, status);
players[next_player_index()] = player_id;
break;
}
case AVRCP_BROWSING_FOLDER_ITEM:{
printf("AVRCP Browsing Client: Received folder item \n");
int index = next_folder_index();
printf("Found folder [%d]: ", index);
memcpy(folders[index].uid, packet+pos, 8);
uint32_t folder_uid_high = big_endian_read_32(packet, pos);
pos += 4;
uint32_t folder_uid_low = big_endian_read_32(packet, pos+4);
uint32_t folder_uid_low = big_endian_read_32(packet, pos);
pos += 4;
avrcp_browsing_folder_type_t folder_type = packet[pos++];
uint8_t is_playable = packet[pos++];
@ -209,16 +265,19 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
pos += 2;
uint16_t displayable_name_length = big_endian_read_16(packet, pos);
pos += 2;
char value[20];
uint16_t value_len = sizeof(value) > displayable_name_length ? displayable_name_length:sizeof(value)-1;
char value[AVRCP_BROWSING_MAX_FOLDER_NAME_LEN];
memset(value, 0, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN);
uint16_t value_len = btstack_max(displayable_name_length, AVRCP_BROWSING_MAX_FOLDER_NAME_LEN - 1);
memcpy(value, packet+pos, value_len);
value[value_len] = 0;
printf("Folder UID: 0x%08" PRIx32 "%08" PRIx32 ", folder_type 0x%02x, is_playable %d, charset 0x%02x, displayable_name_length %d, value %s\n",
folder_uid_high, folder_uid_low, folder_type, is_playable, charset, displayable_name_length, value);
memcpy(folders[index].name, value, value_len);
folders[index].name_len = value_len;
break;
}
case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{
printf("AVRCP Browsing Client: Received media item \n");
printf("Found media: ");
uint32_t media_uid_high = big_endian_read_32(packet, pos);
pos += 4;
@ -251,7 +310,6 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
break;
}
break;
case HCI_EVENT_PACKET:
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
uint16_t local_cid;
@ -262,13 +320,13 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client: AVRCP Controller connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
printf("AVRCP Controller connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
}
status = avrcp_subevent_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Browsing Client: AVRCP Controller connection failed: status 0x%02x\n", status);
printf("AVRCP Controller connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
@ -276,25 +334,26 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
avrcp_cid = local_cid;
avrcp_connected = 1;
avrcp_subevent_connection_established_get_bd_addr(packet, address);
printf("AVRCP Browsing Client: AVRCP Controller Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(address), avrcp_cid);
printf("AVRCP Controller connected.\n");
return;
}
case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("AVRCP Browsing Client: AVRCP Controller Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
printf("AVRCP Browsing Client released.\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
folder_index = 0;
memset(folders, 0, sizeof(folders));
return;
case AVRCP_SUBEVENT_BROWSING_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_browsing_connection_established_get_browsing_cid(packet);
if (browsing_cid != 0 && browsing_cid != local_cid) {
printf("AVRCP Browsing Client: AVRCP Browsing Controller Connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
printf("AVRCP Browsing Client connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", browsing_cid, local_cid);
return;
}
status = avrcp_subevent_browsing_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){
printf("AVRCP Browsing Client: AVRCP Browsing Controller Connection failed: status 0x%02x\n", status);
printf("AVRCP Browsing Client connection failed: status 0x%02x\n", status);
browsing_cid = 0;
return;
}
@ -302,22 +361,24 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
browsing_cid = local_cid;
avrcp_browsing_connected = 1;
avrcp_subevent_browsing_connection_established_get_bd_addr(packet, address);
printf("AVRCP Browsing Client: AVRCP Browsing Controller Channel successfully opened: %s, browsing_cid 0x%02x\n", bd_addr_to_str(address), browsing_cid);
printf("AVRCP Browsing Client connected\n");
return;
}
case AVRCP_SUBEVENT_BROWSING_CONNECTION_RELEASED:
printf("AVRCP Browsing Client: AVRCP Browsing Controller Channel released: browsing_cid 0x%02x\n", avrcp_subevent_browsing_connection_released_get_browsing_cid(packet));
printf("AVRCP Browsing Client released\n");
browsing_cid = 0;
avrcp_browsing_connected = 0;
return;
case AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE:
printf("AVRCP Browsing Client: query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
browsing_query_active = 0;
if (avrcp_subevent_browsing_media_item_done_get_browsing_status(packet) != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
printf("AVRCP Browsing query done with browsing status 0x%02x, bluetooth status 0x%02x.\n",
avrcp_subevent_browsing_media_item_done_get_browsing_status(packet),
avrcp_subevent_browsing_media_item_done_get_bluetooth_status(packet));
browsing_query_active = 0;
break;
}
printf("AVRCP Browsing cmd: DONE.\n");
break;
default:
printf("AVRCP Browsing Client: event is not parsed\n");
break;
@ -335,11 +396,19 @@ static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth AVRCP Controller Connection Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("a - AVRCP Controller create connection to addr %s\n", bd_addr_to_str(device_addr));
printf("e - AVRCP Controller create connection to addr %s\n", bd_addr_to_str(device_addr));
printf("c - AVRCP Browsing Controller create connection to addr %s\n", bd_addr_to_str(device_addr));
printf("C - AVRCP Browsing Controller disconnect\n");
printf("A - AVRCP Controller disconnect\n");
printf("p - Get player list\n");
printf("E - AVRCP Controller disconnect\n");
printf("a - Set first found player as addressed player\n");
printf("b - Set first found player as browsed player\n");
printf("m - Get media players\n");
printf("f - Browse folders\n");
printf("u - Go up one level\n");
printf("d - Go down one level\n");
printf("i - Browse media items\n");
printf("---\n");
}
#endif
@ -357,11 +426,11 @@ static void stdin_process(char cmd){
}
switch (cmd){
case 'a':
case 'e':
printf(" - Create AVRCP connection for control to addr %s.\n", bd_addr_to_str(device_addr));
status = avrcp_controller_connect(device_addr, &avrcp_cid);
break;
case 'A':
case 'E':
if (avrcp_connected){
printf(" - AVRCP Controller disconnect from addr %s.\n", bd_addr_to_str(device_addr));
status = avrcp_controller_disconnect(avrcp_cid);
@ -386,18 +455,81 @@ static void stdin_process(char cmd){
}
printf("AVRCP Browsing Controller already disconnected\n");
break;
case 'p':
printf("AVRCP Browsing: get player list\n");
avrcp_browsing_controller_get_player_list(browsing_cid);
break;
case '\n':
case '\r':
break;
default:
if (!avrcp_browsing_connected){
show_usage();
printf("Please connect the AVRCP Browsing client\n");
break;
}
switch (cmd) {
case 'a':
if (player_index < 0) {
printf("Get media players first\n");
break;
}
printf("Set addressed player\n");
status = avrcp_browsing_controller_set_addressed_player(browsing_cid, players[0]);
break;
case 'b':
if (player_index < 0) {
printf("Get media players first\n");
break;
}
printf("Set browsed player\n");
status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
break;
case 'm':
printf("AVRCP Browsing: get media players\n");
player_index = -1;
status = avrcp_browsing_controller_get_media_players(browsing_cid);
break;
case 'f':
printf("AVRCP Browsing: browse folders\n");
folder_index = -1;
status = avrcp_browsing_controller_browse_file_system(browsing_cid);
break;
case 'i':
printf("AVRCP Browsing: browse media items\n");
avrcp_browsing_controller_browse_media(browsing_cid);
break;
case 'u':
if (folder_index < 0 && !parent_folder_set){
printf("AVRCP Browsing: no folders available\n");
break;
}
if (!parent_folder_set){
parent_folder_set = 1;
memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
memcpy(parent_folder_uid, folders[0].uid, 8);
}
printf("AVRCP Browsing: go up one level of %s\n", (char *)parent_folder_name);
status = avrcp_browsing_controller_go_up_one_level(browsing_cid, parent_folder_uid);
folder_index = -1;
break;
case 'd':
if (folder_index < 0 && !parent_folder_set){
printf("AVRCP Browsing: no folders available\n");
break;
}
if (!parent_folder_set){
parent_folder_set = 1;
memcpy(parent_folder_name, folders[0].name, folders[0].name_len);
memcpy(parent_folder_uid, folders[0].uid, 8);
}
printf("AVRCP Browsing: go down one level of %s\n", (char *)parent_folder_name);
status = avrcp_browsing_controller_go_down_one_level(browsing_cid, parent_folder_uid);
folder_index = -1;
break;
default:
show_usage();
return;
break;
}
break;
}
if (status != ERROR_CODE_SUCCESS){

View File

@ -124,6 +124,8 @@ typedef enum {
AVRCP_PDU_ID_REQUEST_CONTINUING_RESPONSE = 0x40,
AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE = 0x41,
AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME = 0x50,
AVRCP_PDU_ID_SET_ADDRESSED_PLAYER = 0x60,
AVRCP_PDU_ID_SET_BROWSED_PLAYER = 0x70,
AVRCP_PDU_ID_GET_FOLDER_ITEMS = 0x71,
AVRCP_PDU_ID_CHANGE_PATH = 0x72,
AVRCP_PDU_ID_UNDEFINED = 0xFF
@ -287,6 +289,14 @@ typedef struct {
uint32_t ertm_buffer_size;
l2cap_ertm_config_t ertm_config;
// players
uint8_t set_addressed_player_id;
uint8_t set_browsed_player_id;
uint16_t addressed_player_id;
uint16_t browsed_player_id;
uint16_t browsed_player_uid_counter;
// get folder item
uint8_t get_folder_item;
uint8_t scope;

View File

@ -129,13 +129,13 @@ static uint8_t avrcp_browsing_connect(bd_addr_t remote_addr, avrcp_context_t * c
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection){
printf(" avrcp_browsing_connect connection exists\n");
log_error(" avrcp_browsing_connect connection exists.");
return ERROR_CODE_SUCCESS;
}
connection = avrcp_browsing_create_connection(avrcp_connection);
if (!connection){
printf("avrcp: could not allocate connection struct.");
log_error("avrcp: could not allocate connection struct.");
return BTSTACK_MEMORY_ALLOC_FAILED;
}
@ -183,7 +183,7 @@ void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
log_info("L2CAP_EVENT_INCOMING_CONNECTION browsing_cid 0x%02x, l2cap_signaling_cid 0x%02x", avrcp_connection->avrcp_browsing_cid, connection->l2cap_browsing_cid);
// l2cap_accept_connection(local_cid);
printf("L2CAP Accepting incoming connection request in ERTM\n");
log_error("L2CAP Accepting incoming connection request in ERTM.");
l2cap_accept_ertm_connection(local_cid, &connection->ertm_config, connection->ertm_buffer, connection->ertm_buffer_size);
break;
@ -258,7 +258,6 @@ static int avrcp_browsing_controller_send_get_folder_items_cmd(uint16_t cid, avr
pos += 4;
big_endian_store_32(command, pos, connection->end_item);
pos += 4;
command[pos++] = connection->attribute_count;
if (connection->attribute_count){
memcpy(command+pos, connection->attribute_list, connection->attribute_count);
@ -267,6 +266,62 @@ static int avrcp_browsing_controller_send_get_folder_items_cmd(uint16_t cid, avr
return l2cap_send(cid, command, pos);
}
static int avrcp_browsing_controller_send_change_path_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
uint8_t command[100];
int pos = 0;
// transport header
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
// Profile IDentifier (PID)
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
command[pos++] = AVRCP_PDU_ID_CHANGE_PATH;
big_endian_store_16(command, pos, 11);
pos += 2;
big_endian_store_16(command, pos, connection->browsed_player_uid_counter);
pos += 2;
command[pos++] = connection->direction;
memcpy(command+pos, connection->folder_uid, 8);
pos += 8;
return l2cap_send(cid, command, pos);
}
static int avrcp_browsing_controller_send_set_browsed_player_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
uint8_t command[100];
int pos = 0;
// transport header
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
// Profile IDentifier (PID)
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
command[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
big_endian_store_16(command, pos, 2);
pos += 2;
big_endian_store_16(command, pos, connection->browsed_player_id);
pos += 2;
return l2cap_send(cid, command, pos);
}
static int avrcp_browsing_controller_send_set_addressed_player_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){
uint8_t command[100];
int pos = 0;
// transport header
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
// Profile IDentifier (PID)
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
command[pos++] = AVRCP_PDU_ID_SET_ADDRESSED_PLAYER;
big_endian_store_16(command, pos, 2);
pos += 2;
big_endian_store_16(command, pos, connection->addressed_player_id);
pos += 2;
return l2cap_send(cid, command, pos);
}
static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection){
switch (connection->state){
@ -277,7 +332,26 @@ static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connect
avrcp_browsing_controller_send_get_folder_items_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->change_path){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->change_path = 0;
avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->set_browsed_player_id){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->set_browsed_player_id = 0;
avrcp_browsing_controller_send_set_browsed_player_cmd(connection->l2cap_browsing_cid, connection);
break;
}
if (connection->set_addressed_player_id){
connection->state = AVCTP_W2_RECEIVE_RESPONSE;
connection->set_addressed_player_id = 0;
avrcp_browsing_controller_send_set_addressed_player_cmd(connection->l2cap_browsing_cid, connection);
break;
}
default:
return;
}
@ -315,35 +389,55 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16
avrcp_pdu_id_t pdu_id = packet[pos++];
uint16_t length = big_endian_read_16(packet, pos);
pos += 2;
uint8_t browsing_status = packet[pos++];
if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
}
if (size + pos < length){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
break;
}
uint8_t browsing_status = packet[pos++];
if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
}
uint32_t i;
switch(pdu_id){
case AVRCP_PDU_ID_CHANGE_PATH:
case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER:
break;
case AVRCP_PDU_ID_SET_BROWSED_PLAYER:{
browsing_connection->browsed_player_uid_counter = big_endian_read_16(packet, pos);
pos += 2;
uint32_t num_items = big_endian_read_32(packet, pos);
pos += 4;
for (i = 0; i < num_items; i++){
uint16_t browsable_item_length = 5 + big_endian_read_16(packet, pos+3);
// reuse byte to put the new type AVRCP_BROWSING_MEDIA_ROOT_FOLDER
packet[pos-1] = AVRCP_BROWSING_MEDIA_ROOT_FOLDER;
(*avrcp_controller_context.avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length+1);
pos += browsable_item_length;
}
break;
}
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{
// uint16_t uid_counter = big_endian_read_16(packet, pos);
pos += 2;
uint16_t num_items = big_endian_read_16(packet, pos);
pos += 2;
int i;
switch(pdu_id){
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
for (i = 0; i < num_items; i++){
uint16_t browsable_item_length = big_endian_read_16(packet, pos+1);
uint16_t browsable_item_length = 3 + big_endian_read_16(packet, pos+1);
(*avrcp_controller_context.avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length);
pos += 3 + browsable_item_length;
pos += browsable_item_length;
}
break;
}
default:
return;
}
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS);
break;
default:
break;
}
break;
}
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){
@ -390,7 +484,7 @@ uint8_t avrcp_browsing_controller_disconnect(uint16_t avrcp_browsing_cid){
* @param attribute_count
* @param attribute_list
**/
uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t attribute_count, uint8_t * attribute_list){
static uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t attribute_count, uint8_t * attribute_list){
avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context);
if (!avrcp_connection){
log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
@ -410,6 +504,97 @@ uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid,
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_browsing_controller_get_player_list(uint16_t avrcp_browsing_cid){
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 0x01, 0, 0xFFFFFFFF, 0, NULL);
uint8_t avrcp_browsing_controller_get_media_players(uint16_t avrcp_browsing_cid){
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 0, 0, 0xFFFFFFFF, 0, NULL);
}
uint8_t avrcp_browsing_controller_browse_file_system(uint16_t avrcp_browsing_cid){
// return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, 0, 0xFFFFFFFF, 0, NULL);
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, 0, 0x05, 0, NULL);
}
uint8_t avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid){
// return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, 0, 0xFFFFFFFF, 0, NULL);
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, 0, 0x05, 0, NULL);
}
uint8_t avrcp_browsing_controller_browse_now_playing_list(uint16_t avrcp_browsing_cid){
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 3, 0, 0xFFFFFFFF, 0, NULL);
}
uint8_t avrcp_browsing_controller_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t browsed_player_id){
avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context);
if (!avrcp_connection){
log_error("avrcp_browsing_controller_change_path: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection->state != AVCTP_CONNECTION_OPENED){
log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection->set_browsed_player_id = 1;
connection->browsed_player_id = browsed_player_id;
avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid);
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_browsing_controller_set_addressed_player(uint16_t avrcp_browsing_cid, uint16_t addressed_player_id){
avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context);
if (!avrcp_connection){
log_error("avrcp_browsing_controller_change_path: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection->state != AVCTP_CONNECTION_OPENED){
log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection->set_addressed_player_id = 1;
connection->addressed_player_id = addressed_player_id;
avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid);
return ERROR_CODE_SUCCESS;
}
/**
* @brief Retrieve a listing of the contents of a folder.
* @param direction 0-folder up, 1-folder down
* @param folder_uid 8 bytes long
**/
uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8_t direction, uint8_t * folder_uid){
avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context);
if (!avrcp_connection){
log_error("avrcp_browsing_controller_change_path: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
if (connection->state != AVCTP_CONNECTION_OPENED){
log_error("avrcp_browsing_controller_change_path: connection in wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
if (!connection->browsed_player_id){
log_error("avrcp_browsing_controller_change_path: no browsed player set.");
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection->change_path = 1;
connection->direction = direction;
memcpy(connection->folder_uid, folder_uid, 8);
avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid);
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid){
return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 0, folder_uid);
}
uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid){
return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 1, folder_uid);
}

View File

@ -58,7 +58,8 @@ extern "C" {
typedef enum {
AVRCP_BROWSING_MEDIA_PLAYER_ITEM = 0x01,
AVRCP_BROWSING_FOLDER_ITEM,
AVRCP_BROWSING_MEDIA_ELEMENT_ITEM
AVRCP_BROWSING_MEDIA_ELEMENT_ITEM,
AVRCP_BROWSING_MEDIA_ROOT_FOLDER
} avrcp_browsing_item_type_t;
typedef enum {
@ -127,21 +128,51 @@ uint8_t avrcp_browsing_controller_connect(bd_addr_t bd_addr, uint8_t * ertm_buff
uint8_t avrcp_browsing_controller_disconnect(uint16_t avrcp_browsing_cid);
/**
* @brief Retrieve a listing of the contents of a folder.
* @brief Retrieve a list of media players.
* @param avrcp_browsing_cid
* @param scope 0-player list, 1-virtual file system, 2-search, 3-now playing
* @param start_item
* @param end_item
* @param attribute_count
* @param attribute_list
**/
uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t attribute_count, uint8_t * attribute_list);
uint8_t avrcp_browsing_controller_get_media_players(uint16_t avrcp_browsing_cid);
/**
* @brief Retrieve a player list.
* @brief Retrieve a list of folders and media items of the browsed player.
* @param avrcp_browsing_cid
**/
uint8_t avrcp_browsing_controller_get_player_list(uint16_t avrcp_browsing_cid);
uint8_t avrcp_browsing_controller_browse_file_system(uint16_t avrcp_browsing_cid);
/**
* @brief Retrieve a list of media items of the browsed player.
* @param avrcp_browsing_cid
**/
uint8_t avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid);
/**
* @brief Retrieve a list of folders and media items of the addressed player.
* @param avrcp_browsing_cid
**/
uint8_t avrcp_browsing_controller_browse_now_playing_list(uint16_t avrcp_browsing_cid);
/**
* @brief Set browsed player. Calling this command is required prior to browsing the player's file system. Some players may support browsing only when set as the Addressed Player.
* @param avrcp_browsing_cid
* @param browsed_player_id
*/
uint8_t avrcp_browsing_controller_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t browsed_player_id);
/**
* @brief Set addressed player.
* @param avrcp_browsing_cid
* @param addressed_player_id
*/
uint8_t avrcp_browsing_controller_set_addressed_player(uint16_t avrcp_browsing_cid, uint16_t addressed_player_id);
/**
* @brief Navigate one level up or down in thhe virtual filesystem. Requires that s browsed player is set.
* @param direction 0-folder up, 1-folder down
* @param folder_uid 8 bytes long
**/
uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8_t direction, uint8_t * folder_uid);
uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid);
uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid);
/* API_END */