mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-31 09:32:57 +00:00
avrcp: add media item iterator
This commit is contained in:
parent
6afb9aca51
commit
954cc391b7
@ -108,6 +108,7 @@ HXCMOD_PLAYER = \
|
||||
${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c \
|
||||
|
||||
EXAMPLES = \
|
||||
avrcp_browsing_client \
|
||||
a2dp_sink_demo \
|
||||
a2dp_source_demo \
|
||||
ancs_client_demo \
|
||||
@ -276,7 +277,7 @@ a2dp_source_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_E
|
||||
a2dp_sink_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_ENCODER_OBJ} ${SBC_DECODER_OBJ} ${AVDTP_OBJ} avrcp.o avrcp_controller.o a2dp_sink_demo.c
|
||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
avrcp_browsing_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} avrcp.o avrcp_controller.o avrcp_browsing_controller.o avrcp_browsing_client.c
|
||||
avrcp_browsing_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} avrcp.o avrcp_controller.o avrcp_browsing_controller.o avrcp_media_item_iterator.o avrcp_browsing_client.c
|
||||
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
|
||||
|
||||
dut_mode_classic: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} dut_mode_classic.c
|
||||
|
@ -55,12 +55,12 @@
|
||||
// *****************************************************************************
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "btstack.h"
|
||||
#include "avrcp_browsing_controller.h"
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
#include "btstack_stdin.h"
|
||||
@ -84,6 +84,9 @@ static uint16_t browsing_cid = 0;
|
||||
static uint8_t avrcp_browsing_connected = 0;
|
||||
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;
|
||||
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
static uint8_t ertm_buffer[10000];
|
||||
@ -165,73 +168,166 @@ int btstack_main(int argc, const char * argv[]){
|
||||
}
|
||||
/* LISTING_END */
|
||||
|
||||
|
||||
static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
if (packet_type != HCI_EVENT_PACKET) return;
|
||||
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
|
||||
|
||||
uint16_t local_cid;
|
||||
uint8_t status = 0xFF;
|
||||
bd_addr_t adress;
|
||||
|
||||
switch (packet[2]){
|
||||
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);
|
||||
return;
|
||||
}
|
||||
int pos;
|
||||
|
||||
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);
|
||||
browsing_cid = 0;
|
||||
return;
|
||||
}
|
||||
switch(packet_type){
|
||||
case AVRCP_BROWSING_DATA_PACKET:
|
||||
pos = 0;
|
||||
browsing_query_active = 1;
|
||||
avrcp_browsing_item_type_t data_type = (avrcp_browsing_item_type_t)packet[pos++];
|
||||
pos += 2; // length
|
||||
|
||||
switch (data_type){
|
||||
case AVRCP_BROWSING_MEDIA_PLAYER_ITEM:{
|
||||
printf("AVRCP Browsing Client: Received media player item \n");
|
||||
|
||||
avrcp_cid = local_cid;
|
||||
avrcp_connected = 1;
|
||||
avrcp_subevent_connection_established_get_bd_addr(packet, adress);
|
||||
printf("AVRCP Browsing Client: AVRCP Controller Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(adress), avrcp_cid);
|
||||
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));
|
||||
browsing_cid = 0;
|
||||
avrcp_browsing_connected = 0;
|
||||
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);
|
||||
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);
|
||||
browsing_cid = 0;
|
||||
return;
|
||||
}
|
||||
uint16_t player_id = big_endian_read_16(packet, pos);
|
||||
pos += 2;
|
||||
avrcp_browsing_media_player_major_type_t major_type = packet[pos++];
|
||||
avrcp_browsing_media_player_subtype_t subtype = big_endian_read_32(packet, pos);
|
||||
pos += 4;
|
||||
uint8_t status = packet[pos++];
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case AVRCP_BROWSING_FOLDER_ITEM:{
|
||||
printf("AVRCP Browsing Client: Received folder item \n");
|
||||
|
||||
browsing_cid = local_cid;
|
||||
avrcp_browsing_connected = 1;
|
||||
avrcp_subevent_browsing_connection_established_get_bd_addr(packet, adress);
|
||||
printf("AVRCP Browsing Client: AVRCP Browsing Controller Channel successfully opened: %s, browsing_cid 0x%02x\n", bd_addr_to_str(adress), browsing_cid);
|
||||
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));
|
||||
browsing_cid = 0;
|
||||
avrcp_browsing_connected = 0;
|
||||
return;
|
||||
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);
|
||||
pos += 4;
|
||||
avrcp_browsing_folder_type_t folder_type = packet[pos++];
|
||||
uint8_t is_playable = packet[pos++];
|
||||
uint16_t charset = big_endian_read_16(packet, pos);
|
||||
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;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case AVRCP_BROWSING_MEDIA_ELEMENT_ITEM:{
|
||||
printf("AVRCP Browsing Client: Received media item \n");
|
||||
|
||||
uint32_t media_uid_high = big_endian_read_32(packet, pos);
|
||||
pos += 4;
|
||||
uint32_t media_uid_low = big_endian_read_32(packet, pos+4);
|
||||
pos += 4;
|
||||
avrcp_browsing_media_type_t media_type = packet[pos++];
|
||||
uint16_t charset = big_endian_read_16(packet, pos);
|
||||
pos += 2;
|
||||
uint16_t displayable_name_length = big_endian_read_16(packet, pos);
|
||||
pos += 2;
|
||||
pos += displayable_name_length;
|
||||
printf("Media UID: 0x%08" PRIx32 "%08" PRIx32 ", media_type 0x%02x, charset 0x%02x, displayable_name_length %d\n", media_uid_high, media_uid_low, media_type, charset, displayable_name_length);
|
||||
|
||||
uint8_t num_attributes = packet[pos++];
|
||||
printf("Num media attributes %d\n", num_attributes);
|
||||
|
||||
for (avrcp_media_item_iterator_init(&media_item_context, size-pos, packet+pos); avrcp_media_item_iterator_has_more(&media_item_context); avrcp_media_item_iterator_next(&media_item_context)){
|
||||
uint32_t attr_id = avrcp_media_item_iterator_get_attr_id(&media_item_context);
|
||||
uint16_t attr_charset = avrcp_media_item_iterator_get_attr_charset(&media_item_context);
|
||||
uint16_t attr_value_length = avrcp_media_item_iterator_get_attr_value_len(&media_item_context);
|
||||
const uint8_t * attr_value = avrcp_media_item_iterator_get_attr_value(&media_item_context);
|
||||
|
||||
printf("Attr ID 0x%08" PRIx32 ", charset %d, attr_value_length %d, value %s", attr_id, attr_charset, attr_value_length, attr_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
log_error("AVRCP browsing: unknown browsable item type 0%02x", data_type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_EVENT_PACKET:
|
||||
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
|
||||
uint16_t local_cid;
|
||||
uint8_t status = 0xFF;
|
||||
bd_addr_t address;
|
||||
|
||||
switch (packet[2]){
|
||||
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);
|
||||
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);
|
||||
browsing_cid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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));
|
||||
browsing_cid = 0;
|
||||
avrcp_browsing_connected = 0;
|
||||
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);
|
||||
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);
|
||||
browsing_cid = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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));
|
||||
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",
|
||||
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;
|
||||
|
||||
default:
|
||||
printf("AVRCP Browsing Client: event is not parsed\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("AVRCP Browsing Client: event is not parsed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
@ -253,6 +349,13 @@ static void show_usage(void){
|
||||
static void stdin_process(char cmd){
|
||||
uint8_t status = ERROR_CODE_SUCCESS;
|
||||
|
||||
if (cmd != 'a' && cmd != 'A' && cmd != 'c' && cmd != 'C'){
|
||||
if (browsing_query_active){
|
||||
printf("Query active, try later!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmd){
|
||||
case 'a':
|
||||
printf(" - Create AVRCP connection for control to addr %s.\n", bd_addr_to_str(device_addr));
|
||||
@ -283,7 +386,7 @@ 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);
|
||||
|
@ -245,6 +245,33 @@ typedef enum {
|
||||
#define ERROR_CODE_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING 0x40
|
||||
/* ENUM_END */
|
||||
|
||||
/* ENUM_START: AVRCP_BROWSING_ERROR_CODE */
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND 0x00 // Sent if TG received a PDU that it did not understand. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_PARAMETER 0x01 // Sent if the TG received a PDU with a parameter ID that it did not understand. Sent if there is only one parameter ID in the PDU. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SPECIFIED_PARAMETER_NOT_FOUND 0x02 // Sent if the parameter ID is understood, but content is wrong or corrupted. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INTERNAL_ERROR 0x03 // Sent if there are error conditions not covered by a more specific error code. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SUCCESS 0x04 // This is the status that should be returned if the operation was successful. Valid for All except where the response CType is AV/C REJECTED.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_UID_CHANGED 0x05 // The UIDs on the device have changed. Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_RESERVED_06 0x06 // Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_DIRECTION 0x07 // The Direction parameter is invalid. Valid for Change Path.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NOT_A_DIRECTORY 0x08 // The UID provided does not refer to a folder item. Valid for Change Path.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_DOES_NOT_EXIST 0x09 // The UID provided does not refer to any currently valid. Valid for Change Path, PlayItem, AddToNowPlaying, GetItemAttributes.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_SCOPE 0x0a // The scope parameter is invalid. Valid for GetFolderItems, PlayItem, AddToNowPlayer, GetItemAttributes,.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_RANGE_OUT_OF_BOUNDS 0x0b // The start of range provided is not valid. Valid for GetFolderItems.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_UID_IS_A_DIRECTORY 0x0c // The UID provided refers to a directory, which cannot be handled by this media player. Valid for PlayItem, AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_MEDIA_IN_USES 0x0d // The media is not able to be used for this operation at this time. Valid for PlayItem, AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NOW_PLAYING_LIST_FULL 0x0e // No more items can be added to the Now Playing List. Valid for AddToNowPlaying.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SEARCH_NOT_SUPPORTED 0x0f // The Browsed Media Player does not support search. Valid for Search.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_SEARCH_IN_PROGRESS 0x10 // A search operation is already in progress. Valid for Search.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_INVALID_PLAYER_ID 0x11 // The specified Player Id does not refer to a valid player. Valid for SetAddressedPlayer, SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_PLAYER_NOT_BROWSABLE 0x12 // The Player Id supplied refers to a Media Player which does not support browsing. Valid for SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_PLAYER_NOT_ADDRESSED 0x13 // The Player Id supplied refers to a player which is not currently addressed, and the command is not able to be performed if the player is not set as addressed. Valid for Search SetBrowsedPlayer.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NO_VALID_SEARCH_RESULTS 0x14 // The Search result list does not contain valid entries, e.g. after being invalidated due to change of browsed player. Valid for GetFolderItems.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_NO_AVAILABLE_PLAYERS 0x15 // Valid for All.
|
||||
#define AVRCP_BROWSING_ERROR_CODE_ADDRESSED_PLAYER_CHANGED 0x16 // Valid for Register Notification.
|
||||
// 0x17-0xff Reserved
|
||||
/* ENUM_END */
|
||||
|
||||
// HCI roles
|
||||
#define HCI_ROLE_MASTER 0
|
||||
#define HCI_ROLE_SLAVE 1
|
||||
|
@ -92,7 +92,9 @@
|
||||
#include "classic/avdtp_source.h"
|
||||
#include "classic/avdtp_util.h"
|
||||
#include "classic/avrcp.h"
|
||||
#include "classic/avrcp_browsing_controller.h"
|
||||
#include "classic/avrcp_controller.h"
|
||||
#include "classic/avrcp_media_item_iterator.h"
|
||||
#include "classic/avrcp_target.h"
|
||||
#include "classic/bnep.h"
|
||||
#include "classic/btstack_link_key_db.h"
|
||||
|
@ -189,7 +189,6 @@ typedef uint8_t sm_key_t[16];
|
||||
#define AVDTP_CONNECTION_IN_WRONG_STATE 0xC2
|
||||
#define AVDTP_STREAM_ENDPOINT_IN_WRONG_STATE 0xC3
|
||||
#define AVDTP_MEDIA_CONNECTION_DOES_NOT_EXIST 0xC4
|
||||
|
||||
/* ENUM_END */
|
||||
|
||||
// DAEMON COMMANDS
|
||||
@ -1809,9 +1808,11 @@ typedef uint8_t sm_key_t[16];
|
||||
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param browsing_cid
|
||||
* @param browsing_status
|
||||
* @param bluetooth_status
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE 0x1D
|
||||
|
||||
|
@ -5759,6 +5759,24 @@ static inline uint16_t avrcp_subevent_browsing_connection_released_get_browsing_
|
||||
static inline uint16_t avrcp_subevent_browsing_media_item_done_get_browsing_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field browsing_status from event AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE
|
||||
* @param event packet
|
||||
* @return browsing_status
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_browsing_media_item_done_get_browsing_status(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field bluetooth_status from event AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE
|
||||
* @param event packet
|
||||
* @return bluetooth_status
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_browsing_media_item_done_get_bluetooth_status(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
|
||||
|
@ -283,59 +283,74 @@ static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connect
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void avrcp_browsing_controller_emit_done(btstack_packet_handler_t callback, uint16_t browsing_cid){
|
||||
static void avrcp_browsing_controller_emit_done(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){
|
||||
if (!callback) return;
|
||||
uint8_t event[5];
|
||||
uint8_t event[6];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_AVRCP_META;
|
||||
event[pos++] = sizeof(event) - 2;
|
||||
event[pos++] = AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE;
|
||||
little_endian_store_16(event, pos, browsing_cid);
|
||||
pos += 2;
|
||||
event[pos++] = browsing_status;
|
||||
event[pos++] = bluetooth_status;
|
||||
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||
}
|
||||
|
||||
static void avrcp_browsing_controller_handle_l2cap_data_packet(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
|
||||
UNUSED(size);
|
||||
printf("avrcp_browsing_controller_handle_l2cap_data_packet \n");
|
||||
printf_hexdump(packet, size);
|
||||
|
||||
int pos = 3;
|
||||
avrcp_pdu_id_t pdu_id = packet[pos++];
|
||||
|
||||
switch(pdu_id){
|
||||
case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
|
||||
printf("AVRCP_PDU_ID_GET_FOLDER_ITEMS response \n");
|
||||
|
||||
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, connection->avrcp_browsing_cid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
connection->state = AVCTP_CONNECTION_OPENED;
|
||||
}
|
||||
|
||||
static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
avrcp_browsing_connection_t * connection;
|
||||
avrcp_connection_t * avrcp_connection;
|
||||
printf("avrcp_browsing_controller_packet_handler, packet type 0%02x\n", packet_type);
|
||||
|
||||
avrcp_browsing_connection_t * browsing_connection;
|
||||
|
||||
switch (packet_type) {
|
||||
case L2CAP_DATA_PACKET:
|
||||
avrcp_connection = get_avrcp_connection_for_browsing_l2cap_cid(channel, &avrcp_controller_context);
|
||||
if (!avrcp_connection){
|
||||
printf("connection not found 0%2x\n", channel);
|
||||
break;
|
||||
}
|
||||
avrcp_browsing_controller_handle_l2cap_data_packet(avrcp_connection, packet, size);
|
||||
case L2CAP_DATA_PACKET:{
|
||||
browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_controller_context);
|
||||
if (!browsing_connection) break;
|
||||
browsing_connection->state = AVCTP_CONNECTION_OPENED;
|
||||
|
||||
int pos = 3;
|
||||
if (size < pos + 4){
|
||||
avrcp_browsing_controller_emit_done(avrcp_controller_context.avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS);
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
(*avrcp_controller_context.avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length);
|
||||
pos += 3 + browsable_item_length;
|
||||
}
|
||||
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)){
|
||||
case L2CAP_EVENT_CAN_SEND_NOW:
|
||||
connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_controller_context);
|
||||
if (!connection) break;
|
||||
avrcp_browsing_controller_handle_can_send_now(connection);
|
||||
browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_controller_context);
|
||||
if (!browsing_connection) break;
|
||||
avrcp_browsing_controller_handle_can_send_now(browsing_connection);
|
||||
break;
|
||||
default:
|
||||
avrcp_browser_packet_handler(packet_type, channel, packet, size, &avrcp_controller_context);
|
||||
@ -396,5 +411,5 @@ uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid,
|
||||
}
|
||||
|
||||
uint8_t avrcp_browsing_controller_get_player_list(uint16_t avrcp_browsing_cid){
|
||||
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 0, 0, 0xFFFFFFFF, 0, NULL);
|
||||
return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 0x01, 0, 0xFFFFFFFF, 0, NULL);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* avrcp_browsing.h
|
||||
* avrcp_browsing_controller.h
|
||||
*
|
||||
* Audio/Video Remote Control Profile Browsing
|
||||
*
|
||||
@ -55,6 +55,48 @@ extern "C" {
|
||||
|
||||
/* API_START */
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_ITEM = 0x01,
|
||||
AVRCP_BROWSING_FOLDER_ITEM,
|
||||
AVRCP_BROWSING_MEDIA_ELEMENT_ITEM
|
||||
} avrcp_browsing_item_type_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_MAJOR_TYPE_AUDIO = 1,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_MAJOR_TYPE_VIDEO = 2,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_MAJOR_TYPE_BROADCASTING_AUDIO = 4,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_MAJOR_TYPE_BROADCASTING_VIDEO = 8
|
||||
} avrcp_browsing_media_player_major_type_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_SUBTYPE_AUDIO_BOOK = 1,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_SUBTYPE_POADCAST = 2
|
||||
} avrcp_browsing_media_player_subtype_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_STOPPED = 0,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_PLAYING,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_PAUSED,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_FWD_SEEK,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_REV_SEEK,
|
||||
AVRCP_BROWSING_MEDIA_PLAYER_STATUS_ERROR = 0xFF
|
||||
} avrcp_browsing_media_player_status_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_FOLDER_TYPE_MIXED = 0x00,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_TITLES,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_ALBUMS,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_ARTISTS,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_GENRES,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_PLAYLISTS,
|
||||
AVRCP_BROWSING_FOLDER_TYPE_YEARS
|
||||
} avrcp_browsing_folder_type_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_BROWSING_MEDIA_TYPE_AUDIO = 0x00,
|
||||
AVRCP_BROWSING_MEDIA_TYPE_VIDEO
|
||||
} avrcp_browsing_media_type_t;
|
||||
|
||||
/**
|
||||
* @brief Set up AVRCP Browsing Controller device.
|
||||
*/
|
||||
|
90
src/classic/avrcp_media_item_iterator.c
Normal file
90
src/classic/avrcp_media_item_iterator.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2014 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define __BTSTACK_FILE__ "avrcp_media_item_iterator.c"
|
||||
|
||||
|
||||
// *****************************************************************************
|
||||
//
|
||||
// AVRCP Media Item Iterator
|
||||
//
|
||||
// *****************************************************************************
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "classic/avrcp_media_item_iterator.h"
|
||||
#include "btstack_util.h"
|
||||
|
||||
void avrcp_media_item_iterator_init(avrcp_media_item_context_t *context, uint16_t avrcp_media_item_len, const uint8_t * avrcp_media_item_data){
|
||||
context->data = avrcp_media_item_data;
|
||||
context->length = avrcp_media_item_len;
|
||||
context->offset = 0;
|
||||
}
|
||||
|
||||
int avrcp_media_item_iterator_has_more(const avrcp_media_item_context_t * context){
|
||||
return context->offset < context->length;
|
||||
}
|
||||
|
||||
void avrcp_media_item_iterator_next(avrcp_media_item_context_t * context){
|
||||
int chunk_len = big_endian_read_16(context->data, context->offset + 6);
|
||||
int new_offset = context->offset + 1 + chunk_len;
|
||||
// avoid uint8_t overrun
|
||||
if (new_offset > 0xffff){
|
||||
new_offset = 0xffff;
|
||||
}
|
||||
context->offset = new_offset;
|
||||
}
|
||||
|
||||
uint32_t avrcp_media_item_iterator_get_attr_id(const avrcp_media_item_context_t * context){
|
||||
return big_endian_read_32(context->data, context->offset);
|
||||
}
|
||||
|
||||
uint16_t avrcp_media_item_iterator_get_attr_charset(const avrcp_media_item_context_t * context){
|
||||
return big_endian_read_16(context->data, context->offset + 4);
|
||||
}
|
||||
|
||||
uint16_t avrcp_media_item_iterator_get_attr_value_len(const avrcp_media_item_context_t * context){
|
||||
return big_endian_read_16(context->data, context->offset + 6);
|
||||
}
|
||||
|
||||
const uint8_t * avrcp_media_item_iterator_get_attr_value(const avrcp_media_item_context_t * context){
|
||||
return &context->data[context->offset + 8];
|
||||
}
|
||||
|
78
src/classic/avrcp_media_item_iterator.h
Normal file
78
src/classic/avrcp_media_item_iterator.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2017 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
// *****************************************************************************
|
||||
//
|
||||
// AVRCP Media Item Iterator
|
||||
//
|
||||
// *****************************************************************************
|
||||
|
||||
#ifndef __AVRCP_MEDIA_ITEM_ITERATOR_H
|
||||
#define __AVRCP_MEDIA_ITEM_ITERATOR_H
|
||||
|
||||
#include "btstack_config.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* API_START */
|
||||
|
||||
typedef struct avrcp_media_item_context {
|
||||
const uint8_t * data;
|
||||
uint16_t offset;
|
||||
uint16_t length;
|
||||
} avrcp_media_item_context_t;
|
||||
|
||||
// Media item data iterator
|
||||
void avrcp_media_item_iterator_init(avrcp_media_item_context_t *context, uint16_t avrcp_media_item_len, const uint8_t * avrcp_media_item_data);
|
||||
int avrcp_media_item_iterator_has_more(const avrcp_media_item_context_t * context);
|
||||
void avrcp_media_item_iterator_next(avrcp_media_item_context_t * context);
|
||||
|
||||
// Access functions
|
||||
uint32_t avrcp_media_item_iterator_get_attr_id(const avrcp_media_item_context_t * context);
|
||||
uint16_t avrcp_media_item_iterator_get_attr_charset(const avrcp_media_item_context_t * context);
|
||||
uint16_t avrcp_media_item_iterator_get_attr_value_len(const avrcp_media_item_context_t * context);
|
||||
const uint8_t * avrcp_media_item_iterator_get_attr_value(const avrcp_media_item_context_t * context);
|
||||
|
||||
/* API_END */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __AVRCP_MEDIA_ITEM_ITERATOR_H
|
Loading…
x
Reference in New Issue
Block a user