avrcp: get now playing media info

This commit is contained in:
Milanka Ringwald 2017-02-07 16:02:54 +01:00 committed by Matthias Ringwald
parent 7c8a70a56f
commit e93ab60652
3 changed files with 142 additions and 22 deletions

View File

@ -95,6 +95,24 @@ typedef enum {
AVRCP_RESPONSE_FRAME AVRCP_RESPONSE_FRAME
} avrcp_frame_type_t; } avrcp_frame_type_t;
static const char * avrcp_play_status_name[] = {
"STOPPED",
"PLAYING",
"PAUSED",
"FORWARD SEEK",
"REVERSE SEEK"
};
// static const char * avrcp_media_attribute_id_name[] = {
// "NONE",
// "TITLE",
// "ARTIST",
// "ALBUM",
// "TOTAL TRACKS",
// "GENRE",
// "SONG LENGTH"
// };
static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service"; static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service";
static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider"; static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider";
static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service"; static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service";
@ -387,7 +405,6 @@ static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){
return l2cap_send(cid, command, pos); return l2cap_send(cid, command, pos);
} }
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
switch (connection->state){ switch (connection->state){
case AVCTP_W2_RECEIVE_PRESS_RESPONSE: case AVCTP_W2_RECEIVE_PRESS_RESPONSE:
@ -414,7 +431,6 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint8_t operands[20]; uint8_t operands[20];
uint8_t opcode; uint8_t opcode;
uint8_t value;
int pos = 0; int pos = 0;
uint8_t transport_header = packet[pos++]; uint8_t transport_header = packet[pos++];
@ -422,8 +438,8 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint8_t packet_type = (transport_header & 0x0F) >> 2; uint8_t packet_type = (transport_header & 0x0F) >> 2;
uint8_t frame_type = (transport_header & 0x03) >> 1; uint8_t frame_type = (transport_header & 0x03) >> 1;
uint8_t ipid = transport_header & 0x01; uint8_t ipid = transport_header & 0x01;
value = packet[pos++]; uint8_t byte_value = packet[pos++];
uint16_t pid = (value << 8) | packet[pos++]; uint16_t pid = (byte_value << 8) | packet[pos++];
printf("L2CAP DATA, response: "); printf("L2CAP DATA, response: ");
printf_hexdump(packet, size); printf_hexdump(packet, size);
@ -433,9 +449,9 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
switch (connection->cmd_to_send){ switch (connection->cmd_to_send){
case AVRCP_CMD_OPCODE_UNIT_INFO:{ case AVRCP_CMD_OPCODE_UNIT_INFO:{
ctype = packet[pos++]; ctype = packet[pos++];
value = packet[pos++]; byte_value = packet[pos++];
subunit_type = value >> 3; subunit_type = byte_value >> 3;
subunit_id = value & 0x07; subunit_id = byte_value & 0x07;
opcode = packet[pos++]; opcode = packet[pos++];
// operands: // operands:
@ -449,9 +465,9 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
} }
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
ctype = packet[pos++]; ctype = packet[pos++];
value = packet[pos++]; byte_value = packet[pos++];
subunit_type = value >> 3; subunit_type = byte_value >> 3;
subunit_id = value & 0x07; subunit_id = byte_value & 0x07;
opcode = packet[pos++]; opcode = packet[pos++];
if (size - pos < 7) { if (size - pos < 7) {
@ -478,8 +494,51 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint32_t song_position = big_endian_read_32(packet, pos); uint32_t song_position = big_endian_read_32(packet, pos);
pos += 4; pos += 4;
uint8_t status = packet[pos]; uint8_t status = packet[pos];
printf_hexdump(packet+pos, size - pos); if (status == 0xFF){
printf(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %d\n", song_length, song_position, status); printf(" GET_PLAY_STATUS ERROR\n");
} else {
printf(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s\n", song_length, song_position, avrcp_play_status_name[status]);
}
break;
}
case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{
uint8_t event_id = packet[pos++];
uint8_t status;
printf(" REGISTER_NOTIFICATION: ");
switch (event_id){
case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:
status = packet[pos];
if (status == 0xFF){
printf("PLAYBACK_STATUS_CHANGED ERROR\n");
} else {
printf("PLAYBACK_STATUS_CHANGED status %s\n", avrcp_play_status_name[status]);
}
break;
default:
printf("not implemented\n");
break;
}
break;
}
case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{
uint8_t num_attributes = packet[pos++];
int i;
for (i = 0; i < num_attributes; i++){
avrcp_media_attribute_id_t attr_id = big_endian_read_32(packet, pos);
pos += 4;
// uint16_t character_set = big_endian_read_16(packet, pos);
pos += 2;
uint16_t attr_value_length = big_endian_read_16(packet, pos);
pos += 2;
uint8_t value[100];
uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length;
memcpy(value, packet+pos, value_len);
value[value_len] = 0;
printf("attr id %d: %s \n", attr_id, value);
pos += attr_value_length;
}
break; break;
} }
default: default:
@ -742,7 +801,7 @@ void avrcp_get_play_status(uint16_t con_handle){
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
} }
void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id){ void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle); avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){ if (!connection){
log_error("avrcp_get_play_status: coud not find a connection."); log_error("avrcp_get_play_status: coud not find a connection.");
@ -756,12 +815,55 @@ void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_i
connection->command_type = AVRCP_CTYPE_NOTIFY; connection->command_type = AVRCP_CTYPE_NOTIFY;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
connection->subunit_id = 0; connection->subunit_id = 0;
big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); int pos = 0;
connection->cmd_operands[3] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 pos += 3;
big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION;
connection->cmd_operands[7] = event_id; connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0
connection->cmd_operands_lenght = 8; big_endian_store_16(connection->cmd_operands, pos, 5); // parameter length
pos += 2;
connection->cmd_operands[pos++] = event_id;
big_endian_store_32(connection->cmd_operands, pos, playback_interval_in_seconds);
pos += 4;
connection->cmd_operands_lenght = pos;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
// AVRCP_SPEC_V14.pdf 166 // AVRCP_SPEC_V14.pdf 166
} }
void avrcp_get_now_playing_info(uint16_t con_handle){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_capabilities: coud not find a connection.");
return;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return;
connection->state = AVCTP_W2_SEND_COMMAND;
connection->transaction_label++;
connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
connection->command_type = AVRCP_CTYPE_STATUS;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
connection->subunit_id = 0;
int pos = 0;
big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
pos += 3;
connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID
connection->cmd_operands[pos++] = 0;
// Parameter Length
big_endian_store_16(connection->cmd_operands, pos, 9);
pos += 2;
// write 8 bytes value
memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING
pos += 8;
connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes
// big_endian_store_32(connection->cmd_operands, pos, TitleOfMedia); // 0x1
// pos += 4;
connection->cmd_operands_lenght = pos;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}

View File

@ -54,9 +54,18 @@ extern "C" {
#define BT_SIG_COMPANY_ID 0x001958 #define BT_SIG_COMPANY_ID 0x001958
/* API_START */ /* API_START */
typedef enum {
AVRCP_MEDIA_ATTR_TITLE = 1,
AVRCP_MEDIA_ATTR_ARTIST,
AVRCP_MEDIA_ATTR_ALBUM,
AVRCP_MEDIA_ATTR_TOTAL_TRACKS,
AVRCP_MEDIA_ATTR_GENRE,
AVRCP_MEDIA_ATTR_SONG_LENGTH
} avrcp_media_attribute_id_t;
typedef enum { typedef enum {
AVRCP_PDU_ID_GET_CAPABILITIES = 0x10, AVRCP_PDU_ID_GET_CAPABILITIES = 0x10,
AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20,
AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30, AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30,
AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31 AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31
} avrcp_pdu_id_t; } avrcp_pdu_id_t;
@ -290,13 +299,18 @@ void avrcp_backward(uint16_t con_handle);
*/ */
void avrcp_get_play_status(uint16_t con_handle); void avrcp_get_play_status(uint16_t con_handle);
/** /**
* @brief Register notification. * @brief Register notification.
* @param con_handle * @param con_handle
* @param event_id * @param event_id
*/ */
void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id); void avrcp_register_notification(uint16_t con_handle, avrcp_notification_event_id_t event_id, uint32_t playback_interval_in_seconds);
/**
* @brief Get info on now playing media.
* @param con_handle
*/
void avrcp_get_now_playing_info(uint16_t con_handle);
/* API_END */ /* API_END */
#if defined __cplusplus #if defined __cplusplus

View File

@ -128,6 +128,7 @@ static void show_usage(void){
printf("b - avrcp_backward\n"); printf("b - avrcp_backward\n");
printf("S - get play status\n"); printf("S - get play status\n");
printf("N - register notification, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED\n"); printf("N - register notification, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED\n");
printf("I - get now playing info\n");
printf("Ctrl-c - exit\n"); printf("Ctrl-c - exit\n");
printf("---\n"); printf("---\n");
} }
@ -179,7 +180,10 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
avrcp_get_play_status(con_handle); avrcp_get_play_status(con_handle);
break; break;
case 'N': case 'N':
avrcp_register_notification(con_handle, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED); avrcp_register_notification(con_handle, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED, 2);
break;
case 'I':
avrcp_get_now_playing_info(con_handle);
break; break;
default: default:
show_usage(); show_usage();