From e93ab60652ad7d17d81b9c309e0ae962d5107201 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Tue, 7 Feb 2017 16:02:54 +0100 Subject: [PATCH] avrcp: get now playing media info --- test/avrcp/avrcp.c | 140 ++++++++++++++++++++++++++++++++++------ test/avrcp/avrcp.h | 18 +++++- test/avrcp/avrcp_test.c | 6 +- 3 files changed, 142 insertions(+), 22 deletions(-) diff --git a/test/avrcp/avrcp.c b/test/avrcp/avrcp.c index 88d93c740..6c2cc5fc6 100644 --- a/test/avrcp/avrcp.c +++ b/test/avrcp/avrcp.c @@ -95,6 +95,24 @@ typedef enum { AVRCP_RESPONSE_FRAME } 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_provider_name = "BTstack AVRCP Controller Service Provider"; 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); } - static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){ switch (connection->state){ 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 opcode; - uint8_t value; int pos = 0; 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 frame_type = (transport_header & 0x03) >> 1; uint8_t ipid = transport_header & 0x01; - value = packet[pos++]; - uint16_t pid = (value << 8) | packet[pos++]; + uint8_t byte_value = packet[pos++]; + uint16_t pid = (byte_value << 8) | packet[pos++]; printf("L2CAP DATA, response: "); 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){ case AVRCP_CMD_OPCODE_UNIT_INFO:{ ctype = packet[pos++]; - value = packet[pos++]; - subunit_type = value >> 3; - subunit_id = value & 0x07; + byte_value = packet[pos++]; + subunit_type = byte_value >> 3; + subunit_id = byte_value & 0x07; opcode = packet[pos++]; // operands: @@ -449,9 +465,9 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec } case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT: ctype = packet[pos++]; - value = packet[pos++]; - subunit_type = value >> 3; - subunit_id = value & 0x07; + byte_value = packet[pos++]; + subunit_type = byte_value >> 3; + subunit_id = byte_value & 0x07; opcode = packet[pos++]; 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); pos += 4; uint8_t status = packet[pos]; - printf_hexdump(packet+pos, size - pos); - printf(" GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %d\n", song_length, song_position, status); + if (status == 0xFF){ + 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; } default: @@ -742,7 +801,7 @@ void avrcp_get_play_status(uint16_t con_handle){ 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); if (!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->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; connection->subunit_id = 0; - big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID); - connection->cmd_operands[3] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; - connection->cmd_operands[4] = 0; // reserved(upper 6) | packet_type -> 0 - big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length - connection->cmd_operands[7] = event_id; - connection->cmd_operands_lenght = 8; + int pos = 0; + big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); + pos += 3; + connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION; + connection->cmd_operands[pos++] = 0; // reserved(upper 6) | packet_type -> 0 + 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_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); + +} \ No newline at end of file diff --git a/test/avrcp/avrcp.h b/test/avrcp/avrcp.h index 63f5814b0..2fe40e565 100644 --- a/test/avrcp/avrcp.h +++ b/test/avrcp/avrcp.h @@ -54,9 +54,18 @@ extern "C" { #define BT_SIG_COMPANY_ID 0x001958 /* 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 { AVRCP_PDU_ID_GET_CAPABILITIES = 0x10, + AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20, AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30, AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31 } avrcp_pdu_id_t; @@ -290,13 +299,18 @@ void avrcp_backward(uint16_t con_handle); */ void avrcp_get_play_status(uint16_t con_handle); - /** * @brief Register notification. * @param con_handle * @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 */ #if defined __cplusplus diff --git a/test/avrcp/avrcp_test.c b/test/avrcp/avrcp_test.c index e7a2e4afa..0bf3d86b9 100644 --- a/test/avrcp/avrcp_test.c +++ b/test/avrcp/avrcp_test.c @@ -128,6 +128,7 @@ static void show_usage(void){ printf("b - avrcp_backward\n"); printf("S - get play status\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("---\n"); } @@ -179,7 +180,10 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac avrcp_get_play_status(con_handle); break; 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; default: show_usage();