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:

  • SOURCE (SRC) - A device is the SRC when it acts as a source of a digital audio stream that is delivered to the SNK of the piconet.

  • SINK (SNK) - A device is the SNK when it acts as a sink of a digital audio stream delivered from the SRC on the same piconet.

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.

  1. Connecting and Disconnecting: Including functions to connect/disconnect A2DP.

  2. 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.

Bluetooth A2DP Profile

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

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 = &param->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

Note

bt_avrcp_play() is recommended to start music.

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;
    }
}

Note

bt_avrcp_pause() is recommended to stop music.

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.