avdtp sink: update API to retrun cmd status, fix reconfigure, remove printf from library

This commit is contained in:
Milanka Ringwald 2017-12-15 16:05:31 +01:00
parent 549011958f
commit 9974aee0d2
19 changed files with 541 additions and 274 deletions

View File

@ -129,7 +129,7 @@ static uint8_t sbc_frame_size;
static int sbc_samples_fix; static int sbc_samples_fix;
#endif #endif
// PortAdudio - live playback // PortAudio - live playback
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
#define PA_SAMPLE_TYPE paInt16 #define PA_SAMPLE_TYPE paInt16
#define SAMPLE_RATE 48000 #define SAMPLE_RATE 48000

View File

@ -25,7 +25,7 @@
#define ENABLE_SCO_OVER_HCI #define ENABLE_SCO_OVER_HCI
#define ENABLE_SDP_DES_DUMP #define ENABLE_SDP_DES_DUMP
// #define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE #define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
// BTstack configuration. buffers, sizes, ... // BTstack configuration. buffers, sizes, ...
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4) #define HCI_ACL_PAYLOAD_SIZE (1691 + 4)

View File

@ -188,7 +188,8 @@ typedef uint8_t sm_key_t[16];
#define AVDTP_CONNECTION_DOES_NOT_EXIST 0xC1 #define AVDTP_CONNECTION_DOES_NOT_EXIST 0xC1
#define AVDTP_CONNECTION_IN_WRONG_STATE 0xC2 #define AVDTP_CONNECTION_IN_WRONG_STATE 0xC2
#define AVDTP_STREAM_ENDPOINT_IN_WRONG_STATE 0xC3 #define AVDTP_STREAM_ENDPOINT_IN_WRONG_STATE 0xC3
#define AVDTP_MEDIA_CONNECTION_DOES_NOT_EXIST 0xC4 #define AVDTP_STREAM_ENDPOINT_DOES_NOT_EXIST 0xC4
#define AVDTP_MEDIA_CONNECTION_DOES_NOT_EXIST 0xC5
/* ENUM_END */ /* ENUM_END */
// DAEMON COMMANDS // DAEMON COMMANDS

View File

@ -854,7 +854,6 @@ uint8_t avdtp_suspend_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_conte
log_error("avdtp_suspend_stream: no connection for signaling cid 0x%02x found", avdtp_cid); log_error("avdtp_suspend_stream: no connection for signaling cid 0x%02x found", avdtp_cid);
return AVDTP_CONNECTION_DOES_NOT_EXIST; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(local_seid, context); avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(local_seid, context);
if (!stream_endpoint) { if (!stream_endpoint) {
log_error("avdtp_suspend_stream: no stream_endpoint with seid %d found", local_seid); log_error("avdtp_suspend_stream: no stream_endpoint with seid %d found", local_seid);
@ -873,92 +872,96 @@ uint8_t avdtp_suspend_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_conte
return ERROR_CODE_SUCCESS; return ERROR_CODE_SUCCESS;
} }
void avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context){ uint8_t avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context){
log_info("call avdtp_discover_stream_endpoints");
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_discover_stream_endpoints: no connection for signaling cid 0x%02x found", avdtp_cid); log_error("avdtp_discover_stream_endpoints: no connection for signaling cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
switch (connection->initiator_connection_state){
case AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE:
connection->initiator_transaction_label++;
connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS;
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
break;
default:
log_error("avdtp_discover_stream_endpoints: wrong state");
break;
} }
connection->initiator_transaction_label++;
connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS;
return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
void avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){ uint8_t avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_get_capabilities: no connection for AVDTP cid 0x%02x found", avdtp_cid); log_error("avdtp: no connection for AVDTP cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
}
connection->initiator_transaction_label++; connection->initiator_transaction_label++;
connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES; connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES;
connection->remote_seid = remote_seid; connection->remote_seid = remote_seid;
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
void avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){ uint8_t avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_get_all_capabilities: no connection for AVDTP cid 0x%02x found", avdtp_cid); log_error("avdtp: no connection for AVDTP cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
}
connection->initiator_transaction_label++; connection->initiator_transaction_label++;
connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES; connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES;
connection->remote_seid = remote_seid; connection->remote_seid = remote_seid;
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
void avdtp_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){ uint8_t avdtp_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context){
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_get_configuration: no connection for AVDTP cid 0x%02x found", avdtp_cid); log_error("avdtp: no connection for AVDTP cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
}
connection->initiator_transaction_label++; connection->initiator_transaction_label++;
connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION; connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION;
connection->remote_seid = remote_seid; connection->remote_seid = remote_seid;
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
void avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ uint8_t avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_set_configuration: no connection for AVDTP cid 0x%02x found", avdtp_cid); log_error("avdtp: no connection for AVDTP cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
}
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
} }
if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return;
if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return;
avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, context); avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, context);
if (!stream_endpoint) { if (!stream_endpoint) {
log_error("avdtp_set_configuration: no initiator stream endpoint for seid %d", local_seid); log_error("avdtp: no initiator stream endpoint for seid %d", local_seid);
return; return AVDTP_STREAM_ENDPOINT_DOES_NOT_EXIST;
} }
connection->active_stream_endpoint = (void*) stream_endpoint; connection->active_stream_endpoint = (void*) stream_endpoint;
connection->is_configuration_initiated_locally = 1; connection->is_configuration_initiated_locally = 1;
connection->is_initiator = 1; connection->is_initiator = 1;
log_info("avdtp_set_configuration locally: role is_initiator %d", connection->is_initiator);
connection->initiator_transaction_label++; connection->initiator_transaction_label++;
connection->remote_seid = remote_seid; connection->remote_seid = remote_seid;
connection->local_seid = local_seid; connection->local_seid = local_seid;
@ -972,37 +975,38 @@ void avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t rem
stream_endpoint->media_type = configuration.media_codec.media_type; stream_endpoint->media_type = configuration.media_codec.media_type;
memcpy(stream_endpoint->media_codec_sbc_info, configuration.media_codec.media_codec_information, 4); memcpy(stream_endpoint->media_codec_sbc_info, configuration.media_codec.media_codec_information, 4);
} }
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
void avdtp_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ uint8_t avdtp_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){
avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context); avdtp_connection_t * connection = avdtp_connection_for_avdtp_cid(avdtp_cid, context);
if (!connection){ if (!connection){
log_error("avdtp_reconfigure: no connection for AVDTP cid 0x%02x found", avdtp_cid); log_error("avdtp: no connection for AVDTP cid 0x%02x found", avdtp_cid);
return; return AVDTP_CONNECTION_DOES_NOT_EXIST;
} }
//TODO: if opened only app capabilities, enable reconfigure for not opened //TODO: if opened only app capabilities, enable reconfigure for not opened
if (connection->state < AVDTP_SIGNALING_CONNECTION_OPENED) return; if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED ||
if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) {
return AVDTP_CONNECTION_IN_WRONG_STATE;
}
avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, context); avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, context);
if (!stream_endpoint) { if (!stream_endpoint) {
log_error("avdtp_reconfigure: no initiator stream endpoint for seid %d", local_seid); log_error("avdtp_reconfigure: no initiator stream endpoint for seid %d", local_seid);
return; return AVDTP_STREAM_ENDPOINT_DOES_NOT_EXIST;
} }
if (stream_endpoint->remote_sep_index == 0xFF){ if (stream_endpoint->remote_sep_index == 0xFF){
log_error("avdtp_reconfigure: no associated remote sep"); log_error("avdtp_reconfigure: no associated remote sep");
return; return AVDTP_STREAM_ENDPOINT_IN_WRONG_STATE;
} }
connection->initiator_transaction_label++; connection->initiator_transaction_label++;
connection->remote_seid = remote_seid; connection->remote_seid = remote_seid;
connection->local_seid = local_seid; connection->local_seid = local_seid;
stream_endpoint->remote_configuration_bitmap = configured_services_bitmap; stream_endpoint->remote_configuration_bitmap = configured_services_bitmap;
stream_endpoint->remote_configuration = configuration; stream_endpoint->remote_configuration = configuration;
stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID;
avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); return avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid);
} }
uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context){ uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context){

View File

@ -545,12 +545,12 @@ uint8_t avdtp_stop_stream (uint16_t avdtp_cid, uint8_t local_seid, avdtp_context
uint8_t avdtp_abort_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_context_t * context); uint8_t avdtp_abort_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_context_t * context);
uint8_t avdtp_suspend_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_context_t * context); uint8_t avdtp_suspend_stream(uint16_t avdtp_cid, uint8_t local_seid, avdtp_context_t * context);
void avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context); uint8_t avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context);
void avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context); uint8_t avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context);
void avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context); uint8_t avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context);
void avdtp_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context); uint8_t avdtp_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid, avdtp_context_t * context);
void avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); uint8_t avdtp_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context);
void avdtp_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); uint8_t avdtp_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context);
uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context); uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context);
avdtp_sep_t * avdtp_remote_sep(uint16_t avdtp_cid, uint8_t index, avdtp_context_t * context); avdtp_sep_t * avdtp_remote_sep(uint16_t avdtp_cid, uint8_t index, avdtp_context_t * context);

View File

@ -83,7 +83,6 @@ static int avdtp_acceptor_validate_msg_length(avdtp_signal_identifier_t signal_i
void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t * packet, uint16_t size, int offset, avdtp_context_t * context){ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t * packet, uint16_t size, int offset, avdtp_context_t * context){
avdtp_stream_endpoint_t * stream_endpoint; avdtp_stream_endpoint_t * stream_endpoint;
connection->acceptor_transaction_label = connection->signaling_packet.transaction_label; connection->acceptor_transaction_label = connection->signaling_packet.transaction_label;
if (!avdtp_acceptor_validate_msg_length(connection->signaling_packet.signal_identifier, size)) { if (!avdtp_acceptor_validate_msg_length(connection->signaling_packet.signal_identifier, size)) {
connection->error_code = BAD_LENGTH; connection->error_code = BAD_LENGTH;
connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE;

View File

@ -284,7 +284,7 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_
} }
void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, avdtp_context_t * context){ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, avdtp_context_t * context){
int sent = 1; int sent = 1;
switch (connection->initiator_connection_state){ switch (connection->initiator_connection_state){
case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS: case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS:
log_info("INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS"); log_info("INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS");
@ -335,7 +335,7 @@ int sent = 1;
} }
return; return;
} }
if (stream_endpoint->stop_stream){ if (stream_endpoint->stop_stream){
stream_endpoint->stop_stream = 0; stream_endpoint->stop_stream = 0;
if (stream_endpoint->state >= AVDTP_STREAM_ENDPOINT_OPENED){ if (stream_endpoint->state >= AVDTP_STREAM_ENDPOINT_OPENED){
@ -345,7 +345,7 @@ int sent = 1;
return; return;
} }
} }
if (stream_endpoint->abort_stream){ if (stream_endpoint->abort_stream){
stream_endpoint->abort_stream = 0; stream_endpoint->abort_stream = 0;
switch (stream_endpoint->state){ switch (stream_endpoint->state){
@ -362,7 +362,7 @@ int sent = 1;
break; break;
} }
} }
if (stream_endpoint->suspend_stream){ if (stream_endpoint->suspend_stream){
stream_endpoint->suspend_stream = 0; stream_endpoint->suspend_stream = 0;
if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){ if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){
@ -371,7 +371,7 @@ int sent = 1;
return; return;
} }
} }
if (stream_endpoint->send_stream){ if (stream_endpoint->send_stream){
stream_endpoint->send_stream = 0; stream_endpoint->send_stream = 0;
if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){ if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){
@ -381,17 +381,15 @@ int sent = 1;
} }
} }
switch (stream_endpoint_state){ switch (stream_endpoint_state){
case AVDTP_INITIATOR_W2_SET_CONFIGURATION: case AVDTP_INITIATOR_W2_SET_CONFIGURATION:
case AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID:{ case AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID:{
log_info("initiator SM prepare SET_CONFIGURATION cmd: role is_initiator %d", connection->is_initiator); if (stream_endpoint_state == AVDTP_INITIATOR_W2_SET_CONFIGURATION && !connection->is_initiator){
if (!connection->is_initiator){
log_info("initiator SM stop sending SET_CONFIGURATION cmd: current role is acceptor"); log_info("initiator SM stop sending SET_CONFIGURATION cmd: current role is acceptor");
connection->is_configuration_initiated_locally = 0; connection->is_configuration_initiated_locally = 0;
break; break;
} }
log_info("initiator SM prepare SET_CONFIGURATION cmd");
connection->is_configuration_initiated_locally = 1; connection->is_configuration_initiated_locally = 1;
log_info("INT: AVDTP_INITIATOR_W2_(RE)CONFIGURATION bitmap, int seid %d, acp seid %d", connection->local_seid, connection->remote_seid); log_info("INT: AVDTP_INITIATOR_W2_(RE)CONFIGURATION bitmap, int seid %d, acp seid %d", connection->local_seid, connection->remote_seid);
// log_info_hexdump( connection->remote_capabilities.media_codec.media_codec_information, connection->remote_capabilities.media_codec.media_codec_information_len); // log_info_hexdump( connection->remote_capabilities.media_codec.media_codec_information, connection->remote_capabilities.media_codec.media_codec_information_len);

View File

@ -169,27 +169,27 @@ uint8_t avdtp_sink_suspend(uint16_t avdtp_cid, uint8_t local_seid){
return avdtp_suspend_stream(avdtp_cid, local_seid, avdtp_sink_context); return avdtp_suspend_stream(avdtp_cid, local_seid, avdtp_sink_context);
} }
void avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid){ uint8_t avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid){
avdtp_discover_stream_endpoints(avdtp_cid, avdtp_sink_context); return avdtp_discover_stream_endpoints(avdtp_cid, avdtp_sink_context);
} }
void avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_capabilities(avdtp_cid, remote_seid, avdtp_sink_context); return avdtp_get_capabilities(avdtp_cid, remote_seid, avdtp_sink_context);
} }
void avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_all_capabilities(avdtp_cid, remote_seid, avdtp_sink_context); return avdtp_get_all_capabilities(avdtp_cid, remote_seid, avdtp_sink_context);
} }
void avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_configuration(avdtp_cid, remote_seid, avdtp_sink_context); return avdtp_get_configuration(avdtp_cid, remote_seid, avdtp_sink_context);
} }
void avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ uint8_t avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){
avdtp_set_configuration(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_sink_context); return avdtp_set_configuration(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_sink_context);
} }
void avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ uint8_t avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){
avdtp_reconfigure(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_sink_context); return avdtp_reconfigure(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_sink_context);
} }

View File

@ -98,38 +98,38 @@ uint8_t avdtp_sink_disconnect(uint16_t avdtp_cid);
* @brief Discover stream endpoints * @brief Discover stream endpoints
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid); uint8_t avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid);
/** /**
* @brief Get capabilities * @brief Get capabilities
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**
* @brief Get all capabilities * @brief Get all capabilities
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**
* @brief Set configuration * @brief Set configuration
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); uint8_t avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration);
/** /**
* @brief Reconfigure stream * @brief Reconfigure stream
* @param avdtp_cid * @param avdtp_cid
* @param seid * @param seid
*/ */
void avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); uint8_t avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration);
/** /**
* @brief Get configuration * @brief Get configuration
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**

View File

@ -133,28 +133,28 @@ uint8_t avdtp_source_suspend(uint16_t avdtp_cid, uint8_t local_seid){
return avdtp_suspend_stream(avdtp_cid, local_seid, avdtp_source_context); return avdtp_suspend_stream(avdtp_cid, local_seid, avdtp_source_context);
} }
void avdtp_source_discover_stream_endpoints(uint16_t avdtp_cid){ uint8_t avdtp_source_discover_stream_endpoints(uint16_t avdtp_cid){
avdtp_discover_stream_endpoints(avdtp_cid, avdtp_source_context); return avdtp_discover_stream_endpoints(avdtp_cid, avdtp_source_context);
} }
void avdtp_source_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_source_get_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_capabilities(avdtp_cid, remote_seid, avdtp_source_context); return avdtp_get_capabilities(avdtp_cid, remote_seid, avdtp_source_context);
} }
void avdtp_source_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_source_get_all_capabilities(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_all_capabilities(avdtp_cid, remote_seid, avdtp_source_context); return avdtp_get_all_capabilities(avdtp_cid, remote_seid, avdtp_source_context);
} }
void avdtp_source_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid){ uint8_t avdtp_source_get_configuration(uint16_t avdtp_cid, uint8_t remote_seid){
avdtp_get_configuration(avdtp_cid, remote_seid, avdtp_source_context); return avdtp_get_configuration(avdtp_cid, remote_seid, avdtp_source_context);
} }
void avdtp_source_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ uint8_t avdtp_source_set_configuration(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){
avdtp_set_configuration(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_source_context); return avdtp_set_configuration(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_source_context);
} }
void avdtp_source_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ uint8_t avdtp_source_reconfigure(uint16_t avdtp_cid, uint8_t local_seid, uint8_t remote_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){
avdtp_reconfigure(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_source_context); return avdtp_reconfigure(avdtp_cid, local_seid, remote_seid, configured_services_bitmap, configuration, avdtp_source_context);
} }
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){

View File

@ -84,38 +84,38 @@ uint8_t avdtp_source_disconnect(uint16_t avdtp_cid);
* @brief Discover stream endpoints * @brief Discover stream endpoints
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_source_discover_stream_endpoints(uint16_t avdtp_cid); uint8_t avdtp_source_discover_stream_endpoints(uint16_t avdtp_cid);
/** /**
* @brief Get capabilities * @brief Get capabilities
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_source_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_source_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**
* @brief Get all capabilities * @brief Get all capabilities
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_source_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_source_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**
* @brief Set configuration * @brief Set configuration
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_source_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); uint8_t avdtp_source_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration);
/** /**
* @brief Reconfigure stream * @brief Reconfigure stream
* @param avdtp_cid * @param avdtp_cid
* @param seid * @param seid
*/ */
void avdtp_source_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); uint8_t avdtp_source_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration);
/** /**
* @brief Get configuration * @brief Get configuration
* @param avdtp_cid * @param avdtp_cid
*/ */
void avdtp_source_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid); uint8_t avdtp_source_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid);
/** /**

View File

@ -825,20 +825,25 @@ void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handl
} }
void avdtp_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid){ uint8_t avdtp_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid){
if (!connection) return; if (!connection) return AVDTP_CONNECTION_DOES_NOT_EXIST;
connection->wait_to_send_acceptor = 1; connection->wait_to_send_acceptor = 1;
l2cap_request_can_send_now_event(l2cap_cid); l2cap_request_can_send_now_event(l2cap_cid);
return ERROR_CODE_SUCCESS;
} }
void avdtp_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid){
if (!connection) return; uint8_t avdtp_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid){
if (!connection) return AVDTP_CONNECTION_DOES_NOT_EXIST;
connection->wait_to_send_initiator = 1; connection->wait_to_send_initiator = 1;
l2cap_request_can_send_now_event(l2cap_cid); l2cap_request_can_send_now_event(l2cap_cid);
return ERROR_CODE_SUCCESS;
} }
void avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid){
if (!connection) return; uint8_t avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid){
if (!connection) return AVDTP_CONNECTION_DOES_NOT_EXIST;
connection->wait_to_send_self = 1; connection->wait_to_send_self = 1;
l2cap_request_can_send_now_event(l2cap_cid); l2cap_request_can_send_now_event(l2cap_cid);
return ERROR_CODE_SUCCESS;
} }
uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t seid){ uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t seid){

View File

@ -97,9 +97,9 @@ void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler
void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, avdtp_media_type_t media_type, const uint8_t * media_codec_information); void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, avdtp_media_type_t media_type, const uint8_t * media_codec_information);
void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec);
void avdtp_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid); uint8_t avdtp_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid);
void avdtp_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid); uint8_t avdtp_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid);
void avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid); uint8_t avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l2cap_cid);
uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t acp_seid); uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t acp_seid);

View File

@ -100,7 +100,6 @@ static void avrcp_emit_browsing_connection_established(btstack_packet_handler_t
} }
static void avrcp_emit_incoming_browsing_connection(btstack_packet_handler_t callback, uint16_t browsing_cid, bd_addr_t addr){ static void avrcp_emit_incoming_browsing_connection(btstack_packet_handler_t callback, uint16_t browsing_cid, bd_addr_t addr){
printf("avrcp_emit_incoming_browsing_connection browsing_cid 0x%02x \n", browsing_cid);
if (!callback) return; if (!callback) return;
uint8_t event[11]; uint8_t event[11];
int pos = 0; int pos = 0;
@ -190,14 +189,14 @@ void avrcp_browser_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
local_cid = l2cap_event_incoming_connection_get_local_cid(packet); local_cid = l2cap_event_incoming_connection_get_local_cid(packet);
avrcp_connection = get_avrcp_connection_for_bd_addr(event_addr, context); avrcp_connection = get_avrcp_connection_for_bd_addr(event_addr, context);
if (!avrcp_connection) { if (!avrcp_connection) {
printf("No previously created AVRCP controller connections"); log_error("No previously created AVRCP controller connections");
l2cap_decline_connection(local_cid); l2cap_decline_connection(local_cid);
break; break;
} }
browsing_connection = avrcp_browsing_create_connection(avrcp_connection); browsing_connection = avrcp_browsing_create_connection(avrcp_connection);
browsing_connection->l2cap_browsing_cid = local_cid; browsing_connection->l2cap_browsing_cid = local_cid;
browsing_connection->state = AVCTP_CONNECTION_W4_ERTM_CONFIGURATION; browsing_connection->state = AVCTP_CONNECTION_W4_ERTM_CONFIGURATION;
printf("Emit AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION browsing_cid 0x%02x, l2cap_signaling_cid 0x%02x\n", avrcp_connection->avrcp_browsing_cid, browsing_connection->l2cap_browsing_cid); log_info("Emit AVRCP_SUBEVENT_INCOMING_BROWSING_CONNECTION browsing_cid 0x%02x, l2cap_signaling_cid 0x%02x\n", avrcp_connection->avrcp_browsing_cid, browsing_connection->l2cap_browsing_cid);
avrcp_emit_incoming_browsing_connection(context->browsing_avrcp_callback, avrcp_connection->avrcp_browsing_cid, event_addr); avrcp_emit_incoming_browsing_connection(context->browsing_avrcp_callback, avrcp_connection->avrcp_browsing_cid, event_addr);
break; break;
@ -517,19 +516,18 @@ uint8_t avrcp_browsing_controller_disconnect(uint16_t avrcp_browsing_cid){
} }
uint8_t avrcp_avrcp_browsing_configure_incoming_connection(uint16_t avrcp_browsing_cid, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config){ uint8_t avrcp_avrcp_browsing_configure_incoming_connection(uint16_t avrcp_browsing_cid, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config){
printf("avrcp_avrcp_browsing_configure_incoming_connection browsing cid 0x%02X\n", avrcp_browsing_cid);
avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context); avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context);
if (!avrcp_connection){ if (!avrcp_connection){
printf("avrcp_avrcp_browsing_decline_incoming_connection: could not find a connection.\n"); log_error("avrcp_avrcp_browsing_decline_incoming_connection: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
} }
if (!avrcp_connection->browsing_connection){ if (!avrcp_connection->browsing_connection){
printf("avrcp_avrcp_browsing_decline_incoming_connection: no browsing connection.\n"); log_error("avrcp_avrcp_browsing_decline_incoming_connection: no browsing connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
} }
if (avrcp_connection->browsing_connection->state != AVCTP_CONNECTION_W4_ERTM_CONFIGURATION){ if (avrcp_connection->browsing_connection->state != AVCTP_CONNECTION_W4_ERTM_CONFIGURATION){
printf("avrcp_avrcp_browsing_decline_incoming_connection: browsing connection in a wrong state.\n"); log_error("avrcp_avrcp_browsing_decline_incoming_connection: browsing connection in a wrong state.");
return ERROR_CODE_COMMAND_DISALLOWED; return ERROR_CODE_COMMAND_DISALLOWED;
} }
@ -537,7 +535,6 @@ uint8_t avrcp_avrcp_browsing_configure_incoming_connection(uint16_t avrcp_browsi
avrcp_connection->browsing_connection->ertm_buffer = ertm_buffer; avrcp_connection->browsing_connection->ertm_buffer = ertm_buffer;
avrcp_connection->browsing_connection->ertm_buffer_size = size; avrcp_connection->browsing_connection->ertm_buffer_size = size;
memcpy(&avrcp_connection->browsing_connection->ertm_config, ertm_config, sizeof(l2cap_ertm_config_t)); memcpy(&avrcp_connection->browsing_connection->ertm_config, ertm_config, sizeof(l2cap_ertm_config_t));
printf("accept ertm connection\n");
l2cap_accept_ertm_connection(avrcp_connection->browsing_connection->l2cap_browsing_cid, &avrcp_connection->browsing_connection->ertm_config, avrcp_connection->browsing_connection->ertm_buffer, avrcp_connection->browsing_connection->ertm_buffer_size); l2cap_accept_ertm_connection(avrcp_connection->browsing_connection->l2cap_browsing_cid, &avrcp_connection->browsing_connection->ertm_config, avrcp_connection->browsing_connection->ertm_buffer, avrcp_connection->browsing_connection->ertm_buffer_size);
return ERROR_CODE_SUCCESS; return ERROR_CODE_SUCCESS;
} }

View File

@ -29,7 +29,6 @@
// BTstack configuration. buffers, sizes, ... // BTstack configuration. buffers, sizes, ...
#define HCI_ACL_PAYLOAD_SIZE 1024 #define HCI_ACL_PAYLOAD_SIZE 1024
#define HCI_INCOMING_PRE_BUFFER_SIZE 6 #define HCI_INCOMING_PRE_BUFFER_SIZE 6
#define NVM_NUM_LINK_KEYS 2 #define NVM_NUM_LINK_KEYS 2
#define NVM_NUM_DEVICE_DB_ENTRIES 4 #define NVM_NUM_DEVICE_DB_ENTRIES 4

View File

@ -2,6 +2,16 @@
BTSTACK_ROOT = ../.. BTSTACK_ROOT = ../..
CLASSIC += \
btstack_link_key_db_memory.c \
sdp_util.c \
spp_server.c \
rfcomm.c \
bnep.c \
sdp_server.c \
device_id_server.c \
CORE += \ CORE += \
btstack_memory.c \ btstack_memory.c \
btstack_linked_list.c \ btstack_linked_list.c \
@ -67,6 +77,12 @@ AVDTP += \
btstack_ring_buffer.c \ btstack_ring_buffer.c \
# include ${BTSTACK_ROOT}/example/Makefile.inc # include ${BTSTACK_ROOT}/example/Makefile.inc
HXCMOD_PLAYER = \
${BTSTACK_ROOT}/3rd-party/hxcmod-player/hxcmod.c \
${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c \
include ${BTSTACK_ROOT}/example/Makefile.inc
include ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/Makefile.inc include ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/Makefile.inc
include ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/Makefile.inc include ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/Makefile.inc
@ -107,16 +123,22 @@ ATT_OBJ = $(ATT:.c=.o)
SM_OBJ = $(SM:.c=.o) $(MICROECC:.c=.o) SM_OBJ = $(SM:.c=.o) $(MICROECC:.c=.o)
GATT_CLIENT_OBJ = $(GATT_CLIENT:.c=.o) GATT_CLIENT_OBJ = $(GATT_CLIENT:.c=.o)
GATT_SERVER_OBJ = $(GATT_SERVER:.c=.o) GATT_SERVER_OBJ = $(GATT_SERVER:.c=.o)
CLASSIC_OBJ = $(CLASSIC:.c=.o)
SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o) SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o)
SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o) SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o)
AVDTP_OBJ = $(AVDTP:.c=.o) AVDTP_OBJ = $(AVDTP:.c=.o)
HXCMOD_PLAYER_OBJ = ${HXCMOD_PLAYER:.c=.o}
EXAMPLES = iopt ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback le_data_channel EXAMPLES = iopt ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback le_data_channel
EXAMPLES += avdtp_source_test avdtp_sink_test le_data_channel avrcp_controller_test EXAMPLES += avdtp_source_test avdtp_sink_test le_data_channel avrcp_controller_test
# avrcp_target_test
all: ${EXAMPLES} all: ${EXAMPLES}
# compile .gatt descriptions # compile .gatt descriptions
# avrcp_target_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_ENCODER_OBJ} ${SM_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avrcp.o avrcp_target.o avrcp_target_test.c
# ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
avrcp_controller_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SM_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sdp_client.o avrcp.o avrcp_media_item_iterator.o avrcp_controller.o avrcp_browsing_controller.o avrcp_controller_test.o avrcp_controller_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SM_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sdp_client.o avrcp.o avrcp_media_item_iterator.o avrcp_controller.o avrcp_browsing_controller.o avrcp_controller_test.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@

View File

@ -46,11 +46,12 @@
#include "wav_util.h" #include "wav_util.h"
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
#include <portaudio.h>
#include "btstack_ring_buffer.h" #include "btstack_ring_buffer.h"
#include <portaudio.h>
#endif #endif
#ifdef HAVE_POSIX_FILE_IO #ifdef HAVE_POSIX_FILE_IO
#include "wav_util.h"
#define STORE_SBC_TO_SBC_FILE #define STORE_SBC_TO_SBC_FILE
#define STORE_SBC_TO_WAV_FILE #define STORE_SBC_TO_WAV_FILE
#endif #endif
@ -60,27 +61,30 @@
#endif #endif
#define NUM_CHANNELS 2 #define NUM_CHANNELS 2
#define SAMPLE_RATE 44100 #define BYTES_PER_FRAME (2*NUM_CHANNELS)
#define MAX_SBC_FRAME_SIZE 120
// SBC Decoder for WAV file or PortAudio // SBC Decoder for WAV file or PortAudio
#ifdef DECODE_SBC #ifdef DECODE_SBC
static btstack_sbc_decoder_state_t state; static btstack_sbc_decoder_state_t state;
static btstack_sbc_mode_t mode = SBC_MODE_STANDARD; static btstack_sbc_mode_t mode = SBC_MODE_STANDARD;
static int total_num_samples = 0;
#endif #endif
// PortAdudio - live playback #if defined(HAVE_PORTAUDIO)
#define PREBUFFER_MS 200
static int audio_stream_started = 0;
static int audio_stream_paused = 0;
static btstack_ring_buffer_t ring_buffer;
#endif
// PortAudio - live playback
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
#define PA_SAMPLE_TYPE paInt16 #define PA_SAMPLE_TYPE paInt16
#define SAMPLE_RATE 48000
#define FRAMES_PER_BUFFER 128 #define FRAMES_PER_BUFFER 128
#define BYTES_PER_FRAME (2*NUM_CHANNELS)
#define PREBUFFER_MS 300
#define PREBUFFER_BYTES (PREBUFFER_MS*SAMPLE_RATE/1000*BYTES_PER_FRAME) #define PREBUFFER_BYTES (PREBUFFER_MS*SAMPLE_RATE/1000*BYTES_PER_FRAME)
static uint8_t ring_buffer_storage[2*PREBUFFER_BYTES];
static btstack_ring_buffer_t ring_buffer;
static PaStream * stream; static PaStream * stream;
static int pa_stream_started = 0; static uint8_t ring_buffer_storage[2*PREBUFFER_BYTES];
static int pa_stream_paused = 0;
#endif #endif
// WAV File // WAV File
@ -118,7 +122,6 @@ typedef struct {
int frames_per_buffer; int frames_per_buffer;
} avdtp_media_codec_configuration_sbc_t; } avdtp_media_codec_configuration_sbc_t;
#ifdef HAVE_BTSTACK_STDIN
// mac 2011: static const char * device_addr_string = "04:0C:CE:E4:85:D3"; // mac 2011: static const char * device_addr_string = "04:0C:CE:E4:85:D3";
// pts: static const char * device_addr_string = "00:1B:DC:08:0A:A5"; // pts: static const char * device_addr_string = "00:1B:DC:08:0A:A5";
// mac 2013: // mac 2013:
@ -127,13 +130,17 @@ static const char * device_addr_string = "84:38:35:65:d1:15";
// minijambox: static const char * device_addr_string = "00:21:3C:AC:F7:38"; // minijambox: static const char * device_addr_string = "00:21:3C:AC:F7:38";
// head phones: static const char * device_addr_string = "00:18:09:28:50:18"; // head phones: static const char * device_addr_string = "00:18:09:28:50:18";
// bt dongle: static const char * device_addr_string = "00:15:83:5F:9D:46"; // bt dongle: static const char * device_addr_string = "00:15:83:5F:9D:46";
#endif
static bd_addr_t device_addr; static bd_addr_t device_addr;
static uint16_t avdtp_cid = 0; static uint8_t is_cmd_triggered_localy = 0;
static uint8_t sdp_avdtp_sink_service_buffer[150]; static uint8_t is_media_header_reported_once = 0;
static uint8_t is_media_initialized = 0;
static adtvp_media_codec_information_sbc_t sbc_capability; static uint16_t avdtp_cid = 0;
static uint8_t local_seid = 0;
static uint8_t sdp_avdtp_sink_service_buffer[150];
static adtvp_media_codec_information_sbc_t sbc_capability;
static avdtp_media_codec_configuration_sbc_t sbc_configuration; static avdtp_media_codec_configuration_sbc_t sbc_configuration;
static uint8_t local_seid; static uint8_t local_seid;
static uint8_t remote_seid; static uint8_t remote_seid;
@ -144,100 +151,138 @@ static avdtp_capabilities_t remote_configuration;
static avdtp_context_t a2dp_sink_context; static avdtp_context_t a2dp_sink_context;
static btstack_packet_callback_registration_t hci_event_callback_registration; static btstack_packet_callback_registration_t hci_event_callback_registration;
#if defined(HAVE_PORTAUDIO) || defined(STORE_SBC_TO_WAV_FILE) || defined(HAVE_AUDIO_DMA)
static int media_initialized = 0; static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context);
#endif
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
static int patestCallback( const void *inputBuffer, void *outputBuffer, static int portaudio_callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void *userData ) { void *userData ) {
/** patestCallback is called from different thread, don't use hci_dump / log_info here without additional checks */ /** portaudio_callback is called from different thread, don't use hci_dump / log_info here without additional checks */
(void) timeInfo; /* Prevent unused variable warnings. */ // Prevent unused variable warnings.
(void) timeInfo;
(void) statusFlags; (void) statusFlags;
(void) inputBuffer; (void) inputBuffer;
(void) userData; (void) userData;
int bytes_to_copy = framesPerBuffer * BYTES_PER_FRAME; int bytes_to_copy = framesPerBuffer * BYTES_PER_FRAME;
// fill with silence while paused // fill ring buffer with silence while stream is paused
if (pa_stream_paused){ if (audio_stream_paused){
if (btstack_ring_buffer_bytes_available(&ring_buffer) < PREBUFFER_BYTES){ if (btstack_ring_buffer_bytes_available(&ring_buffer) < PREBUFFER_BYTES){
// printf("PA: silence\n");
memset(outputBuffer, 0, bytes_to_copy); memset(outputBuffer, 0, bytes_to_copy);
return 0; return 0;
} else { } else {
// resume playback // resume playback
pa_stream_paused = 0; audio_stream_paused = 0;
} }
} }
// get data from ringbuffer // get data from ring buffer
uint32_t bytes_read = 0; uint32_t bytes_read = 0;
btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_to_copy, &bytes_read);
bytes_to_copy -= bytes_read; bytes_to_copy -= bytes_read;
// fill with 0 if not enough // fill ring buffer with silence if there are not enough bytes to copy
if (bytes_to_copy){ if (bytes_to_copy){
memset(outputBuffer + bytes_read, 0, bytes_to_copy); memset(outputBuffer + bytes_read, 0, bytes_to_copy);
pa_stream_paused = 1; audio_stream_paused = 1;
} }
return 0; return 0;
} }
#endif #endif
#ifdef DECODE_SBC #ifdef HAVE_AUDIO_DMA
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ static int next_buffer(int current){
UNUSED(sample_rate); if (current == NUM_AUDIO_BUFFERS-1) return 0;
UNUSED(context); return current + 1;
}
#ifdef STORE_SBC_TO_WAV_FILE static uint8_t * start_of_buffer(int num){
wav_writer_write_int16(num_samples*num_channels, data); return (uint8_t *) &audio_samples[num * DMA_AUDIO_FRAMES * 2];
frame_count++; }
#endif void hal_audio_dma_done(void){
if (audio_stream_paused){
total_num_samples+=num_samples*num_channels; hal_audio_dma_play((const uint8_t *) silent_buffer, DMA_AUDIO_FRAMES*4);
return;
#ifdef HAVE_PORTAUDIO
if (!pa_stream_started){
/* -- start stream -- */
PaError err = Pa_StartStream(stream);
if (err != paNoError){
printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
pa_stream_started = 1;
pa_stream_paused = 1;
} }
btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); // next buffer
#endif int next_playback_buffer = next_buffer(playback_buffer);
uint8_t * playback_data;
if (next_playback_buffer == write_buffer){
// TODO: stop codec while playing silence when getting 'stream paused'
// start playing silence
audio_stream_paused = 1;
hal_audio_dma_play((const uint8_t *) silent_buffer, DMA_AUDIO_FRAMES*4);
printf("%6u - paused - bytes in buffer %u\n", (int) btstack_run_loop_get_time_ms(), btstack_ring_buffer_bytes_available(&ring_buffer));
return;
}
playback_buffer = next_playback_buffer;
playback_data = start_of_buffer(playback_buffer);
hal_audio_dma_play(playback_data, audio_samples_len[playback_buffer]);
// btstack_run_loop_embedded_trigger();
} }
#endif #endif
static int init_media_processing(avdtp_media_codec_configuration_sbc_t configuration){
int num_channels = configuration.num_channels; #ifdef HAVE_AUDIO_DMA
int sample_rate = configuration.sampling_frequency; static void hal_audio_dma_process(btstack_data_source_t * ds, btstack_data_source_callback_type_t callback_type){
UNUSED(ds);
UNUSED(callback_type);
if (!media_initialized) return;
int trigger_resume = 0;
if (audio_stream_paused) {
if (sbc_frame_size && btstack_ring_buffer_bytes_available(&ring_buffer) >= OPTIMAL_FRAMES_MIN * sbc_frame_size){
trigger_resume = 1;
// reset buffers
playback_buffer = NUM_AUDIO_BUFFERS - 1;
write_buffer = 0;
} else {
return;
}
}
while (playback_buffer != write_buffer && btstack_ring_buffer_bytes_available(&ring_buffer) >= sbc_frame_size ){
uint8_t frame[MAX_SBC_FRAME_SIZE];
uint32_t bytes_read = 0;
btstack_ring_buffer_read(&ring_buffer, frame, sbc_frame_size, &bytes_read);
btstack_sbc_decoder_process_data(&state, 0, frame, sbc_frame_size);
}
if (trigger_resume){
printf("%6u - resume\n", (int) btstack_run_loop_get_time_ms());
audio_stream_paused = 0;
}
}
#endif
static int media_processing_init(avdtp_media_codec_configuration_sbc_t configuration){
if (is_media_initialized) return 0;
#ifdef DECODE_SBC #ifdef DECODE_SBC
btstack_sbc_decoder_init(&state, mode, handle_pcm_data, NULL); btstack_sbc_decoder_init(&state, mode, handle_pcm_data, NULL);
#endif #endif
#ifdef STORE_SBC_TO_WAV_FILE #ifdef STORE_SBC_TO_WAV_FILE
wav_writer_open(wav_filename, num_channels, sample_rate); wav_writer_open(wav_filename, configuration.num_channels, configuration.sampling_frequency);
#endif #endif
#ifdef STORE_SBC_TO_SBC_FILE #ifdef STORE_SBC_TO_SBC_FILE
sbc_file = fopen(sbc_filename, "wb"); sbc_file = fopen(sbc_filename, "wb");
#endif #endif
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
// int frames_per_buffer = configuration.frames_per_buffer; // int frames_per_buffer = configuration.frames_per_buffer;
PaError err; PaError err;
PaStreamParameters outputParameters; PaStreamParameters outputParameters;
const PaDeviceInfo *deviceInfo;
/* -- initialize PortAudio -- */ /* -- initialize PortAudio -- */
err = Pa_Initialize(); err = Pa_Initialize();
@ -247,126 +292,263 @@ static int init_media_processing(avdtp_media_codec_configuration_sbc_t configura
} }
/* -- setup input and output -- */ /* -- setup input and output -- */
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = num_channels; outputParameters.channelCount = configuration.num_channels;
outputParameters.sampleFormat = PA_SAMPLE_TYPE; outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL; outputParameters.hostApiSpecificStreamInfo = NULL;
deviceInfo = Pa_GetDeviceInfo( outputParameters.device );
printf("PortAudio: Output device: %s\n", deviceInfo->name);
log_info("PortAudio: Output device: %s", deviceInfo->name);
/* -- setup stream -- */ /* -- setup stream -- */
err = Pa_OpenStream( err = Pa_OpenStream(
&stream, &stream,
NULL, /* &inputParameters */ NULL, /* &inputParameters */
&outputParameters, &outputParameters,
sample_rate, configuration.sampling_frequency,
0, 0,
paClipOff, /* we won't output out of range samples so don't bother clipping them */ paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback, /* use callback */ portaudio_callback, /* use callback */
NULL ); NULL );
if (err != paNoError){ if (err != paNoError){
printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err));
return err; return err;
} }
log_info("PortAudio: stream opened");
printf("PortAudio: stream opened\n");
#endif
#ifdef HAVE_AUDIO_DMA
audio_stream_paused = 1;
hal_audio_dma_init(configuration.sampling_frequency);
hal_audio_dma_set_audio_played(&hal_audio_dma_done);
// start playing silence
hal_audio_dma_done();
#endif
#if defined(HAVE_PORTAUDIO) || defined (HAVE_AUDIO_DMA)
memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
pa_stream_started = 0; audio_stream_started = 0;
audio_stream_paused = 0;
#endif #endif
media_initialized = 1; is_media_initialized = 1;
return 0; return 0;
} }
static void close_media_processing(void){ static void media_processing_close(void){
if (!media_initialized) return; if (is_media_initialized) return;
media_initialized = 0; is_media_initialized = 0;
#ifdef STORE_SBC_TO_WAV_FILE
printf(" Close wav writer.\n"); #ifdef STORE_SBC_TO_WAV_FILE
wav_writer_close(); wav_writer_close();
int total_frames_nr = state.good_frames_nr + state.bad_frames_nr + state.zero_frames_nr; int total_frames_nr = state.good_frames_nr + state.bad_frames_nr + state.zero_frames_nr;
printf(" Decoding done. Processed totaly %d frames:\n - %d good\n - %d bad\n - %d zero frames\n", total_frames_nr, state.good_frames_nr, state.bad_frames_nr, state.zero_frames_nr); printf("WAV Writer: Decoding done. Processed totaly %d frames:\n - %d good\n - %d bad\n - %d zero frames\n", total_frames_nr, state.good_frames_nr, state.bad_frames_nr, state.zero_frames_nr);
printf(" Written %d frames to wav file: %s\n\n", frame_count, wav_filename); printf("WAV Writer: Written %d frames to wav file: %s\n", frame_count, wav_filename);
#endif #endif
#ifdef STORE_SBC_TO_SBC_FILE #ifdef STORE_SBC_TO_SBC_FILE
fclose(sbc_file); fclose(sbc_file);
#endif #endif
#if defined(HAVE_PORTAUDIO) || defined (HAVE_AUDIO_DMA)
audio_stream_started = 0;
#endif
#ifdef HAVE_PORTAUDIO #ifdef HAVE_PORTAUDIO
printf("PortAudio: Stream closed\n");
log_info("PortAudio: Stream closed");
PaError err = Pa_StopStream(stream); PaError err = Pa_StopStream(stream);
if (err != paNoError){ if (err != paNoError){
printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err));
log_error("Error stopping the stream: \"%s\"", Pa_GetErrorText(err));
return; return;
} }
pa_stream_started = 0;
err = Pa_CloseStream(stream); err = Pa_CloseStream(stream);
if (err != paNoError){ if (err != paNoError){
printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err));
log_error("Error closing the stream: \"%s\"", Pa_GetErrorText(err));
return; return;
} }
err = Pa_Terminate(); err = Pa_Terminate();
if (err != paNoError){ if (err != paNoError){
printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err));
log_error("Error terminating portaudio: \"%s\"", Pa_GetErrorText(err));
return; return;
} }
#endif #endif
#ifdef HAVE_AUDIO_DMA
hal_audio_dma_close();
#endif
} }
/* @section Handle Media Data Packet
*
* @text Media data packets, in this case the audio data, are received through the handle_l2cap_media_data_packet callback.
* Currently, only the SBC media codec is supported. Hence, the media data consists of the media packet header and the SBC packet.
* The SBC data will be decoded using an SBC decoder if either HAVE_PORTAUDIO or STORE_SBC_TO_WAV_FILE directive is defined.
* The resulting PCM frames can be then captured through a PCM data callback registered during SBC decoder setup, i.e. the
* handle_pcm_data callback.
*/
static int read_media_data_header(uint8_t * packet, int size, int * offset, avdtp_media_packet_header_t * media_header);
static int read_sbc_header(uint8_t * packet, int size, int * offset, avdtp_sbc_codec_header_t * sbc_header);
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size){ static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size){
UNUSED(seid); UNUSED(seid);
int pos = 0; int pos = 0;
avdtp_media_packet_header_t media_header; avdtp_media_packet_header_t media_header;
media_header.version = packet[pos] & 0x03; if (!read_media_data_header(packet, size, &pos, &media_header)) return;
media_header.padding = get_bit16(packet[pos],2);
media_header.extension = get_bit16(packet[pos],3);
media_header.csrc_count = (packet[pos] >> 4) & 0x0F;
pos++;
media_header.marker = get_bit16(packet[pos],0);
media_header.payload_type = (packet[pos] >> 1) & 0x7F;
pos++;
media_header.sequence_number = big_endian_read_16(packet, pos);
pos+=2;
media_header.timestamp = big_endian_read_32(packet, pos);
pos+=4;
media_header.synchronization_source = big_endian_read_32(packet, pos);
pos+=4;
UNUSED(media_header);
// TODO: read csrc list
// printf_hexdump( packet, pos );
// printf("MEDIA HEADER: %u timestamp, version %u, padding %u, extension %u, csrc_count %u\n",
// media_header.timestamp, media_header.version, media_header.padding, media_header.extension, media_header.csrc_count);
// printf("MEDIA HEADER: marker %02x, payload_type %02x, sequence_number %u, synchronization_source %u\n",
// media_header.marker, media_header.payload_type, media_header.sequence_number, media_header.synchronization_source);
avdtp_sbc_codec_header_t sbc_header; avdtp_sbc_codec_header_t sbc_header;
sbc_header.fragmentation = get_bit16(packet[pos], 7); if (!read_sbc_header(packet, size, &pos, &sbc_header)) return;
sbc_header.starting_packet = get_bit16(packet[pos], 6);
sbc_header.last_packet = get_bit16(packet[pos], 5);
sbc_header.num_frames = packet[pos] & 0x0f;
pos++;
UNUSED(sbc_header); #ifdef HAVE_AUDIO_DMA
// printf("SBC HEADER: num_frames %u, fragmented %u, start %u, stop %u\n", sbc_header.num_frames, sbc_header.fragmentation, sbc_header.starting_packet, sbc_header.last_packet); // store sbc frame size for buffer management
// printf_hexdump( packet+pos, size-pos ); sbc_frame_size = (size-pos)/ sbc_header.num_frames;
#endif
#ifdef DECODE_SBC #if defined(HAVE_PORTAUDIO) || defined(STORE_SBC_TO_WAV_FILE)
btstack_sbc_decoder_process_data(&state, 0, packet+pos, size-pos); btstack_sbc_decoder_process_data(&state, 0, packet+pos, size-pos);
#endif #endif
#ifdef HAVE_AUDIO_DMA
btstack_ring_buffer_write(&ring_buffer, packet+pos, size-pos);
// decide on audio sync drift based on number of sbc frames in queue
int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available(&ring_buffer) / sbc_frame_size;
if (sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN){
sbc_samples_fix = 1; // duplicate last sample
} else if (sbc_frames_in_buffer <= OPTIMAL_FRAMES_MAX){
sbc_samples_fix = 0; // nothing to do
} else {
sbc_samples_fix = -1; // drop last sample
}
// dump
printf("%6u %03u %d\n", (int) btstack_run_loop_get_time_ms(), sbc_frames_in_buffer, sbc_samples_fix);
#endif
#ifdef STORE_SBC_TO_SBC_FILE #ifdef STORE_SBC_TO_SBC_FILE
fwrite(packet+pos, size-pos, 1, sbc_file); fwrite(packet+pos, size-pos, 1, sbc_file);
#endif #endif
#ifdef HAVE_PORTAUDIO }
log_info("PA: bytes avail after recv: %d", btstack_ring_buffer_bytes_available(&ring_buffer));
/* @section Handle PCM Data
*
* @text In this example, we use the [PortAudio library](http://www.portaudio.com) to play the audio stream.
* The PCM data are bufferd in a ring buffer.
* Aditionally, tha audio data can be stored in the avdtp_sink.wav file.
*/
#if defined(HAVE_PORTAUDIO) || defined(STORE_SBC_TO_WAV_FILE)
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
UNUSED(sample_rate);
UNUSED(context);
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(num_samples*num_channels, data);
frame_count++;
#endif #endif
#ifdef HAVE_PORTAUDIO
// store pcm samples in ring buffer
btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
if (!audio_stream_started){
audio_stream_paused = 1;
/* -- start stream -- */
PaError err = Pa_StartStream(stream);
if (err != paNoError){
printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err));
return;
}
audio_stream_started = 1;
}
#endif
#ifdef HAVE_AUDIO_DMA
// store in ring buffer
uint8_t * write_data = start_of_buffer(write_buffer);
uint16_t len = num_samples*num_channels*2;
memcpy(write_data, data, len);
audio_samples_len[write_buffer] = len;
// add/drop audio frame to fix drift
if (sbc_samples_fix > 0){
memcpy(write_data + len, write_data + len - 4, 4);
audio_samples_len[write_buffer] += 4;
}
if (sbc_samples_fix < 0){
audio_samples_len[write_buffer] -= 4;
}
write_buffer = next_buffer(write_buffer);
#endif
}
#endif
static int read_sbc_header(uint8_t * packet, int size, int * offset, avdtp_sbc_codec_header_t * sbc_header){
int sbc_header_len = 12; // without crc
int pos = *offset;
if (size - pos < sbc_header_len){
printf("Not enough data to read SBC header, expected %d, received %d\n", sbc_header_len, size-pos);
return 0;
}
sbc_header->fragmentation = get_bit16(packet[pos], 7);
sbc_header->starting_packet = get_bit16(packet[pos], 6);
sbc_header->last_packet = get_bit16(packet[pos], 5);
sbc_header->num_frames = packet[pos] & 0x0f;
pos++;
// printf("SBC HEADER: num_frames %u, fragmented %u, start %u, stop %u\n", sbc_header.num_frames, sbc_header.fragmentation, sbc_header.starting_packet, sbc_header.last_packet);
*offset = pos;
return 1;
}
static int read_media_data_header(uint8_t *packet, int size, int *offset, avdtp_media_packet_header_t *media_header){
int media_header_len = 12; // without crc
int pos = *offset;
if (size - pos < media_header_len){
printf("Not enough data to read media packet header, expected %d, received %d\n", media_header_len, size-pos);
return 0;
}
media_header->version = packet[pos] & 0x03;
media_header->padding = get_bit16(packet[pos],2);
media_header->extension = get_bit16(packet[pos],3);
media_header->csrc_count = (packet[pos] >> 4) & 0x0F;
pos++;
media_header->marker = get_bit16(packet[pos],0);
media_header->payload_type = (packet[pos] >> 1) & 0x7F;
pos++;
media_header->sequence_number = big_endian_read_16(packet, pos);
pos+=2;
media_header->timestamp = big_endian_read_32(packet, pos);
pos+=4;
media_header->synchronization_source = big_endian_read_32(packet, pos);
pos+=4;
*offset = pos;
// TODO: read csrc list
// printf_hexdump( packet, pos );
if (!is_media_header_reported_once){
is_media_header_reported_once = 1;
printf("MEDIA HEADER: %u timestamp, version %u, padding %u, extension %u, csrc_count %u\n",
media_header->timestamp, media_header->version, media_header->padding, media_header->extension, media_header->csrc_count);
printf("MEDIA HEADER: marker %02x, payload_type %02x, sequence_number %u, synchronization_source %u\n",
media_header->marker, media_header->payload_type, media_header->sequence_number, media_header->synchronization_source);
}
return 1;
} }
static void dump_sbc_capability(adtvp_media_codec_information_sbc_t media_codec_sbc){ static void dump_sbc_capability(adtvp_media_codec_information_sbc_t media_codec_sbc){
@ -393,20 +575,29 @@ static void dump_sbc_configuration(avdtp_media_codec_configuration_sbc_t configu
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
if (packet_type != HCI_EVENT_PACKET) return; if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST){
// inform about pin code request
bd_addr_t event_addr;
printf("Pin code request - using '0000'\n");
hci_event_pin_code_request_get_bd_addr(packet, event_addr);
gap_pin_code_response(event_addr, "0000");
}
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVDTP_META) return; if (hci_event_packet_get_type(packet) != HCI_EVENT_AVDTP_META) return;
UNUSED(channel); UNUSED(channel);
UNUSED(size); UNUSED(size);
uint8_t status; uint8_t status;
switch (packet[2]){ switch (packet[2]){
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED: case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet); avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet);
status = avdtp_subevent_signaling_connection_established_get_status(packet); status = avdtp_subevent_signaling_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS){ if (status != ERROR_CODE_SUCCESS){
printf("AVDTP connection establishment failed: status 0x%02x.\n", status); printf("AVDTP connection failed with status 0x%02x.\n", status);
break; break;
} }
printf("AVDTP connection established: avdtp_cid 0x%02x.\n", avdtp_cid); printf("AVDTP Sink connected: avdtp_cid 0x%02x.\n", avdtp_cid);
break; break;
case AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED: case AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
avdtp_cid = avdtp_subevent_signaling_connection_released_get_avdtp_cid(packet); avdtp_cid = avdtp_subevent_signaling_connection_released_get_avdtp_cid(packet);
@ -419,7 +610,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
avdtp_subevent_signaling_sep_found_get_media_type(packet), avdtp_subevent_signaling_sep_found_get_sep_type(packet)); avdtp_subevent_signaling_sep_found_get_media_type(packet), avdtp_subevent_signaling_sep_found_get_sep_type(packet));
break; break;
case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY: case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY:
printf("Received MEDIA_CODEC_SBC_CAPABILITY\n"); printf("Received SBC codec capabilities\n");
sbc_capability.sampling_frequency_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet); sbc_capability.sampling_frequency_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet);
sbc_capability.channel_mode_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet); sbc_capability.channel_mode_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet);
sbc_capability.block_length_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet); sbc_capability.block_length_bitmap = avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet);
@ -430,7 +621,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
dump_sbc_capability(sbc_capability); dump_sbc_capability(sbc_capability);
break; break;
case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:{ case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:{
printf("Received MEDIA_CODEC_SBC_CONFIGURATION\n"); printf("Received SBC codec configuration\n");
sbc_configuration.reconfigure = avdtp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet); sbc_configuration.reconfigure = avdtp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet);
sbc_configuration.num_channels = avdtp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet); sbc_configuration.num_channels = avdtp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet);
sbc_configuration.sampling_frequency = avdtp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet); sbc_configuration.sampling_frequency = avdtp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet);
@ -444,22 +635,61 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
dump_sbc_configuration(sbc_configuration); dump_sbc_configuration(sbc_configuration);
if (sbc_configuration.reconfigure){ if (sbc_configuration.reconfigure){
close_media_processing(); media_processing_close();
init_media_processing(sbc_configuration);
} else {
init_media_processing(sbc_configuration);
} }
media_processing_init(sbc_configuration);
break; break;
} }
case AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED: case AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED:
printf("Streaming connection opened.\n");
break;
case AVDTP_SUBEVENT_STREAMING_CONNECTION_RELEASED:
printf("Streaming connection released.\n");
is_cmd_triggered_localy = 0;
is_media_header_reported_once = 0;
media_processing_close();
break; break;
case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY: case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY:
printf("Received non SBC codec, event not parsed.\n"); printf("Received non SBC codec, event not parsed.\n");
break; break;
case AVDTP_SUBEVENT_SIGNALING_ACCEPT:
case AVDTP_SUBEVENT_SIGNALING_ACCEPT:{
switch (avdtp_subevent_signaling_accept_get_signal_identifier(packet)){
case AVDTP_SI_START:
printf("Stream started\n");
media_processing_init(sbc_configuration);
break;
case AVDTP_SI_SUSPEND:
printf("Stream paused\n");
media_processing_close();
break;
case AVDTP_SI_ABORT:
case AVDTP_SI_CLOSE:
printf("Stream stoped\n");
media_processing_close();
break;
default:
break;
}
if (is_cmd_triggered_localy){
is_cmd_triggered_localy = 0;
printf("AVDTP Sink command accepted\n");
}
break;
}
case AVDTP_SUBEVENT_SIGNALING_REJECT:
case AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT:
if (is_cmd_triggered_localy){
is_cmd_triggered_localy = 0;
printf("AVDTP Sink command rejected\n");
}
break; break;
default: default:
printf("AVDTP Sink event not parsed\n"); if (is_cmd_triggered_localy){
is_cmd_triggered_localy = 0;
}
printf("AVDTP Sink event 0x%02x not parsed\n", packet[2]);
break; break;
} }
} }
@ -506,31 +736,33 @@ static void show_usage(void){
} }
static void stdin_process(char cmd){ static void stdin_process(char cmd){
uint8_t status = ERROR_CODE_SUCCESS;
remote_seid = 1; remote_seid = 1;
is_cmd_triggered_localy = 1;
switch (cmd){ switch (cmd){
case 'c': case 'c':
printf("Establish AVDTP Sink connection to %s\n", device_addr_string); printf("Establish AVDTP Sink connection to %s\n", device_addr_string);
avdtp_sink_connect(device_addr, &avdtp_cid); status = avdtp_sink_connect(device_addr, &avdtp_cid);
break; break;
case 'C': case 'C':
printf("Disconnect AVDTP Sink\n"); printf("Disconnect AVDTP Sink\n");
avdtp_sink_disconnect(avdtp_cid); status = avdtp_sink_disconnect(avdtp_cid);
break; break;
case 'd': case 'd':
printf("Discover stream endpoints of %s\n", device_addr_string); printf("Discover stream endpoints of %s\n", device_addr_string);
avdtp_sink_discover_stream_endpoints(avdtp_cid); status = avdtp_sink_discover_stream_endpoints(avdtp_cid);
break; break;
case 'g': case 'g':
printf("Get capabilities of stream endpoint with seid %d\n", remote_seid); printf("Get capabilities of stream endpoint with seid %d\n", remote_seid);
avdtp_sink_get_capabilities(avdtp_cid, remote_seid); status = avdtp_sink_get_capabilities(avdtp_cid, remote_seid);
break; break;
case 'a': case 'a':
printf("Get all capabilities of stream endpoint with seid %d\n", remote_seid); printf("Get all capabilities of stream endpoint with seid %d\n", remote_seid);
avdtp_sink_get_all_capabilities(avdtp_cid, remote_seid); status = avdtp_sink_get_all_capabilities(avdtp_cid, remote_seid);
break; break;
case 'f': case 'f':
printf("Get configuration of stream endpoint with seid %d\n", remote_seid); printf("Get configuration of stream endpoint with seid %d\n", remote_seid);
avdtp_sink_get_configuration(avdtp_cid, remote_seid); status = avdtp_sink_get_configuration(avdtp_cid, remote_seid);
break; break;
case 's': case 's':
printf("Set configuration of stream endpoint with seid %d\n", remote_seid); printf("Set configuration of stream endpoint with seid %d\n", remote_seid);
@ -539,7 +771,7 @@ static void stdin_process(char cmd){
remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_configuration); remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_configuration);
remote_configuration.media_codec.media_codec_information = media_sbc_codec_configuration; remote_configuration.media_codec.media_codec_information = media_sbc_codec_configuration;
avdtp_sink_set_configuration(avdtp_cid, local_seid, remote_seid, remote_configuration_bitmap, remote_configuration); status = avdtp_sink_set_configuration(avdtp_cid, local_seid, remote_seid, remote_configuration_bitmap, remote_configuration);
break; break;
case 'R': case 'R':
printf("Reconfigure stream endpoint with seid %d\n", remote_seid); printf("Reconfigure stream endpoint with seid %d\n", remote_seid);
@ -548,37 +780,42 @@ static void stdin_process(char cmd){
remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_reconfiguration); remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_reconfiguration);
remote_configuration.media_codec.media_codec_information = media_sbc_codec_reconfiguration; remote_configuration.media_codec.media_codec_information = media_sbc_codec_reconfiguration;
avdtp_sink_reconfigure(avdtp_cid, local_seid, remote_seid, remote_configuration_bitmap, remote_configuration); status = avdtp_sink_reconfigure(avdtp_cid, local_seid, remote_seid, remote_configuration_bitmap, remote_configuration);
break; break;
case 'o': case 'o':
printf("Establish stream between local %d and remote %d seid\n", local_seid, remote_seid); printf("Establish stream between local %d and remote %d seid\n", local_seid, remote_seid);
avdtp_sink_open_stream(avdtp_cid, local_seid, remote_seid); status = avdtp_sink_open_stream(avdtp_cid, local_seid, remote_seid);
break; break;
case 'm': case 'm':
printf("Start stream between local %d and remote %d seid\n", local_seid, remote_seid); printf("Start stream between local %d and remote %d seid\n", local_seid, remote_seid);
avdtp_sink_start_stream(avdtp_cid, local_seid); status = avdtp_sink_start_stream(avdtp_cid, local_seid);
break; break;
case 'A': case 'A':
printf("Abort stream between local %d and remote %d seid\n", local_seid, remote_seid); printf("Abort stream between local %d and remote %d seid\n", local_seid, remote_seid);
avdtp_sink_abort_stream(avdtp_cid, local_seid); status = avdtp_sink_abort_stream(avdtp_cid, local_seid);
break; break;
case 'S': case 'S':
printf("Release stream between local %d and remote %d seid\n", local_seid, remote_seid); printf("Release stream between local %d and remote %d seid\n", local_seid, remote_seid);
avdtp_sink_stop_stream(avdtp_cid, local_seid); status = avdtp_sink_stop_stream(avdtp_cid, local_seid);
break; break;
case 'P': case 'P':
printf("Susspend stream between local %d and remote %d seid\n", local_seid, remote_seid); printf("Suspend stream between local %d and remote %d seid\n", local_seid, remote_seid);
avdtp_sink_suspend(avdtp_cid, local_seid); status = avdtp_sink_suspend(avdtp_cid, local_seid);
break; break;
case '\n': case '\n':
case '\r': case '\r':
is_cmd_triggered_localy = 0;
break; break;
default: default:
is_cmd_triggered_localy = 0;
show_usage(); show_usage();
break; break;
} }
if (status != ERROR_CODE_SUCCESS){
printf("AVDTP Sink cmd \'%c\' failed, status 0x%02x\n", cmd, status);
}
} }
#endif #endif
@ -613,7 +850,7 @@ int btstack_main(int argc, const char * argv[]){
a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer, 0x10001, 1, NULL, NULL); a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer, 0x10001, 1, NULL, NULL);
sdp_register_service(sdp_avdtp_sink_service_buffer); sdp_register_service(sdp_avdtp_sink_service_buffer);
gap_set_local_name("BTstack A2DP Sink PTS Test"); gap_set_local_name("BTstack AVDTP Sink PTS 00:00:00:00:00:00");
gap_discoverable_control(1); gap_discoverable_control(1);
gap_set_class_of_device(0x200408); gap_set_class_of_device(0x200408);

View File

@ -690,7 +690,7 @@ int btstack_main(int argc, const char * argv[]){
a2dp_source_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL); a2dp_source_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL);
sdp_register_service(sdp_avdtp_source_service_buffer); sdp_register_service(sdp_avdtp_source_service_buffer);
gap_set_local_name("BTstack PTS AVDTP Source Test 00:00:00:00:00:00"); gap_set_local_name("BTstack AVDTP Source PTS 00:00:00:00:00:00");
gap_discoverable_control(1); gap_discoverable_control(1);
gap_set_class_of_device(0x200408); gap_set_class_of_device(0x200408);
sscanf_bd_addr(device_addr_string, device_addr); sscanf_bd_addr(device_addr_string, device_addr);

View File

@ -502,7 +502,7 @@ static uint8_t media_sbc_codec_capabilities[] = {
#ifdef HAVE_BTSTACK_STDIN #ifdef HAVE_BTSTACK_STDIN
static void stdin_process(char cmd){ static void stdin_process(char cmd){
uint8_t status; uint8_t status = ERROR_CODE_SUCCESS;
sep.seid = 1; sep.seid = 1;
switch (cmd){ switch (cmd){
case 'b': case 'b':
@ -754,9 +754,11 @@ static void stdin_process(char cmd){
status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]); status = avrcp_browsing_controller_set_browsed_player(browsing_cid, players[0]);
break; break;
case 'p': case 'p':
printf("AVRCP Browsing: get media players\n"); players[next_player_index()] = 1;
player_index = -1;
status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL); // printf("AVRCP Browsing: get media players. Brosing cid 0x%02X\n", browsing_cid);
// player_index = -1;
// status = avrcp_browsing_controller_get_media_players(browsing_cid, 0, 0xFFFFFFFF, AVRCP_MEDIA_ATTR_ALL);
break; break;
case 'Q': case 'Q':
printf("AVRCP Browsing: browse folders\n"); printf("AVRCP Browsing: browse folders\n");
@ -792,6 +794,9 @@ static void stdin_process(char cmd){
break; break;
} }
if (status != ERROR_CODE_SUCCESS){
printf("Could not complete cmd %c, status 0x%02X\n", cmd, status);
}
} }
#endif #endif
@ -839,7 +844,7 @@ int btstack_main(int argc, const char * argv[]){
avrcp_controller_create_sdp_record(sdp_avrcp_controller_service_buffer, 0x10001, AVRCP_BROWSING_ENABLED, 1, NULL, NULL); avrcp_controller_create_sdp_record(sdp_avrcp_controller_service_buffer, 0x10001, AVRCP_BROWSING_ENABLED, 1, NULL, NULL);
sdp_register_service(sdp_avrcp_controller_service_buffer); sdp_register_service(sdp_avrcp_controller_service_buffer);
gap_set_local_name("BTstack AVRCP PTS Test"); gap_set_local_name("BTstack AVRCP Controller PTS 00:00:00:00:00:00");
gap_discoverable_control(1); gap_discoverable_control(1);
gap_set_class_of_device(0x200408); gap_set_class_of_device(0x200408);