example/a2dp_sink_demo: improve example annotation

This commit is contained in:
Milanka Ringwald 2020-06-23 11:38:26 +02:00
parent be5001946f
commit fb944a68cb

View File

@ -48,17 +48,15 @@
* receive an audio data stream from a remote A2DP Source device. In addition,
* the AVRCP Controller is used to get information on currently played media,
* such are title, artist and album, as well as to control the playback,
* i.e. to play, stop, repeat, etc.
* i.e. to play, stop, repeat, etc. If HAVE_BTSTACK_STDIN is set, press SPACE on
* the console to show the available AVDTP and AVRCP commands.
*
* @test To test with a remote device, e.g. a mobile phone,
* @text To test with a remote device, e.g. a mobile phone,
* pair from the remote device with the demo, then start playing music on the remote device.
* Alternatively, set the device_addr_string to the Bluetooth address of your
* remote device in the code, and call connect from the UI.
*
* @test To controll the playback, tap SPACE on the console to show the available
* AVRCP commands.
*
* For more info on BTstack audio, see our blog post
* @text For more info on BTstack audio, see our blog post
* [A2DP Sink and Source on STM32 F4 Discovery Board](http://bluekitchen-gmbh.com/a2dp-sink-and-source-on-stm32-f4-discovery-board/).
*
*/
@ -108,7 +106,6 @@ static unsigned int sbc_frame_size;
static uint8_t decoded_audio_storage[(128+16) * BYTES_PER_FRAME];
static btstack_ring_buffer_t decoded_audio_ring_buffer;
//
static int audio_stream_started;
// temp storage of lower-layer request
@ -128,19 +125,6 @@ static FILE * sbc_file;
static char * sbc_filename = "av2dp_sink_demo.sbc";
#endif
static int volume_percentage = 0;
typedef struct {
// bitmaps
uint8_t sampling_frequency_bitmap;
uint8_t channel_mode_bitmap;
uint8_t block_length_bitmap;
uint8_t subbands_bitmap;
uint8_t allocation_method_bitmap;
uint8_t min_bitpool_value;
uint8_t max_bitpool_value;
} adtvp_media_codec_information_sbc_t;
typedef struct {
int reconfigure;
int num_channels;
@ -154,8 +138,10 @@ typedef struct {
int frames_per_buffer;
} avdtp_media_codec_configuration_sbc_t;
static uint8_t events_num = 1;
static avdtp_media_codec_configuration_sbc_t sbc_configuration;
static int volume_percentage = 0;
static uint8_t events_num = 1;
static uint8_t events[] = {
AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED
};
@ -164,32 +150,27 @@ static uint8_t companies_num = 1;
static uint8_t companies[] = {
0x00, 0x19, 0x58 //BT SIG registered CompanyID
};
#ifdef HAVE_BTSTACK_STDIN
// pts:
static const char * device_addr_string = "6C:72:E7:10:22:EE";
// mac 2013: static const char * device_addr_string = "84:38:35:65:d1:15";
// iPhone 5S:
// static const char * device_addr_string = "54:E4:3A:26:A2:39";
#endif
static uint8_t sdp_avdtp_sink_service_buffer[150];
static uint8_t sdp_avrcp_target_service_buffer[150];
static avdtp_media_codec_configuration_sbc_t sbc_configuration;
static uint16_t a2dp_cid = 0;
static uint8_t a2dp_local_seid = 0;
static uint8_t value[100];
static btstack_packet_callback_registration_t hci_event_callback_registration;
static int media_initialized = 0;
#ifdef HAVE_BTSTACK_STDIN
// mac 2013: static const char * device_addr_string = "84:38:35:65:d1:15";
// iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39";
static bd_addr_t device_addr;
#endif
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint8_t sdp_avdtp_sink_service_buffer[150];
static uint8_t sdp_avrcp_target_service_buffer[150];
static uint8_t sdp_avrcp_controller_service_buffer[200];
static uint16_t a2dp_cid = 0;
static uint8_t a2dp_local_seid = 0;
static uint16_t avrcp_cid = 0;
static uint8_t avrcp_connected = 0;
static uint8_t sdp_avrcp_controller_service_buffer[200];
static uint8_t avrcp_subevent_value[100];
static uint8_t media_sbc_codec_capabilities[] = {
0xFF,//(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
@ -203,18 +184,23 @@ static uint8_t media_sbc_codec_configuration[] = {
2, 53
};
static int media_initialized = 0;
static btstack_resample_t resample_instance;
/* @section Main Application Setup
*
* @text The Listing MainConfiguration shows how to setup AD2P Sink and AVRCP controller services.
* To announce A2DP Sink and AVRCP Controller services, you need to create corresponding
* SDP records and register them with the SDP service.
* You'll also need to register several packet handlers:
* @text The Listing MainConfiguration shows how to setup AD2P Sink and AVRCP services.
* Besides calling init() method for each service, you'll also need to register several packet handlers:
* - hci_packet_handler - handles legacy pairing, here by using fixed '0000' pin code.
* - a2dp_sink_packet_handler - handles events on stream connection status (established, released), the media codec configuration, and, the status of the stream itself (opened, paused, stopped).
* - handle_l2cap_media_data_packet - used to receive streaming data. If STORE_TO_WAV_FILE directive (check btstack_config.h) is used, the SBC decoder will be used to decode the SBC data into PCM frames. The resulting PCM frames are then processed in the SBC Decoder callback.
* - avrcp_packet_handler - receives connect/disconnect event.
* - avrcp_controller_packet_handler - receives answers for sent AVRCP commands.
* - avrcp_target_packet_handler - receives AVRCP commands, and registered notifications.
* - stdin_process callback - used to trigger AVRCP commands to the A2DP Source device, such are get now playing info, start, stop, volume control. Requires HAVE_BTSTACK_STDIN.
* - avrcp_controller_packet_handler - used to receive answers for AVRCP commands,
*
* @text To announce A2DP Sink and AVRCP services, you need to create corresponding
* SDP records and register them with the SDP service.
*
* @text Note, currently only the SBC codec is supported.
* If you want to store the audio data in a file, you'll need to define STORE_TO_WAV_FILE.
@ -225,11 +211,11 @@ static btstack_resample_t resample_instance;
/* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */
static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * event, uint16_t event_size);
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size);
static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size);
static int a2dp_and_avrcp_setup(void){
@ -302,6 +288,7 @@ static int a2dp_and_avrcp_setup(void){
#endif
return 0;
}
/* LISTING_END */
static void playback_handler(int16_t * buffer, uint16_t num_audio_frames){
@ -504,10 +491,6 @@ static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16
btstack_resample_set_factor(&resample_instance, resampling_factor);
// dump
// printf("%6u %03u %05x\n", (int) btstack_run_loop_get_time_ms(), sbc_frames_in_buffer, resampling_factor);
// log_info("%03u %05x", sbc_frames_in_buffer, resampling_factor);
// start stream if enough frames buffered
if (!audio_stream_started && sbc_frames_in_buffer >= OPTIMAL_FRAMES_MIN){
audio_stream_started = 1;
@ -532,7 +515,6 @@ static int read_sbc_header(uint8_t * packet, int size, int * offset, avdtp_sbc_c
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;
}
@ -647,7 +629,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
return;
}
memset(value, 0, sizeof(value));
memset(avrcp_subevent_value, 0, sizeof(avrcp_subevent_value));
switch (packet[2]){
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_POS_CHANGED:
printf("AVRCP Controller: Playback position changed, position %d ms\n", (unsigned int) avrcp_subevent_notification_playback_pos_changed_get_playback_position_ms(packet));
@ -683,29 +665,29 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0){
memcpy(value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
printf("AVRCP Controller: Title: %s\n", value);
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
printf("AVRCP Controller: Title: %s\n", avrcp_subevent_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0){
memcpy(value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
printf("AVRCP Controller: Artist: %s\n", value);
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
printf("AVRCP Controller: Artist: %s\n", avrcp_subevent_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0){
memcpy(value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
printf("AVRCP Controller: Album: %s\n", value);
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
printf("AVRCP Controller: Album: %s\n", avrcp_subevent_value);
}
break;
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0){
memcpy(value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
printf("AVRCP Controller: Genre: %s\n", value);
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
printf("AVRCP Controller: Genre: %s\n", avrcp_subevent_value);
}
break;
@ -833,7 +815,6 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
uint16_t cid;
bd_addr_t address;
uint8_t status;
@ -868,49 +849,37 @@ static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint
case A2DP_SUBEVENT_STREAM_ESTABLISHED:
a2dp_subevent_stream_established_get_bd_addr(packet, address);
status = a2dp_subevent_stream_established_get_status(packet);
cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
if (!a2dp_cid){
// incoming connection
a2dp_cid = cid;
} else if (cid != a2dp_cid) {
break;
}
if (status){
printf("A2DP Sink : Streaming connection failed, status 0x%02x\n", status);
break;
}
a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
memcpy(device_addr, address, 6);
a2dp_local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
printf("A2DP Sink : Streaming connection is established, address %s, cid 0x%02X, local seid %d\n", bd_addr_to_str(address), a2dp_cid, a2dp_local_seid);
break;
case A2DP_SUBEVENT_STREAM_STARTED:
cid = a2dp_subevent_stream_started_get_a2dp_cid(packet);
if (cid != a2dp_cid) break;
a2dp_local_seid = a2dp_subevent_stream_started_get_local_seid(packet);
printf("A2DP Sink : Stream started\n");
media_processing_start();
break;
case A2DP_SUBEVENT_STREAM_SUSPENDED:
cid = a2dp_subevent_stream_suspended_get_a2dp_cid(packet);
if (cid != a2dp_cid) break;
a2dp_local_seid = a2dp_subevent_stream_suspended_get_local_seid(packet);
printf("A2DP Sink : Stream paused\n");
media_processing_pause();
break;
case A2DP_SUBEVENT_STREAM_RELEASED:
a2dp_local_seid = a2dp_subevent_stream_released_get_local_seid(packet);
printf("A2DP Sink : Stream released\n");
media_processing_close();
break;
case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
cid = a2dp_subevent_signaling_connection_released_get_a2dp_cid(packet);
printf("A2DP Sink : Signaling connection released\n");
media_processing_close();
break;
default:
printf("A2DP Sink : Not parsed 0x%02x\n", packet[2]);
break;
@ -1123,3 +1092,4 @@ int btstack_main(int argc, const char * argv[]){
hci_power_control(HCI_POWER_ON);
return 0;
}
/* EXAMPLE_END */