mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-02-10 15:40:11 +00:00
avrcp: get now playing media info
This commit is contained in:
parent
7c8a70a56f
commit
e93ab60652
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user