a2dp_source: suppport stream reconfiguration (a2dp_source_reconfigure_stream_sampling_frequency)

This commit is contained in:
Matthias Ringwald 2018-09-17 12:06:51 +02:00
parent 6da84376d1
commit cfd2437ec6
4 changed files with 115 additions and 18 deletions

View File

@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- GATT Client: stop timer on disconnect - fixes use after free / crash
- L2CAP: Use valid signaling identifier for L2CAP Connection Parameter Update Request
### Added
- A2DP Source: Support stream reconfiguration (a2dp_source_reconfigure_stream_sampling_frequency)
## Changes August 2018
### Added

View File

@ -213,9 +213,9 @@ typedef struct {
static const char title[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
avrcp_track_t tracks[] = {
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, 1, "Sine", "Generated", "AVRCP Demo", "monotone", 12345},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, 2, "Nao-deceased", "Decease", "AVRCP Demo", "vivid", 12345},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, 3, (char *)title, "Decease", "AVRCP Demo", "vivid", 12345},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, 1, "Sine", "Generated", "A2DP Source Demo", "monotone", 12345},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, 2, "Nao-deceased", "Decease", "A2DP Source Demo", "vivid", 12345},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, 3, (char *)title, "Decease", "A2DP Source Demo", "vivid", 12345},
};
int current_track_index;
avrcp_play_status_info_t play_info;
@ -432,7 +432,7 @@ static void a2dp_source_packet_handler(uint8_t packet_type, uint16_t channel, ui
#ifndef HAVE_BTSTACK_STDIN
if (hci_event_packet_get_type(packet) == BTSTACK_EVENT_STATE){
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
printf("Create AVDTP Source connection to addr %s.\n", bd_addr_to_str(device_addr));
printf("Create A2DP Source connection to addr %s.\n", bd_addr_to_str(device_addr));
status = a2dp_source_establish_stream(device_addr, media_tracker.local_seid, &media_tracker.a2dp_cid);
if (status != ERROR_CODE_SUCCESS){
printf("Could not perform command, status 0x%2x\n", status);
@ -448,7 +448,10 @@ static void a2dp_source_packet_handler(uint8_t packet_type, uint16_t channel, ui
}
if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) return;
switch (packet[2]){
printf("A2DP Meta %x\n", hci_event_a2dp_meta_get_subevent_code(packet));
switch (hci_event_a2dp_meta_get_subevent_code(packet)){
case A2DP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
a2dp_subevent_signaling_connection_established_get_bd_addr(packet, address);
cid = a2dp_subevent_signaling_connection_established_get_a2dp_cid(packet);
@ -464,7 +467,6 @@ static void a2dp_source_packet_handler(uint8_t packet_type, uint16_t channel, ui
break;
case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:{
printf("A2DP Source: Received SBC codec configuration.\n");
sbc_configuration.reconfigure = a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet);
sbc_configuration.num_channels = a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet);
sbc_configuration.sampling_frequency = a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet);
@ -475,17 +477,13 @@ static void a2dp_source_packet_handler(uint8_t packet_type, uint16_t channel, ui
sbc_configuration.min_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(packet);
sbc_configuration.max_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet);
sbc_configuration.frames_per_buffer = sbc_configuration.subbands * sbc_configuration.block_length;
printf("A2DP Source: Received SBC codec configuration, sampling frequency %u.\n", sbc_configuration.sampling_frequency);
btstack_sbc_encoder_init(&sbc_encoder_state, SBC_MODE_STANDARD,
sbc_configuration.block_length, sbc_configuration.subbands,
sbc_configuration.allocation_method, sbc_configuration.sampling_frequency,
sbc_configuration.max_bitpool_value,
sbc_configuration.channel_mode);
// status = a2dp_source_establish_stream(device_addr, media_tracker.local_seid, &media_tracker.a2dp_cid);
// if (status != ERROR_CODE_SUCCESS){
// printf("Could not perform command, status 0x%2x\n", status);
// }
break;
}
@ -503,12 +501,16 @@ static void a2dp_source_packet_handler(uint8_t packet_type, uint16_t channel, ui
}
printf("A2DP Source: Stream established, address %s, a2dp cid 0x%02x, local seid %d, remote seid %d.\n", bd_addr_to_str(address),
media_tracker.a2dp_cid, media_tracker.local_seid, a2dp_subevent_stream_established_get_remote_seid(packet));
printf("A2DP Source: Start playing mod, a2dp cid 0x%02x.\n", media_tracker.a2dp_cid);
media_tracker.stream_opened = 1;
data_source = STREAM_MOD;
status = a2dp_source_start_stream(media_tracker.a2dp_cid, media_tracker.local_seid);
break;
case A2DP_SUBEVENT_STREAM_RECONFIGURED:
status = a2dp_subevent_stream_reconfigured_get_status(packet);
printf("A2DP Source: Reconfigured, status 0x%02x\n", status);
break;
case A2DP_SUBEVENT_STREAM_STARTED:
play_info.status = AVRCP_PLAYBACK_STATUS_PLAYING;
if (media_tracker.avrcp_cid){
@ -643,8 +645,8 @@ static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth A2DP Source/AVRCP Target Demo %s ---\n", bd_addr_to_str(iut_address));
printf("b - AVDTP Source create connection to addr %s\n", device_addr_string);
printf("B - AVDTP Source disconnect\n");
printf("b - A2DP Source create connection to addr %s\n", device_addr_string);
printf("B - A2DP Source disconnect\n");
printf("c - AVRCP Target create connection to addr %s\n", device_addr_string);
printf("C - AVRCP Target disconnect\n");
@ -653,6 +655,8 @@ static void show_usage(void){
printf("z - start streaming '%s'\n", mod_name);
}
printf("p - pause streaming\n");
printf("w - reconfigure stream for 44100 Hz\n");
printf("e - reconfigure stream for 48000 Hz\n");
printf("\n--- Bluetooth AVRCP Target Commands %s ---\n", bd_addr_to_str(iut_address));
printf("---\n");
@ -663,10 +667,10 @@ static void stdin_process(char cmd){
switch (cmd){
case 'b':
status = a2dp_source_establish_stream(device_addr, media_tracker.local_seid, &media_tracker.a2dp_cid);
printf("%c - Create AVDTP Source connection to addr %s, cid 0x%02x.\n", cmd, bd_addr_to_str(device_addr), media_tracker.a2dp_cid);
printf("%c - Create A2DP Source connection to addr %s, cid 0x%02x.\n", cmd, bd_addr_to_str(device_addr), media_tracker.a2dp_cid);
break;
case 'B':
printf("%c - AVDTP Source Disconnect from cid 0x%2x\n", cmd, media_tracker.a2dp_cid);
printf("%c - A2DP Source Disconnect from cid 0x%2x\n", cmd, media_tracker.a2dp_cid);
status = a2dp_source_disconnect(media_tracker.a2dp_cid);
break;
case 'c':
@ -707,6 +711,26 @@ static void stdin_process(char cmd){
status = a2dp_source_pause_stream(media_tracker.a2dp_cid, media_tracker.local_seid);
break;
case 'w':
if (!media_tracker.stream_opened) break;
if (play_info.status == AVRCP_PLAYBACK_STATUS_PLAYING){
printf("Stream cannot be reconfigured while playing, please pause stream first\n");
break;
}
printf("%c - Reconfigure for 44100 Hz.\n", cmd);
status = a2dp_source_reconfigure_stream_sampling_frequency(media_tracker.a2dp_cid, 44100);
break;
case 'e':
if (!media_tracker.stream_opened) break;
if (play_info.status == AVRCP_PLAYBACK_STATUS_PLAYING){
printf("Stream cannot be reconfigured while playing, please pause stream first\n");
break;
}
printf("%c - Reconfigure for 48000 Hz.\n", cmd);
status = a2dp_source_reconfigure_stream_sampling_frequency(media_tracker.a2dp_cid, 48000);
break;
default:
show_usage();
return;

View File

@ -39,7 +39,6 @@
#define __BTSTACK_FILE__ "a2dp_source.c"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -200,6 +199,20 @@ static void a2dp_signaling_emit_control_command(btstack_packet_handler_t callbac
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void a2dp_signaling_emit_reconfigured(btstack_packet_handler_t callback, uint16_t cid, uint8_t local_seid, uint8_t status){
if (!callback) return;
uint8_t event[7];
int pos = 0;
event[pos++] = HCI_EVENT_A2DP_META;
event[pos++] = sizeof(event) - 2;
event[pos++] = A2DP_SUBEVENT_STREAM_RECONFIGURED;
little_endian_store_16(event, pos, cid);
pos += 2;
event[pos++] = local_seid;
event[pos++] = status;
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
UNUSED(size);
@ -365,6 +378,11 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
avdtp_source_set_configuration(cid, avdtp_stream_endpoint_seid(sc.local_stream_endpoint), sc.active_remote_sep->seid, sc.local_stream_endpoint->remote_configuration_bitmap, sc.local_stream_endpoint->remote_configuration);
break;
}
case A2DP_W2_RECONFIGURE_WITH_SEID:
log_info("A2DP reconfigured");
a2dp_signaling_emit_reconfigured(a2dp_source_context.a2dp_callback, cid, avdtp_stream_endpoint_seid(sc.local_stream_endpoint), 0);
app_state = A2DP_STREAMING_OPENED;
break;
case A2DP_W2_OPEN_STREAM_WITH_SEID:{
log_info("A2DP open stream ");
app_state = A2DP_W4_OPEN_STREAM_WITH_SEID;
@ -474,6 +492,51 @@ uint8_t a2dp_source_disconnect(uint16_t a2dp_cid){
return avdtp_disconnect(a2dp_cid, &a2dp_source_context);
}
uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t a2dp_cid, uint32_t sampling_frequency){
// UNUSED(sampling_frequency);
log_info("a2dp_source_reconfigure_stream");
memcpy(sc.local_stream_endpoint->reconfigure_media_codec_sbc_info, sc.local_stream_endpoint->remote_sep.configuration.media_codec.media_codec_information, 4);
// update sampling frequency
uint8_t config = sc.local_stream_endpoint->reconfigure_media_codec_sbc_info[0] & 0x0f;
switch (sampling_frequency){
case 48000:
config |= (AVDTP_SBC_48000 << 4);
break;
case 44100:
config |= (AVDTP_SBC_44100 << 4);
break;
case 32000:
config |= (AVDTP_SBC_32000 << 4);
break;
case 16000:
config |= (AVDTP_SBC_16000 << 4);
break;
default:
log_error("Unsupported sampling frequency %u", sampling_frequency);
return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
sc.local_stream_endpoint->reconfigure_media_codec_sbc_info[0] = config;
avdtp_capabilities_t new_configuration;
new_configuration.media_codec.media_type = AVDTP_AUDIO;
new_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC;
new_configuration.media_codec.media_codec_information_len = 4;
new_configuration.media_codec.media_codec_information = sc.local_stream_endpoint->reconfigure_media_codec_sbc_info;
// sttart reconfigure
app_state = A2DP_W2_RECONFIGURE_WITH_SEID;
return avdtp_source_reconfigure(
a2dp_cid,
avdtp_stream_endpoint_seid(sc.local_stream_endpoint),
sc.active_remote_sep->seid,
1 << AVDTP_MEDIA_CODEC,
new_configuration
);
}
uint8_t a2dp_source_start_stream(uint16_t a2dp_cid, uint8_t local_seid){
return avdtp_start_stream(a2dp_cid, local_seid, &a2dp_source_context);
}

View File

@ -47,7 +47,7 @@
#define __A2DP_SOURCE_H
#include <stdint.h>
#include "avdtp.h"
#include "classic/avdtp.h"
#if defined __cplusplus
extern "C" {
@ -109,6 +109,13 @@ void a2dp_source_register_packet_handler(btstack_packet_handler_t callback);
*/
uint8_t a2dp_source_establish_stream(bd_addr_t remote, uint8_t local_seid, uint16_t * out_a2dp_cid);
/**
* @brief Reconfigure stream.
* @param local_seid ID assigned to a local stream endpoint
* @param sampling_frequency New sampling frequency to use. Cannot be called while stream is active
*/
uint8_t a2dp_source_reconfigure_stream_sampling_frequency(uint16_t a2dp_cid, uint32_t sampling_frequency);
/**
* @brief Start stream.
* @param a2dp_cid A2DP channel identifyer.