A2DP
This manual describes the Advanced Audio Distribution Profile API definition. A2DP defines the protocols and procedures that realize distribution of audio content of high-quality in mono or stereo on ACL channels.
Terminology and Concepts
A2DP Roles
The following roles are defined for devices that implement this profile:
A2DP SDP Record
A2DP local record a2dp_sdp_record should be registered by bt_sdp_record_add()
.
static const uint8_t a2dp_sdp_record[] =
{
SDP_DATA_ELEM_SEQ_HDR,
0x39,//0x55,
//attribute SDP_ATTR_SRV_CLASS_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
(uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_AUDIO_SINK >> 8),
(uint8_t)(UUID_AUDIO_SINK),
//attribute SDP_ATTR_PROTO_DESC_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
(uint8_t)SDP_ATTR_PROTO_DESC_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x10,
SDP_DATA_ELEM_SEQ_HDR,
0x06,
SDP_UUID16_HDR,
(uint8_t)(UUID_L2CAP >> 8),
(uint8_t)(UUID_L2CAP),
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(PSM_AVDTP >> 8),
(uint8_t)(PSM_AVDTP),
SDP_DATA_ELEM_SEQ_HDR,
0x06,
SDP_UUID16_HDR,
(uint8_t)(UUID_AVDTP >> 8),
(uint8_t)(UUID_AVDTP),
SDP_UNSIGNED_TWO_BYTE,
0x01,
0x03,
//attribute SDP_ATTR_BROWSE_GROUP_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
(uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x03,
SDP_UUID16_HDR,
(uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
(uint8_t)(UUID_PUBLIC_BROWSE_GROUP),
/*
//attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
(uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x09,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_LANG_ENGLISH >> 8),
(uint8_t)SDP_LANG_ENGLISH,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_CHARACTER_UTF8 >> 8),
(uint8_t)SDP_CHARACTER_UTF8,
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
(uint8_t)SDP_BASE_LANG_OFFSET,
*/
//attribute SDP_ATTR_PROFILE_DESC_LIST
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
(uint8_t)SDP_ATTR_PROFILE_DESC_LIST,
SDP_DATA_ELEM_SEQ_HDR,
0x08,
SDP_DATA_ELEM_SEQ_HDR,
0x06,
SDP_UUID16_HDR,
(uint8_t)(UUID_ADVANCED_AUDIO_DISTRIBUTION >> 8),
(uint8_t)(UUID_ADVANCED_AUDIO_DISTRIBUTION),
SDP_UNSIGNED_TWO_BYTE,
0x01,//version 1.4
0x04,
//attribute SDP_ATTR_SUPPORTED_FEATURES
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)(SDP_ATTR_SUPPORTED_FEATURES >> 8),
(uint8_t)SDP_ATTR_SUPPORTED_FEATURES,
SDP_UNSIGNED_TWO_BYTE,
0x00,
0x03
/*
//attribute SDP_ATTR_SRV_NAME
SDP_UNSIGNED_TWO_BYTE,
(uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
(uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
SDP_STRING_HDR,
0x09,
0x61, 0x32, 0x64, 0x70, 0x5f, 0x73, 0x69, 0x6e, 0x6b //a2dp_sink
*/
};
Feature Set
The functions provided by A2DP APIs are as below.
Connecting and Disconnecting: Including functions to connect/disconnect A2DP.
Streaming: Including functions to send stream data, etc.
Provided APIs
This figure shows the relationship between applications and A2DP and Bluetooth Protocol Stack. Above the horizon line is developed by the applications. Below the horizon line is developed by Realtek.
The table below shows a simple description of A2DP APIs. A2DP related APIs are provided in the sdk\inc\framework\bt\bt_a2dp.h
.
Header file |
Description |
API Reference |
---|---|---|
bt_a2dp.h |
Connect A2DP, send stream data etc. |
Functions
A2DP Init
A2DP initialization procedure involves profile initialization and stream endpoint registration.
bt_a2dp_init() - Initialize A2DP profile, and configure the maximum number of remote devices and A2DP latency. A2DP latency is used to enable synchronization of audio and video playback by reporting of SNK delay values caused by buffering, decoding and rendering.
bt_a2dp_stream_endpoint_add() - Add A2DP stream end point. An A/V stream on a Bluetooth link is sent or received by Bluetooth devices via an abstract Stream End Point (SEP). A device can discover the SEPs of the remote device by the Stream End Point Discovery procedure, which is done by framework. The Application registers its SEPs in order to allow other devices to discover and connect to them. The SEP with sbc codec shall always be registered.
bt_a2dp_stream_endpoint_delete() - Delete A2DP stream endpoint. Stream endpoint must be deleted when all A2DP profile links were disconnected.
void app_a2dp_init(void)
{
if (app_cfg_const.supported_profile_mask & A2DP_PROFILE_MASK)
{
bt_sdp_record_add((void *)a2dp_sdp_record);
bt_a2dp_init(app_cfg_const.a2dp_link_number, A2DP_LATENCY_MS);
if (app_cfg_const.a2dp_codec_type_sbc)
{
T_BT_A2DP_STREAM_END_POINT sep;
sep.codec_type = BT_A2DP_CODEC_TYPE_SBC;
sep.u.codec_sbc.sampling_frequency_mask = app_cfg_const.sbc_sampling_frequency;
sep.u.codec_sbc.channel_mode_mask = app_cfg_const.sbc_channel_mode;
sep.u.codec_sbc.block_length_mask = app_cfg_const.sbc_block_length;
sep.u.codec_sbc.subbands_mask = app_cfg_const.sbc_subbands;
sep.u.codec_sbc.allocation_method_mask = app_cfg_const.sbc_allocation_method;
sep.u.codec_sbc.min_bitpool = app_cfg_const.sbc_min_bitpool;
sep.u.codec_sbc.max_bitpool = app_cfg_const.sbc_max_bitpool;
bt_a2dp_stream_endpoint_add(sep);
}
if (app_cfg_const.a2dp_codec_type_aac)
{
T_BT_A2DP_STREAM_END_POINT sep;
sep.codec_type = BT_A2DP_CODEC_TYPE_AAC;
sep.u.codec_aac.object_type_mask = app_cfg_const.aac_object_type;
sep.u.codec_aac.sampling_frequency_mask = app_cfg_const.aac_sampling_frequency;
sep.u.codec_aac.channel_number_mask = app_cfg_const.aac_channel_number;
sep.u.codec_aac.vbr_supported = app_cfg_const.aac_vbr_supported;
sep.u.codec_aac.bit_rate = app_cfg_const.aac_bit_rate;
bt_a2dp_stream_endpoint_add(sep);
}
bt_mgr_cback_register(app_a2dp_bt_cback);
}
}
A2DP Connect
bt_a2dp_connect_req() - Initiate an A2DP connection to a remote device, as shown below.
bt_a2dp_disconnect_req() - Request an A2DP disconnection, as shown below.
bt_a2dp_connect_cfm() - Accept or reject the incoming connection from the remote device, as shown below.
static void app_a2dp_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
T_APP_BR_LINK *p_link;
switch (event_type)
{
case BT_EVENT_A2DP_CONN_IND:
{
p_link = app_find_br_link(param->bt_a2dp_conn_ind.bd_addr);
if (p_link != NULL)
{
bt_a2dp_connect_cfm(p_link->bd_addr, true);
}
}
break;
case BT_EVENT_A2DP_CONN_CMPL:
break;
case BT_EVENT_A2DP_CONN_FAIL:
/*If both devices initiate a connection at the same time, both channels shall be closed and BT_EVENT_A2DP_CONN_FAIL will be received.
Each device shall wait a random time (not more than 1s and not less than 100ms) and then try to initiate the A2DP connection again. */
break;
case BT_EVENT_A2DP_CONFIG_CMPL:
{
p_link = app_find_br_link(param->bt_a2dp_config_cmpl.bd_addr);
if (p_link != NULL)
{
T_BT_EVENT_PARAM_A2DP_CONFIG_CMPL *cfg = ¶m->bt_a2dp_config_cmpl;
p_link->a2dp_codec_type = param->bt_a2dp_config_cmpl.codec_type;
if (p_link->a2dp_codec_type == BT_A2DP_CODEC_TYPE_SBC)
{
p_link->a2dp_codec_info.sbc.sampling_frequency = cfg->codec_info.sbc.sampling_frequency;
p_link->a2dp_codec_info.sbc.channel_mode = cfg->codec_info.sbc.channel_mode;
p_link->a2dp_codec_info.sbc.block_length = cfg->codec_info.sbc.block_length;
p_link->a2dp_codec_info.sbc.subbands = cfg->codec_info.sbc.subbands;
p_link->a2dp_codec_info.sbc.allocation_method = cfg->codec_info.sbc.allocation_method;
p_link->a2dp_codec_info.sbc.min_bitpool = cfg->codec_info.sbc.min_bitpool;
p_link->a2dp_codec_info.sbc.max_bitpool = cfg->codec_info.sbc.max_bitpool;
}
else if (p_link->a2dp_codec_type == BT_A2DP_CODEC_TYPE_AAC)
{
p_link->a2dp_codec_info.aac.object_type = cfg->codec_info.aac.object_type;
p_link->a2dp_codec_info.aac.sampling_frequency = cfg->codec_info.aac.sampling_frequency;
p_link->a2dp_codec_info.aac.channel_number = cfg->codec_info.aac.channel_number;
p_link->a2dp_codec_info.aac.vbr_supported = cfg->codec_info.aac.vbr_supported;
p_link->a2dp_codec_info.aac.allocation_method = cfg->codec_info.aac.allocation_method;
p_link->a2dp_codec_info.aac.bit_rate = cfg->codec_info.aac.bit_rate;
}
else
{
memcpy(p_link->a2dp_codec_info.vendor.info, cfg->codec_info.vendor.info, 12);
}
}
}
break;
case BT_EVENT_A2DP_DISCONN_CMPL:
break;
}
}
A2DP Streaming
bt_a2dp_stream_open_req() - Connect a streaming channel to the remote device, as shown below.
bt_a2dp_stream_start_req() - Request to start streaming audio data over a streaming channel, as shown below.
Note
bt_avrcp_play()
is recommended to start music.
bt_a2dp_stream_start_cfm() - Accept or reject the incoming start streaming request from a remote device, as shown below.
static void app_a2dp_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
{
T_BT_EVENT_PARAM *param = event_buf;
T_APP_BR_LINK *p_link;
switch (event_type)
{
case BT_EVENT_A2DP_STREAM_OPEN:
break;
case BT_EVENT_A2DP_STREAM_START_IND:
{
p_link = app_find_br_link(param->bt_a2dp_stream_start_ind.bd_addr);
if (p_link != NULL)
{
bt_a2dp_connect_cfm(p_link->bd_addr, true);
}
}
break;
case BT_EVENT_A2DP_STREAM_STOP:
//release resources
break;
case BT_EVENT_A2DP_STREAM_CLOSE:
//release resources
break;
case BT_EVENT_A2DP_STREAM_DATA_IND:
//deal with audio data
break;
}
}
bt_a2dp_stream_close_req() - Request to close a streaming channel, as shown below.
bt_a2dp_stream_suspend_req() - Request to cease the streaming of audio data over a streaming channel, as shown below.
Note
bt_avrcp_pause()
is recommended to stop music.
bt_a2dp_stream_abort_req() - Request to abort the streaming channel.
bt_a2dp_stream_delay_report_request() - Request to Set A2DP stream latency. This enables synchronous playback of audio and video.
bt_a2dp_active_link_set() - Set an active A2DP stream link for A2DP multi-link.
bt_a2dp_stream_data_send() - Send A2DP stream data when role is Source.
static void app_a2dp_src_send_data(void)
{
//get media data
if (a2dp_credits > 0)
{
if (bt_a2dp_stream_data_send(a2dp_sink_addr, frame_num, p_data, length))
{
a2dp_credits--;
}
else
{
APP_PRINT_ERROR0("app_a2dp_src_send_data: send a2dp media pkt failed");
}
}
}
Sample Projects
SDK provides a corresponding demo application for developers’ reference in development. The BR/EDR Audio gives a simple example on how to use A2DP.