-
Notifications
You must be signed in to change notification settings - Fork 8.3k
implement the a2dp source sample #69513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement the a2dp source sample #69513
Conversation
|
@MarkWangChinese I'll review once CI is green :) |
The sample implement the a2dp source that canconnect to one A2DP Bluetooth headset. This are shell commands to find and connect to Bluetooth headset in this sample, it plays one 1k sin media data after connection. Signed-off-by: Mark Wang <[email protected]>
Hi Emil, the current CI issues are: (1) undefined Kconfig macro. it cannot be found because the Kconfig is defined in #31583, and it is not merged; (2) |
If this PR depends on code that is not yet merged, then please revert it to a draft, as it is not ready for review |
|
This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time. |
|
Hello @MarkWangChinese! It's been quite a while since you contributed that excellent example to the Bluetooth samples. I've been trying to implement it on my M5 Stack Atom Echo, which has an ESP32-PICO-D4. However, I haven't had much success so far. I've noticed that the libraries have significantly changed since then. At this point, I can initialize the Bluetooth stack, discover nearby Bluetooth devices, connect to a Bluetooth device, and connect to the A2DP sink. However, I'm having trouble understanding the SBC streaming part. It seems like you're using a library that's not originally included in Zephyr: Additionally, I'm attempting to stream data to a Linux VM, but I'm losing the connection shortly after it's established. I'm not sure why this is happening. Could you offer any advice? Thank you! |
Hi @xG3nesis, Good to know that you are working on the a2dp too. The basic a2dp profile implementation is already merged by (#31583), but this draft pr is old and need updating because this example pr is implemented before #31583 is merged. And this example pr depend on SBC encoder/decoder request that is approve-pending (#67705). Without SBC encoder/decoder, the example can't work and I need to keep it as draft. |
|
Thank you for your prompt response @MarkWangChinese. I will be manually incorporating the work you completed on #70531 and making some modifications to successfully implement A2DP streaming. I also need to integrate your SBC repository (https://github.com/MarkWangChinese/libsbc/tree/main) into my Zephyr stack. Your efforts are greatly appreciated and have been incredibly helpful. Thank you again!😄 |
|
Hello @MarkWangChinese! Thanks to the example you wrote two years ago, I'm currently attempting to implement A2DP streaming on my ESP32 using the new In your old code, I noticed you implemented a structure called sbc_hdr to initialize the header of your SBC frame: static void a2dp_playback_timeout_handler(struct k_timer *timer) {
[...]
struct bt_a2dp_codec_sbc_media_packet_hdr *sbc_hdr;
[...]
buf = bt_a2dp_media_buf_alloc(NULL);
if (buf == NULL) {
return;
}
sbc_hdr = net_buf_add(buf, sizeof(struct bt_a2dp_codec_sbc_media_packet_hdr));
memset(sbc_hdr, 0, sizeof(struct bt_a2dp_codec_sbc_media_packet_hdr));
[...]
sbc_hdr->number_of_sbc_frames = frame_num;
[...]
}I'm unsure how to achieve the same functionality with the new a2dp_codec_sbc.h library. This is what my #include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/libsbc/sbc.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/classic/sdp.h>
#include <zephyr/bluetooth/classic/a2dp.h>
#include <zephyr/bluetooth/classic/rfcomm.h>
#include <zephyr/bluetooth/classic/a2dp_codec_sbc.h>
#include "app_connect.h"
#include "a2dp_media_48KHz_1ksin.h"
#define SDP_CLIENT_USER_BUF_LEN 512U
NET_BUF_POOL_FIXED_DEFINE(app_sdp_client_pool, CONFIG_BT_MAX_CONN,
SDP_CLIENT_USER_BUF_LEN, 8, NULL);
static uint8_t app_sdp_a2sink_user(struct bt_conn *conn, struct bt_sdp_client_result *result);
static void a2dp_playback_timeout_handler(struct k_timer *timer);
K_TIMER_DEFINE(a2dp_player_timer, a2dp_playback_timeout_handler, NULL);
#define APPL_A2DP_MTU (672U)
#define DEFAULT_BIT_RATE (328u)
static uint32_t a2dp_src_sf;
static int64_t ref_time;
static uint32_t a2dp_src_missed_count;
static volatile uint8_t a2dp_src_playback;
static int tone_index;
uint8_t a2dp_src_nc;
#define A2DP_SBC_BLOCK_MAX (512U)
uint32_t audio_time_interval; /* ms */
uint32_t audio_frame_sample_count;
uint8_t a2dp_pcm_buffer[1920u]; /* 10ms max packet pcm data size. the max is 480 * 2 * 2 */
uint8_t a2dp_sbc_encode_buffer_frame[A2DP_SBC_BLOCK_MAX];
struct sbc_encoder encoder;
uint32_t send_samples_count;
uint16_t send_count;
#define A2DP_SRC_PERIOD_MS 10
struct bt_a2dp *default_a2dp;
struct bt_a2dp_ep *default_a2dp_endpoint;
struct bt_a2dp_stream *bt_a2dp_stream_handle;
BT_A2DP_SBC_SOURCE_EP_DEFAULT(a2dp_source_ep);
static void stream_configured(struct bt_a2dp_stream *stream) {
printk("Streaming successfully configurated!");
}
static void stream_etablished(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully etablished!");
}
static void stream_released(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully released!");
}
static void stream_started(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully started");
}
struct bt_a2dp_stream_ops stream_ops = {
.configured = stream_configured,
.established = stream_etablished,
.released = stream_released,
.started = stream_started,
};
static uint8_t *a2dp_pl_produce_media(uint32_t a2dp_src_num_samples)
{
uint8_t *media = NULL;
uint16_t medialen;
/* Music Audio is Stereo */
medialen = (a2dp_src_num_samples * a2dp_src_nc * 2);
/* For mono or dual configuration, skip alternative samples */
if (1 == a2dp_src_nc) {
uint16_t index;
media = (uint8_t *)&a2dp_pcm_buffer[0];
for (index = 0; index < a2dp_src_num_samples; index++) {
media[(2 * index)] = *((uint8_t *)beethoven + tone_index);
media[(2 * index) + 1] = *((uint8_t *)beethoven + tone_index + 1);
/* Update the tone index */
tone_index += 4u;
if (tone_index >= sizeof(beethoven)) {
tone_index = 0U;
}
}
} else {
if ((tone_index + (a2dp_src_num_samples << 2)) > sizeof(beethoven)) {
media = (uint8_t *)&a2dp_pcm_buffer[0];
memcpy(media, ((uint8_t *)beethoven + tone_index),
sizeof(beethoven) - tone_index);
memcpy(&media[sizeof(beethoven) - tone_index],
((uint8_t *)beethoven),
((a2dp_src_num_samples << 2) - (sizeof(beethoven) - tone_index)));
/* Update the tone index */
tone_index = ((a2dp_src_num_samples << 2) -
(sizeof(beethoven) - tone_index));
} else {
media = ((uint8_t *)beethoven + tone_index);
/* Update the tone index */
tone_index += (a2dp_src_num_samples << 2);
if (tone_index >= sizeof(beethoven)) {
tone_index = 0U;
}
}
}
return media;
}
static void a2dp_playback_timeout_handler(struct k_timer *timer)
{
int64_t period_ms;
uint32_t a2dp_src_num_samples;
uint8_t *pcm_data;
uint8_t index;
uint32_t pcm_frame_size;
uint32_t pcm_frame_samples;
uint32_t encoded_frame_size;
uint8_t *net_buffer;
struct net_buf *buf;
uint32_t sample_count = 0;
uint8_t frame_num = 0;
int ret;
// struct bt_a2dp_codec_sbc_media_packet_hdr *sbc_hdr;
uint8_t *sbc_hdr;
int err;
/* If stopped then return */
if (0U == a2dp_src_playback) {
return;
}
period_ms = k_uptime_delta(&ref_time);
NET_BUF_POOL_DEFINE(pool_buf, 0, 1000, 0, NULL);
buf = net_buf_alloc(&pool_buf, K_NO_WAIT);
if (buf == NULL) {
return;
}
sbc_hdr = net_buf_add(buf, sizeof(uint8_t));
/* Get the number of samples */
a2dp_src_num_samples = (uint16_t)((period_ms * a2dp_src_sf) / 1000);
a2dp_src_missed_count += (uint32_t)((period_ms * a2dp_src_sf) % 1000);
/* Raw adjust for the drift */
while (a2dp_src_missed_count >= (1000 * audio_frame_sample_count)) {
a2dp_src_num_samples += audio_frame_sample_count;
a2dp_src_missed_count -= (1000 * audio_frame_sample_count);
}
pcm_data = a2dp_pl_produce_media(a2dp_src_num_samples);
if (pcm_data == NULL) {
return;
}
pcm_frame_size = sbc_frame_bytes(&encoder);
pcm_frame_samples = sbc_frame_samples(&encoder);
encoded_frame_size = sbc_frame_encoded_bytes(&encoder);
for (index = 0; index < (a2dp_src_num_samples / audio_frame_sample_count); index++) {
net_buffer = net_buf_tail(buf);
if (buf->len + encoded_frame_size > bt_a2dp_get_mtu(bt_a2dp_stream_handle)) {
printk("mtu error");
return;
}
err = sbc_encode(&encoder,
(uint8_t *)&pcm_data[index * pcm_frame_size],
encoded_frame_size, &net_buffer[0]);
if (err) {
printk("sbc encode fail");
continue;
}
buf->len += encoded_frame_size;
sample_count += pcm_frame_samples;
frame_num++;
}
// BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_SET(sbc_hdr, frame_num);
*sbc_hdr = BT_A2DP_SBC_MEDIA_HDR_ENCODE(frame_num, 0, 0, 0);
ret = bt_a2dp_stream_send(bt_a2dp_stream_handle, buf, send_count, send_samples_count);
if (ret < 0) {
printk("Failed to send SBC audio data on streams(%d)\n", ret);
net_buf_unref(buf);
} else {
send_count++;
send_samples_count += sample_count;
}
}
static void music_control_a2dp_start(void)
{
/* Start Audio Source */
a2dp_src_playback = 1U;
audio_frame_sample_count = sbc_frame_samples(&encoder);
/* calculate the interval that contains multiple of frame. default is 10ms */
audio_time_interval = ((10 * a2dp_src_sf / 1000) / audio_frame_sample_count);
audio_time_interval = audio_time_interval * audio_frame_sample_count * 1000 / a2dp_src_sf;
k_uptime_delta(&ref_time);
k_timer_start(&a2dp_player_timer, K_MSEC(audio_time_interval), K_MSEC(audio_time_interval));
}
// void app_endpoint_configured(struct bt_a2dp_endpoint_configure_result *result)
// {
// if (result->err == 0) {
// default_a2dp_endpoint = &sbc_endpoint;
// struct bt_a2dp_codec_sbc_params *config =
// (struct bt_a2dp_codec_sbc_params *)
// &result->config.media_config->codec_ie[0];
// a2dp_src_sf = bt_a2dp_sbc_get_sampling_frequency(config);
// a2dp_src_nc = bt_a2dp_sbc_get_channel_num(config);
// sbc_setup_encoder(&encoder, a2dp_src_sf, bt_a2dp_sbc_get_channel_mode(config),
// bt_a2dp_sbc_get_block_length(config), bt_a2dp_sbc_get_subband_num(config),
// bt_a2dp_sbc_get_allocation_method(config), DEFAULT_BIT_RATE);
// bt_a2dp_start(default_a2dp_endpoint);
// printk("a2dp start playing\r\n");
// }
// }
static struct bt_sdp_discover_params discov_a2dp_sink = {
.uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SINK_SVCLASS),
.func = app_sdp_a2sink_user,
.pool = &app_sdp_client_pool,
};
static uint8_t app_sdp_a2sink_user(struct bt_conn *conn,
struct bt_sdp_client_result *result)
{
uint16_t param;
int res;
if ((result) && (result->resp_buf)) {
printk("sdp success callback\r\n");
res = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_L2CAP, ¶m);
if (res < 0) {
printk("PSM is not found\r\n");
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
if (param == BT_UUID_AVDTP_VAL) {
printk("A2DP Service found. Connecting ...\n");
default_a2dp = bt_a2dp_connect(default_conn);
if (default_a2dp == NULL) {
printk("fail to connect a2dp\r\n");
}
return BT_SDP_DISCOVER_UUID_STOP;
}
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
printk("sdp fail callback\r\n");
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
void app_sdp_discover_a2dp_sink(void)
{
int res;
res = bt_sdp_discover(default_conn, &discov_a2dp_sink);
if (res) {
printk("SDP discovery failed: result\r\n");
} else {
printk("SDP discovery started\r\n");
}
}
static void a2dp_connected (struct bt_a2dp *a2dp, int err) {
if(!err) {
printk("[i] Successfully connected to A2DP profile!\n");
struct bt_a2dp_stream bt_a2dp_stream_handle = {
.local_ep = &a2dp_source_ep,
.remote_ep = default_a2dp_endpoint,
.remote_ep_id = 0,
.ops = &stream_ops,
.a2dp = default_a2dp,
.codec_config = bt_a2dp_ep_cap_iea2dp_source_ep
};
int ret;
ret = bt_a2dp_stream_establish(&bt_a2dp_stream_handle);
if(ret){
printk("Unable to send AVDTP_OPEN command");
} else {
music_control_a2dp_start();
}
} else {
if (default_a2dp != NULL) {
default_a2dp = NULL;
}
printk("[E] Unable to connect to A2DP profile (Error: %d).\n", err);
}
}
static void a2dp_disconnected(struct bt_a2dp *a2dp) {
int ret;
printk("[i] Device disconnected from A2DP profile!\n");
a2dp_src_playback = 0U;
/* stop timer */
k_timer_stop(&a2dp_player_timer);
printk("a2dp disconnected\r\n");
ret = bt_a2dp_stream_release(bt_a2dp_stream_handle);
if(ret){
printk("Unable to send AVDTP_OPEN command");
}
}
static struct bt_a2dp_cb bt_a2dp_callbacks = {
.connected = a2dp_connected,
.disconnected = a2dp_disconnected,
};
static void app_a2dp_init(void) {
bt_a2dp_register_ep(&a2dp_source_ep, BT_AVDTP_AUDIO, BT_AVDTP_SOURCE);
bt_a2dp_register_cb(&bt_a2dp_callbacks);
}
static void bt_ready(int err)
{
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
app_connect_init();
app_a2dp_init();
}
int main(void)
{
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
}
return err;
} |
|
To keep you updated @MarkWangChinese, I'm building my path step by step through the different libraries. I'm able to compile my code but this is the error that i'm running into after connecting to my device. Down below, you'll find my updated #include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/libsbc/sbc.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/classic/sdp.h>
#include <zephyr/bluetooth/classic/a2dp.h>
#include <zephyr/bluetooth/classic/rfcomm.h>
#include <zephyr/bluetooth/classic/a2dp_codec_sbc.h>
#include "app_connect.h"
#include "a2dp_media_48KHz_1ksin.h"
#define SDP_CLIENT_USER_BUF_LEN 512U
NET_BUF_POOL_FIXED_DEFINE(app_sdp_client_pool, CONFIG_BT_MAX_CONN,
SDP_CLIENT_USER_BUF_LEN, 8, NULL);
static uint8_t app_sdp_a2sink_user(struct bt_conn *conn, struct bt_sdp_client_result *result);
static void a2dp_playback_timeout_handler(struct k_timer *timer);
K_TIMER_DEFINE(a2dp_player_timer, a2dp_playback_timeout_handler, NULL);
#define APPL_A2DP_MTU (672U)
#define DEFAULT_BIT_RATE (328u)
static uint32_t a2dp_src_sf;
static int64_t ref_time;
static uint32_t a2dp_src_missed_count;
static volatile uint8_t a2dp_src_playback;
static int tone_index;
uint8_t a2dp_src_nc;
#define A2DP_SBC_BLOCK_MAX (512U)
uint32_t audio_time_interval; /* ms */
uint32_t audio_frame_sample_count;
uint8_t a2dp_pcm_buffer[1920u]; /* 10ms max packet pcm data size. the max is 480 * 2 * 2 */
uint8_t a2dp_sbc_encode_buffer_frame[A2DP_SBC_BLOCK_MAX];
struct sbc_encoder encoder;
uint32_t send_samples_count;
uint16_t send_count;
#define A2DP_SRC_PERIOD_MS 10
struct bt_a2dp *source_a2dp;
struct bt_a2dp_ep *remote_a2dp_endpoint;
struct bt_a2dp_stream *bt_a2dp_stream_handle;
BT_A2DP_SBC_SOURCE_EP_DEFAULT(a2dp_source_ep);
static void stream_configured(struct bt_a2dp_stream *stream) {
printk("Streaming successfully configurated!");
}
static void stream_etablished(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully etablished!");
}
static void stream_released(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully released!");
}
static void stream_started(struct bt_a2dp_stream *stream) {
printk("Streaming pipe successfully started");
}
struct bt_a2dp_stream_ops stream_ops = {
.configured = stream_configured,
.established = stream_etablished,
.released = stream_released,
.started = stream_started,
};
static uint8_t *a2dp_pl_produce_media(uint32_t a2dp_src_num_samples)
{
uint8_t *media = NULL;
uint16_t medialen;
/* Music Audio is Stereo */
medialen = (a2dp_src_num_samples * a2dp_src_nc * 2);
/* For mono or dual configuration, skip alternative samples */
if (1 == a2dp_src_nc) {
uint16_t index;
media = (uint8_t *)&a2dp_pcm_buffer[0];
for (index = 0; index < a2dp_src_num_samples; index++) {
media[(2 * index)] = *((uint8_t *)beethoven + tone_index);
media[(2 * index) + 1] = *((uint8_t *)beethoven + tone_index + 1);
/* Update the tone index */
tone_index += 4u;
if (tone_index >= sizeof(beethoven)) {
tone_index = 0U;
}
}
} else {
if ((tone_index + (a2dp_src_num_samples << 2)) > sizeof(beethoven)) {
media = (uint8_t *)&a2dp_pcm_buffer[0];
memcpy(media, ((uint8_t *)beethoven + tone_index),
sizeof(beethoven) - tone_index);
memcpy(&media[sizeof(beethoven) - tone_index],
((uint8_t *)beethoven),
((a2dp_src_num_samples << 2) - (sizeof(beethoven) - tone_index)));
/* Update the tone index */
tone_index = ((a2dp_src_num_samples << 2) -
(sizeof(beethoven) - tone_index));
} else {
media = ((uint8_t *)beethoven + tone_index);
/* Update the tone index */
tone_index += (a2dp_src_num_samples << 2);
if (tone_index >= sizeof(beethoven)) {
tone_index = 0U;
}
}
}
return media;
}
static void a2dp_playback_timeout_handler(struct k_timer *timer)
{
int64_t period_ms;
uint32_t a2dp_src_num_samples;
uint8_t *pcm_data;
uint8_t index;
uint32_t pcm_frame_size;
uint32_t pcm_frame_samples;
uint32_t encoded_frame_size;
uint8_t *net_buffer;
struct net_buf *buf;
uint32_t sample_count = 0;
uint8_t frame_num = 0;
int ret;
// struct bt_a2dp_codec_sbc_media_packet_hdr *sbc_hdr;
uint8_t *sbc_hdr;
int err;
/* If stopped then return */
if (0U == a2dp_src_playback) {
return;
}
period_ms = k_uptime_delta(&ref_time);
NET_BUF_POOL_DEFINE(pool_buf, 0, 1000, 0, NULL);
buf = net_buf_alloc(&pool_buf, K_NO_WAIT);
if (buf == NULL) {
return;
}
sbc_hdr = net_buf_add(buf, sizeof(uint8_t));
/* Get the number of samples */
a2dp_src_num_samples = (uint16_t)((period_ms * a2dp_src_sf) / 1000);
a2dp_src_missed_count += (uint32_t)((period_ms * a2dp_src_sf) % 1000);
/* Raw adjust for the drift */
while (a2dp_src_missed_count >= (1000 * audio_frame_sample_count)) {
a2dp_src_num_samples += audio_frame_sample_count;
a2dp_src_missed_count -= (1000 * audio_frame_sample_count);
}
pcm_data = a2dp_pl_produce_media(a2dp_src_num_samples);
if (pcm_data == NULL) {
return;
}
pcm_frame_size = sbc_frame_bytes(&encoder);
pcm_frame_samples = sbc_frame_samples(&encoder);
encoded_frame_size = sbc_frame_encoded_bytes(&encoder);
for (index = 0; index < (a2dp_src_num_samples / audio_frame_sample_count); index++) {
net_buffer = net_buf_tail(buf);
if (buf->len + encoded_frame_size > bt_a2dp_get_mtu(bt_a2dp_stream_handle)) {
printk("mtu error");
return;
}
err = sbc_encode(&encoder,
(uint8_t *)&pcm_data[index * pcm_frame_size],
encoded_frame_size, &net_buffer[0]);
if (err) {
printk("sbc encode fail");
continue;
}
buf->len += encoded_frame_size;
sample_count += pcm_frame_samples;
frame_num++;
}
// BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_SET(sbc_hdr, frame_num);
*sbc_hdr = BT_A2DP_SBC_MEDIA_HDR_ENCODE(frame_num, 0, 0, 0);
ret = bt_a2dp_stream_send(bt_a2dp_stream_handle, buf, send_count, send_samples_count);
if (ret < 0) {
printk("Failed to send SBC audio data on streams(%d)\n", ret);
net_buf_unref(buf);
} else {
send_count++;
send_samples_count += sample_count;
}
}
static void music_control_a2dp_start(void)
{
/* Start Audio Source */
a2dp_src_playback = 1U;
audio_frame_sample_count = sbc_frame_samples(&encoder);
/* calculate the interval that contains multiple of frame. default is 10ms */
audio_time_interval = ((10 * a2dp_src_sf / 1000) / audio_frame_sample_count);
audio_time_interval = audio_time_interval * audio_frame_sample_count * 1000 / a2dp_src_sf;
k_uptime_delta(&ref_time);
k_timer_start(&a2dp_player_timer, K_MSEC(audio_time_interval), K_MSEC(audio_time_interval));
}
static struct bt_sdp_discover_params discov_a2dp_sink = {
.uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SINK_SVCLASS),
.func = app_sdp_a2sink_user,
.pool = &app_sdp_client_pool,
};
static uint8_t app_sdp_a2sink_user(struct bt_conn *conn,
struct bt_sdp_client_result *result)
{
uint16_t param;
int res;
if ((result) && (result->resp_buf)) {
printk("sdp success callback\r\n");
res = bt_sdp_get_proto_param(result->resp_buf, BT_SDP_PROTO_L2CAP, ¶m);
if (res < 0) {
printk("PSM is not found\r\n");
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
if (param == BT_UUID_AVDTP_VAL) {
printk("A2DP Service found. Connecting ...\n");
source_a2dp = bt_a2dp_connect(default_conn);
if (source_a2dp == NULL) {
printk("fail to connect a2dp\r\n");
}
return BT_SDP_DISCOVER_UUID_STOP;
}
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
printk("sdp fail callback\r\n");
return BT_SDP_DISCOVER_UUID_CONTINUE;
}
void app_sdp_discover_a2dp_sink(void)
{
int res;
res = bt_sdp_discover(default_conn, &discov_a2dp_sink);
if (res) {
printk("SDP discovery failed: result\r\n");
} else {
printk("SDP discovery started\r\n");
}
}
static void a2dp_connected(struct bt_a2dp *a2dp, int err) {
if(!err) {
printk("[i] Successfully connected to A2DP profile!\n");
struct bt_a2dp_stream bt_a2dp_stream_handle = {
.local_ep = &a2dp_source_ep,
.remote_ep = remote_a2dp_endpoint,
.remote_ep_id = 0,
.ops = &stream_ops,
.a2dp = source_a2dp,
.codec_config = bt_a2dp_ep_cap_iea2dp_source_ep
};
int ret;
ret = bt_a2dp_stream_establish(&bt_a2dp_stream_handle);
if(ret){
printk("Unable to send AVDTP_OPEN command");
} else {
music_control_a2dp_start();
}
} else {
if (source_a2dp != NULL) {
source_a2dp = NULL;
}
printk("[E] Unable to connect to A2DP profile (Error: %d).\n", err);
}
}
static void a2dp_disconnected(struct bt_a2dp *a2dp) {
int ret;
printk("[i] Device disconnected from A2DP profile!\n");
a2dp_src_playback = 0U;
/* stop timer */
k_timer_stop(&a2dp_player_timer);
printk("a2dp disconnected\r\n");
ret = bt_a2dp_stream_release(bt_a2dp_stream_handle);
if(ret){
printk("Unable to send AVDTP_OPEN command");
}
}
static int a2dp_configuration(struct bt_a2dp *a2dp, struct bt_a2dp_ep *ep, struct bt_a2dp_codec_cfg *codec_cfg, struct bt_a2dp_stream **stream, uint8_t *rsp_err_code) {
struct bt_a2dp_codec_sbc_params *config = (struct bt_a2dp_codec_sbc_params *) &codec_cfg->codec_config->codec_ie[0];
a2dp_src_sf = bt_a2dp_sbc_get_sampling_frequency(config);
a2dp_src_nc = bt_a2dp_sbc_get_channel_num(config);
sbc_setup_encoder(&encoder, a2dp_src_sf, BT_A2DP_SBC_CHAN_MODE(config), BT_A2DP_SBC_BLK_LEN(config), BT_A2DP_SBC_SUB_BAND(config), BT_A2DP_SBC_ALLOC_MTHD(config), DEFAULT_BIT_RATE);
bt_a2dp_stream_start(bt_a2dp_stream_handle);
printk("a2dp start playing\r\n");
}
static struct bt_a2dp_cb bt_a2dp_callbacks = {
.connected = a2dp_connected,
.disconnected = a2dp_disconnected,
.config_req = a2dp_configuration,
};
static void app_a2dp_init(void) {
bt_a2dp_register_ep(&a2dp_source_ep, BT_AVDTP_AUDIO, BT_AVDTP_SOURCE);
bt_a2dp_register_cb(&bt_a2dp_callbacks);
}
static void bt_ready(int err)
{
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
app_connect_init();
app_a2dp_init();
}
int main(void)
{
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
}
return err;
}If you could bring some assistance, I would really appreciate it ! |
|
Hi @xG3nesis , I create another draft pr #76102 because this draft pr is closed and https://github.com/nxp-zephyr/zephyr is not suggested to use now in NXP. You can reference the new draft pr (#76102), I think you need to resolve some build errors in your side, but the function works in my side based on (#76101) |
|
Hi @MarkWangChinese ! |
|
Hi @MarkWangChinese, Everything is compiling fine now, thank you! However, I'm encountering an issue while trying to connect my device to my Bluetooth Ubuntu VM, which has an A2DP sink endpoint. It seems that the endpoint with the SBC codec is not being detected. How do you typically test your device in your setup? |



The sample is based on PR #31583
The sample implement the a2dp source that can connect to one A2DP Bluetooth headset.
This is one inputting shell commands to find and connect to Bluetooth headset, it plays one 1k sin media data after connection.