a2dp source demo: cleanup

This commit is contained in:
Milanka Ringwald 2017-07-12 15:24:11 +02:00
parent 9413b167ec
commit 67673f1cc2
4 changed files with 137 additions and 175 deletions

View File

@ -46,25 +46,32 @@
#include "hxcmod.h"
#include "mods/mod.h"
#define NUM_CHANNELS 2
#define A2DP_SAMPLE_RATE 44100
#define BYTES_PER_AUDIO_SAMPLE (2*NUM_CHANNELS)
#define FILL_AUDIO_BUFFER_TIMEOUT_MS 10
#ifndef M_PI
#define M_PI 3.14159265
#endif
#define TABLE_SIZE_441HZ 100
typedef struct {
int left_phase;
int right_phase;
} paTestData;
#define NUM_CHANNELS 2
#define A2DP_SAMPLE_RATE 44100
#define BYTES_PER_AUDIO_SAMPLE (2*NUM_CHANNELS)
#define AUDIO_TIMEOUT_MS 10
#define TABLE_SIZE_441HZ 100
typedef enum {
STREAM_SINE,
STREAM_MOD
} stream_data_source_t;
typedef struct {
uint16_t a2dp_cid;
uint8_t local_seid;
uint32_t time_audio_data_sent; // ms
uint32_t acc_num_missed_samples;
uint32_t samples_ready;
btstack_timer_source_t audio_timer;
uint8_t streaming;
int max_media_payload_size;
uint8_t sbc_storage[1030];
uint16_t sbc_storage_count;
uint8_t sbc_ready_to_send;
} a2dp_media_sending_context_t;
static uint8_t media_sbc_codec_capabilities[] = {
(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
@ -88,135 +95,47 @@ static const int16_t sine_int16[] = {
static char * device_name = "A2DP Source BTstack";
#ifdef HAVE_BTSTACK_STDIN
// mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3};
// pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5};
// mac 2013: static bd_addr_t remote = {0x84, 0x38, 0x35, 0x65, 0xd1, 0x15};
// phone 2013: static bd_addr_t remote = {0xD8, 0xBB, 0x2C, 0xDF, 0xF0, 0xF2};
// 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";
// mac 2013: static const char * device_addr_string = "84:38:35:65:d1:15";
// phone 2013: static const char * device_addr_string = "D8:BB:2C:DF:F0:F2";
// minijambox:
static bd_addr_t remote = {0x00, 0x21, 0x3c, 0xac, 0xf7, 0x38};
// head phones: static bd_addr_t remote = {0x00, 0x18, 0x09, 0x28, 0x50, 0x18};
// bt dongle: -u 02-04-01
// static bd_addr_t remote = {0x00, 0x15, 0x83, 0x5F, 0x9D, 0x46};
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";
// bt dongle: static const char * device_addr_string = "00:15:83:5F:9D:46";
#endif
static uint8_t sdp_avdtp_source_service_buffer[150];
static bd_addr_t device_addr;
static uint8_t sdp_a2dp_source_service_buffer[150];
static uint8_t media_sbc_codec_configuration[4];
typedef struct {
uint16_t a2dp_cid;
uint8_t local_seid;
uint32_t time_audio_data_sent; // ms
uint32_t acc_num_missed_samples;
uint32_t samples_ready;
btstack_timer_source_t fill_audio_buffer_timer;
uint8_t streaming;
int max_media_payload_size;
uint8_t sbc_storage[1030];
uint16_t sbc_storage_count;
uint8_t sbc_ready_to_send;
} a2dp_media_sending_context_t;
static a2dp_media_sending_context_t media_tracker;
static paTestData sin_data;
static stream_data_source_t data_source;
static int hxcmod_initialized = 0;
static int sine_phase;
static int hxcmod_initialized;
static modcontext mod_context;
static tracker_buffer_state trkbuf;
static uint8_t local_seid = 0;
stream_data_source_t data_source = STREAM_SINE;
static btstack_packet_callback_registration_t hci_event_callback_registration;
static void a2dp_fill_audio_buffer_timer_start(a2dp_media_sending_context_t * context);
static void a2dp_fill_audio_buffer_timer_stop(a2dp_media_sending_context_t * context);
static void a2dp_fill_audio_buffer_timer_pause(a2dp_media_sending_context_t * context);
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
uint8_t status;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_A2DP_META:
switch (packet[2]){
case A2DP_SUBEVENT_STREAM_ESTABLISHED:
status = a2dp_subevent_stream_established_get_status(packet);
if (status){
printf("Stream establishment failed: status 0x%02x.\n", status);
break;
}
media_tracker.local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
media_tracker.a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
printf("Stream established: a2dp cid 0x%02x, local seid %d, remote seid %d.\n",
media_tracker.a2dp_cid, media_tracker.local_seid, a2dp_subevent_stream_established_get_remote_seid(packet));
break;
case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW:{
if (local_seid != media_tracker.local_seid) break;
int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length();
int bytes_in_storage = media_tracker.sbc_storage_count;
uint8_t num_frames = bytes_in_storage / num_bytes_in_frame;
a2dp_source_stream_send_media_payload(media_tracker.local_seid, media_tracker.sbc_storage, bytes_in_storage, num_frames, 0);
media_tracker.sbc_storage_count = 0;
media_tracker.sbc_ready_to_send = 0;
break;
}
case A2DP_SUBEVENT_STREAM_STARTED:
if (local_seid != media_tracker.local_seid) break;
if (!a2dp_source_stream_endpoint_ready(media_tracker.a2dp_cid, media_tracker.local_seid)) break;
a2dp_fill_audio_buffer_timer_start(&media_tracker);
printf("Stream started.\n");
break;
case A2DP_SUBEVENT_STREAM_SUSPENDED:
printf("Stream paused.\n");
a2dp_fill_audio_buffer_timer_pause(&media_tracker);
break;
case A2DP_SUBEVENT_STREAM_RELEASED:
printf("Stream released.\n");
a2dp_fill_audio_buffer_timer_stop(&media_tracker);
break;
default:
printf("AVDTP Source demo: event 0x%02x is not implemented\n", packet[2]);
break;
}
break;
default:
break;
}
break;
default:
// other packet type
break;
}
static void a2dp_demo_send_media_packet(void){
int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length();
int bytes_in_storage = media_tracker.sbc_storage_count;
uint8_t num_frames = bytes_in_storage / num_bytes_in_frame;
a2dp_source_stream_send_media_payload(media_tracker.local_seid, media_tracker.sbc_storage, bytes_in_storage, num_frames, 0);
media_tracker.sbc_storage_count = 0;
media_tracker.sbc_ready_to_send = 0;
}
static void produce_sine_audio(int16_t * pcm_buffer, void *user_data, int num_samples_to_write){
paTestData *data = (paTestData*)user_data;
static void produce_sine_audio(int16_t * pcm_buffer, int num_samples_to_write){
int count;
for (count = 0; count < num_samples_to_write ; count++){
pcm_buffer[count * 2] = sine_int16[data->left_phase];
pcm_buffer[count * 2 + 1] = sine_int16[data->right_phase];
data->left_phase += 1;
if (data->left_phase >= TABLE_SIZE_441HZ){
data->left_phase -= TABLE_SIZE_441HZ;
pcm_buffer[count * 2] = sine_int16[sine_phase];
pcm_buffer[count * 2 + 1] = sine_int16[sine_phase];
sine_phase++;
if (sine_phase >= TABLE_SIZE_441HZ){
sine_phase -= TABLE_SIZE_441HZ;
}
data->right_phase += 1;
if (data->right_phase >= TABLE_SIZE_441HZ){
data->right_phase -= TABLE_SIZE_441HZ;
}
}
}
@ -227,7 +146,7 @@ static void produce_mod_audio(int16_t * pcm_buffer, int num_samples_to_write){
static void produce_audio(int16_t * pcm_buffer, int num_samples){
switch (data_source){
case STREAM_SINE:
produce_sine_audio(pcm_buffer, &sin_data, num_samples);
produce_sine_audio(pcm_buffer, num_samples);
break;
case STREAM_MOD:
produce_mod_audio(pcm_buffer, num_samples);
@ -258,13 +177,13 @@ static int fill_sbc_audio_buffer(a2dp_media_sending_context_t * context){
return total_num_bytes_read;
}
static void avdtp_fill_audio_buffer_timeout_handler(btstack_timer_source_t * timer){
static void avdtp_audio_timeout_handler(btstack_timer_source_t * timer){
a2dp_media_sending_context_t * context = (a2dp_media_sending_context_t *) btstack_run_loop_get_timer_context(timer);
btstack_run_loop_set_timer(&context->fill_audio_buffer_timer, FILL_AUDIO_BUFFER_TIMEOUT_MS);
btstack_run_loop_add_timer(&context->fill_audio_buffer_timer);
btstack_run_loop_set_timer(&context->audio_timer, AUDIO_TIMEOUT_MS);
btstack_run_loop_add_timer(&context->audio_timer);
uint32_t now = btstack_run_loop_get_time_ms();
uint32_t update_period_ms = FILL_AUDIO_BUFFER_TIMEOUT_MS;
uint32_t update_period_ms = AUDIO_TIMEOUT_MS;
if (context->time_audio_data_sent > 0){
update_period_ms = now - context->time_audio_data_sent;
}
@ -290,40 +209,102 @@ static void avdtp_fill_audio_buffer_timeout_handler(btstack_timer_source_t * tim
}
}
static void a2dp_fill_audio_buffer_timer_start(a2dp_media_sending_context_t * context){
static void a2dp_demo_timer_start(a2dp_media_sending_context_t * context){
context->max_media_payload_size = a2dp_max_media_payload_size(context->local_seid);
context->sbc_storage_count = 0;
context->sbc_ready_to_send = 0;
context->streaming = 1;
btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer);
btstack_run_loop_set_timer_handler(&context->fill_audio_buffer_timer, avdtp_fill_audio_buffer_timeout_handler);
btstack_run_loop_set_timer_context(&context->fill_audio_buffer_timer, context);
btstack_run_loop_set_timer(&context->fill_audio_buffer_timer, FILL_AUDIO_BUFFER_TIMEOUT_MS);
btstack_run_loop_add_timer(&context->fill_audio_buffer_timer);
btstack_run_loop_remove_timer(&context->audio_timer);
btstack_run_loop_set_timer_handler(&context->audio_timer, avdtp_audio_timeout_handler);
btstack_run_loop_set_timer_context(&context->audio_timer, context);
btstack_run_loop_set_timer(&context->audio_timer, AUDIO_TIMEOUT_MS);
btstack_run_loop_add_timer(&context->audio_timer);
}
static void a2dp_fill_audio_buffer_timer_stop(a2dp_media_sending_context_t * context){
static void a2dp_demo_timer_stop(a2dp_media_sending_context_t * context){
context->time_audio_data_sent = 0;
context->acc_num_missed_samples = 0;
context->samples_ready = 0;
context->streaming = 1;
context->sbc_storage_count = 0;
context->sbc_ready_to_send = 0;
btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer);
btstack_run_loop_remove_timer(&context->audio_timer);
}
static void a2dp_fill_audio_buffer_timer_pause(a2dp_media_sending_context_t * context){
// context->time_audio_data_sent = 0;
btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer);
static void a2dp_demo_timer_pause(a2dp_media_sending_context_t * context){
btstack_run_loop_remove_timer(&context->audio_timer);
}
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
uint8_t status;
uint8_t local_seid;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_A2DP_META:
switch (packet[2]){
case A2DP_SUBEVENT_STREAM_ESTABLISHED:
status = a2dp_subevent_stream_established_get_status(packet);
if (status){
printf("Stream establishment failed: status 0x%02x.\n", status);
break;
}
local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
if (local_seid != media_tracker.local_seid){
printf("Stream establishment failed: wrong local seid %d, expected %d.\n", local_seid, media_tracker.local_seid);
break;
}
media_tracker.a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
printf("Stream established: a2dp cid 0x%02x, local seid %d, remote seid %d.\n",
media_tracker.a2dp_cid, media_tracker.local_seid, a2dp_subevent_stream_established_get_remote_seid(packet));
break;
case A2DP_SUBEVENT_STREAM_STARTED:
a2dp_demo_timer_start(&media_tracker);
printf("Stream started.\n");
break;
case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW:
a2dp_demo_send_media_packet();
break;
case A2DP_SUBEVENT_STREAM_SUSPENDED:
printf("Stream paused.\n");
a2dp_demo_timer_pause(&media_tracker);
break;
case A2DP_SUBEVENT_STREAM_RELEASED:
printf("Stream released.\n");
a2dp_demo_timer_stop(&media_tracker);
break;
default:
printf("AVDTP Source demo: event 0x%02x is not implemented\n", packet[2]);
break;
}
break;
default:
break;
}
break;
default:
// other packet type
break;
}
}
#ifdef HAVE_BTSTACK_STDIN
static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth AVDTP SOURCE Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("c - create connection to addr %s\n", bd_addr_to_str(remote));
printf("\n--- Bluetooth A2DP Source Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("c - create connection to addr %s\n", device_addr_string);
printf("x - start streaming sine\n");
if (hxcmod_initialized){
printf("z - start streaming '%s'\n", mod_name);
@ -337,8 +318,8 @@ static void show_usage(void){
static void stdin_process(char cmd){
switch (cmd){
case 'c':
printf("Creating L2CAP Connection to %s, PSM_AVDTP\n", bd_addr_to_str(remote));
a2dp_source_establish_stream(remote, local_seid, &media_tracker.a2dp_cid);
printf("Creating L2CAP Connection to %s, PSM_AVDTP\n", device_addr_string);
a2dp_source_establish_stream(device_addr, media_tracker.local_seid, &media_tracker.a2dp_cid);
break;
case 'x':
printf("Playing sine.\n");
@ -371,22 +352,18 @@ int btstack_main(int argc, const char * argv[]){
UNUSED(argc);
(void)argv;
/* Register for HCI events */
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
// Initialize AVDTP Source
a2dp_source_init();
a2dp_source_register_packet_handler(&packet_handler);
local_seid = a2dp_source_create_stream_endpoint(AVDTP_AUDIO, AVDTP_CODEC_SBC, media_sbc_codec_capabilities, sizeof(media_sbc_codec_capabilities), media_sbc_codec_configuration, sizeof(media_sbc_codec_configuration));
media_tracker.local_seid = a2dp_source_create_stream_endpoint(AVDTP_AUDIO, AVDTP_CODEC_SBC, media_sbc_codec_capabilities, sizeof(media_sbc_codec_capabilities), media_sbc_codec_configuration, sizeof(media_sbc_codec_configuration));
// Initialize SDP
sdp_init();
memset(sdp_avdtp_source_service_buffer, 0, sizeof(sdp_avdtp_source_service_buffer));
a2dp_source_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL);
sdp_register_service(sdp_avdtp_source_service_buffer);
memset(sdp_a2dp_source_service_buffer, 0, sizeof(sdp_a2dp_source_service_buffer));
a2dp_source_create_sdp_record(sdp_a2dp_source_service_buffer, 0x10002, 1, NULL, NULL);
sdp_register_service(sdp_a2dp_source_service_buffer);
gap_set_local_name(device_name);
gap_discoverable_control(1);
@ -399,11 +376,12 @@ int btstack_main(int argc, const char * argv[]){
printf("loaded mod '%s', size %u\n", mod_name, mod_len);
}
// turn on!
hci_power_control(HCI_POWER_ON);
#ifdef HAVE_BTSTACK_STDIN
// parse human readable Bluetooth address
sscanf_bd_addr(device_addr_string, device_addr);
btstack_stdin_setup(stdin_process);
#endif
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}

View File

@ -62,10 +62,6 @@
#include "sco_demo_util.h"
#ifdef HAVE_BTSTACK_STDIN
#include "btstack_stdin.h"
#endif
uint8_t hfp_service_buffer[150];
const uint8_t rfcomm_channel_nr = 1;
const char hfp_hf_service_name[] = "BTstack HFP HF Demo";

View File

@ -459,10 +459,6 @@ uint8_t a2dp_source_start_stream(uint16_t avdtp_cid, uint8_t local_seid){
return avdtp_start_stream(avdtp_cid, local_seid, &a2dp_source_context);
}
uint8_t a2dp_source_release_stream(uint16_t avdtp_cid, uint8_t local_seid){
return avdtp_stop_stream(avdtp_cid, local_seid, &a2dp_source_context);
}
uint8_t a2dp_source_pause_stream(uint16_t avdtp_cid, uint8_t local_seid){
return avdtp_suspend_stream(avdtp_cid, local_seid, &a2dp_source_context);
}
@ -477,7 +473,6 @@ uint8_t a2dp_source_stream_endpoint_ready(uint16_t avdtp_cid, uint8_t local_seid
return (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING);
}
static void a2dp_source_setup_media_header(uint8_t * media_packet, int size, int *offset, uint8_t marker, uint16_t sequence_number){
if (size < AVDTP_MEDIA_PAYLOAD_HEADER_SIZE){
log_error("small outgoing buffer");

View File

@ -95,13 +95,6 @@ uint8_t a2dp_source_start_stream(uint16_t avdtp_cid, uint8_t local_seid);
*/
uint8_t a2dp_source_pause_stream(uint16_t avdtp_cid, uint8_t local_seid);
/**
* @brief Close stream
* @param avdtp_cid
* @param seid
*/
uint8_t a2dp_source_release_stream(uint16_t avdtp_cid, uint8_t local_seid);
/**
* @brief Disconnect from device with cid.
* @param avdtp_cid