mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-30 06:32:43 +00:00
example/a2dp_sink_demo: improve example annotation
This commit is contained in:
parent
be5001946f
commit
fb944a68cb
@ -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/).
|
||||
*
|
||||
*/
|
||||
@ -107,8 +105,7 @@ static unsigned int sbc_frame_size;
|
||||
// rest buffer for not fully used sbc frames, with additional frames for resampling
|
||||
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 */
|
Loading…
x
Reference in New Issue
Block a user