feat(bt): Support AAC codec for A2DP

- Allow multiple registrations of SEP
- Add a callback event to report the sep information of A2DP sink
- Closes https://github.com/espressif/esp-idf/issues/12114
- Closes https://github.com/espressif/esp-idf/issues/18184
This commit is contained in:
yangfeng
2026-05-07 17:11:57 +08:00
parent f7a5ef7ad5
commit 84bdfef5cf
21 changed files with 2247 additions and 226 deletions
@@ -75,6 +75,7 @@ list(APPEND bluedroid_host_srcs
"${CMAKE_CURRENT_LIST_DIR}/bta/av/bta_av_ci.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/av/bta_av_main.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/av/bta_av_sbc.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/av/bta_av_m24.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/av/bta_av_ssm.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/dm/bta_dm_act.c"
"${CMAKE_CURRENT_LIST_DIR}/bta/dm/bta_dm_api.c"
@@ -154,6 +155,7 @@ list(APPEND bluedroid_host_srcs
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_control.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_sink.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_latm_raw.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_source.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_a2dp_source_ext_codec.c"
"${CMAKE_CURRENT_LIST_DIR}/btc/profile/std/a2dp/btc_av.c"
@@ -190,6 +192,7 @@ list(APPEND bluedroid_host_srcs
"${CMAKE_CURRENT_LIST_DIR}/main/bte_main.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/a2dp/a2d_api.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/a2dp/a2d_sbc.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/a2dp/a2d_m24.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/avct/avct_api.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/avct/avct_ccb.c"
"${CMAKE_CURRENT_LIST_DIR}/stack/avct/avct_l2c.c"
+18
View File
@@ -98,6 +98,24 @@ config BT_A2DP_USE_EXTERNAL_CODEC
audio data in application layer. The internal codec in A2DP will be remove in
the future, it is recommend to use external codec for new design.
config BT_A2DP_CODEC_AAC_ENABLED
bool "A2DP AAC codec"
depends on BT_A2DP_USE_EXTERNAL_CODEC
default n
help
Enable support for A2DP AAC codec when using an external codec.
Currently, only the A2DP sink supports AAC codec.
config BT_A2DP_SEP_NUM_MAX
int "Maximum number of Stream End Points that can be registered for A2DP"
depends on BT_A2DP_USE_EXTERNAL_CODEC
default 2 if BT_A2DP_CODEC_AAC_ENABLED
default 1
range 1 3
help
The maximum number of Stream End Points (SEPs) that can be registered for A2DP.
The default value is 2. If you need to register more SEPs, you can increase this value.
config BT_AVRCP_ENABLED
bool
depends on BT_A2DP_ENABLE
@@ -10,12 +10,20 @@
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_a2dp_legacy_api.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_A2D_MAX_SEPS 1 /*!< Maximum number of Stream Endpoint that supported */
/**
* @brief Maximum number of Stream Endpoint that supported
*/
#ifdef CONFIG_BT_A2DP_SEP_NUM_MAX
#define ESP_A2D_MAX_SEPS CONFIG_BT_A2DP_SEP_NUM_MAX
#else
#define ESP_A2D_MAX_SEPS 1
#endif
typedef uint16_t esp_a2d_conn_hdl_t; /*!< Connection handle, associate with specific device that connected */
@@ -88,7 +96,7 @@ typedef struct {
} __attribute__((packed)) esp_a2d_cie_m12_t;
/**
* @brief A2DP MPEG-2, 4 media codec capabilities information struct (Not supported yet)
* @brief A2DP MPEG-2, 4 media codec capabilities information struct
*/
typedef struct {
uint8_t drc : 1; /*!< Support of MPEG-D DRC */
@@ -136,6 +144,14 @@ typedef struct {
} cie; /*!< A2DP codec information element */
} __attribute__((packed)) esp_a2d_mcc_t;
/**
* @brief A2DP SEP media codec capabilities
*/
typedef struct {
uint8_t seid; /*!< stream endpoint id */
esp_a2d_mcc_t mcc; /*!< media codec capabilities */
} esp_a2d_sep_mcc_t;
/**
* @brief Bluetooth A2DP connection states
*/
@@ -230,8 +246,9 @@ typedef enum {
ESP_A2D_SNK_SET_DELAY_VALUE_EVT, /*!< indicate a2dp sink set delay report value complete, only used for A2DP SINK */
ESP_A2D_SNK_GET_DELAY_VALUE_EVT, /*!< indicate a2dp sink get delay report value complete, only used for A2DP SINK */
ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, /*!< report delay value, only used for A2DP SRC */
ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT, /*!< report sink codec capabilities, only used for A2DP SRC */
ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT, /*!< report currently selected sink codec capabilities, only used for A2DP SRC */
ESP_A2D_SRC_SET_PREF_MCC_EVT, /*!< indicate a2dp source set preferred media codec configuration status, only used for A2DP SRC */
ESP_A2D_REPORT_SNK_ALL_CODEC_CAPS_EVT, /*!< report all supported sink codec capabilities, only used for A2DP SRC */
} esp_a2d_cb_event_t;
/**
@@ -336,7 +353,7 @@ typedef union {
struct a2d_report_snk_codec_caps_param {
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
esp_a2d_mcc_t mcc; /*!< A2DP sink media codec capability information */
} a2d_report_snk_codec_caps_stat; /*!< A2DP source received sink codec capabilities */
} a2d_report_snk_codec_caps_stat; /*!< A2DP source received sink codec capabilities */
/**
* @brief ESP_A2D_SRC_SET_PREF_MCC_EVT
@@ -348,6 +365,16 @@ typedef union {
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
} a2d_set_pref_mcc_stat; /*!< A2DP source set preferred media codec configuration */
/**
* @brief ESP_A2D_REPORT_SNK_ALL_CODEC_CAPS_EVT
*/
struct a2d_report_snk_all_codec_caps_param {
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
esp_a2d_sep_mcc_t *sep_mcc; /*!< A2DP sink SEP information
@note The memory will be released after the callback function ends */
uint8_t sep_num; /*!< number of A2DP sink SEP information */
} a2d_report_snk_all_codec_caps_stat; /*!< A2DP source received all sink codec capabilities */
} esp_a2d_cb_param_t;
/**
@@ -406,7 +433,7 @@ esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback);
/**
* @brief Register A2DP sink audio data output function, the output format is undecoded audio data
* frame in esp_a2d_audio_buff_t, user shall call esp_a2d_audio_buff_free to free the buff
* when the data is consumed.
* when the data is consumed. It is necessary to set BT_A2DP_USE_EXTERNAL_CODEC to y.
*
* @param[in] callback: A2DP sink audio data callback function
*
@@ -438,10 +465,14 @@ esp_err_t esp_a2d_sink_init(void);
* @brief Register a a2dp sink Stream Endpoint (SEP) with specific codec capability, shall register
* SEP after a2dp sink initializing and before a2dp connection establishing. Register the same
* SEP index repeatedly will overwrite the old one.
* It is necessary to set BT_A2DP_USE_EXTERNAL_CODEC to y.
*
* @note The SEID determines the priority of negotiating the configuration with the peer for initiator.
* The lower the SEID, the higher the priority of the codec capability.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently only supports SBC
* @param[in] mcc: codec capability, currently supports SBC and AAC
*
* @return
* - ESP_OK: success
@@ -590,10 +621,14 @@ esp_err_t esp_a2d_source_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, const esp_a2d
* @brief Register a a2dp source Stream Endpoint (SEP) with specific codec capability, shall register
* SEP after a2dp source initializing and before a2dp connection establishing. Register the same
* SEP index repeatedly will overwrite the old one.
* It is necessary to set BT_A2DP_USE_EXTERNAL_CODEC to y.
*
* @note The SEID determines the priority of negotiating the configuration with the peer for initiator.
* The lower the SEID, the higher the priority of the codec capability.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently, only SBC supported
* @param[in] mcc: codec capability, currently, SBC/AAC supported
*
* @return
* - ESP_OK: success
@@ -1330,9 +1330,12 @@ void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
p_scb->avdt_version = AVDT_VERSION_SYNC;
}
if (p_scb->codec_type == BTA_AV_CODEC_SBC || num > 1) {
/* if SBC is used by the SNK as INT, discover req is not sent in bta_av_config_ind.
if (p_scb->codec_type == BTA_AV_CODEC_SBC || num > 1
#if (BTA_AV_CODEC_AAC_INCLUDED == TRUE)
|| p_scb->codec_type == BTA_AV_CODEC_M24
#endif
) {
/* if SBC/AAC is used by the SNK as INT, discover req is not sent in bta_av_config_ind.
* call cfg_res now */
/* this is called in A2DP SRC path only, In case of SINK we don't need it */
if (local_sep == AVDT_TSEP_SRC) {
@@ -1340,7 +1343,7 @@ void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
UUID_SERVCLASS_AUDIO_SOURCE);
}
} else {
/* we do not know the peer device and it is using non-SBC codec
/* we do not know the peer device and it is using non-SBC/AAC codec
* we need to know all the SEPs on SNK */
bta_av_discover_req(p_scb, NULL);
return;
@@ -2216,9 +2219,8 @@ void bta_av_reconfig (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
/* drop the buffers queued in L2CAP */
L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
AVDT_CloseReq(p_scb->avdt_handle);
}
AVDT_CloseReq(p_scb->avdt_handle);
}
}
@@ -2684,6 +2686,11 @@ void bta_av_rcfg_str_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
p_scb->role &= ~BTA_AV_ROLE_START_INT;
{
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback) {
/* report codec configuration after successful reconfig */
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
/* reconfigure success */
evt.status = BTA_AV_SUCCESS;
evt.chnl = p_scb->chnl;
@@ -0,0 +1,255 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/******************************************************************************
*
* MPEG-2, 4 AAC (M24) codec helpers for A2DP.
*
******************************************************************************/
#include "common/bt_target.h"
#include "stack/a2d_api.h"
#include "stack/a2d_m24.h"
#include "bta/bta_av_m24.h"
#if (BTA_AV_INCLUDED == TRUE) && (BTA_AV_CODEC_AAC_INCLUDED == TRUE)
#define BTA_AV_M24_VBR_BR1_OFF 6
#define BTA_AV_M24_BR2_OFF 7
#define BTA_AV_M24_BR3_OFF 8
static UINT32 bta_av_m24_br(const tA2D_M24_CIE *p)
{
return (((UINT32)(p->br1 & A2D_M24_IE_BR1_MSK)) << 16) |
(((UINT32)(p->br2 & A2D_M24_IE_BR2_MSK)) << 8) |
((UINT32)(p->br3 & A2D_M24_IE_BR3_MSK));
}
static void bta_av_m24_set_br(tA2D_M24_CIE *p, UINT32 v, UINT8 vbr_flag)
{
v &= 0x7FFFFFU;
p->vbr = (UINT8)(vbr_flag & A2D_M24_IE_VBR_MSK);
p->br1 = (UINT8)((v >> 16) & A2D_M24_IE_BR1_MSK);
p->br2 = (UINT8)((v >> 8) & A2D_M24_IE_BR2_MSK);
p->br3 = (UINT8)(v & A2D_M24_IE_BR3_MSK);
}
UINT32 bta_av_m24_br_info(const UINT8 *codec_info)
{
return ((UINT32)((codec_info[BTA_AV_M24_VBR_BR1_OFF] & A2D_M24_IE_BR1_MSK) << 16) |
(UINT32)((codec_info[BTA_AV_M24_BR2_OFF] & A2D_M24_IE_BR2_MSK) << 8 ) |
(UINT32)( codec_info[BTA_AV_M24_BR3_OFF] & A2D_M24_IE_BR3_MSK));
}
void bta_av_m24_set_br_info(UINT8 *codec_info, UINT32 bit_rate)
{
bit_rate &= 0x7FFFFFU;
codec_info[BTA_AV_M24_VBR_BR1_OFF] = (UINT8)(codec_info[BTA_AV_M24_VBR_BR1_OFF] & A2D_M24_IE_VBR_MSK) |
(UINT8)((bit_rate >> 16) & A2D_M24_IE_BR1_MSK);
codec_info[BTA_AV_M24_BR2_OFF] = (UINT8)((bit_rate >> 8) & A2D_M24_IE_BR2_MSK);
codec_info[BTA_AV_M24_BR3_OFF] = (UINT8)(bit_rate & A2D_M24_IE_BR3_MSK);
}
UINT8 bta_av_m24_cap_matches_cap(UINT8 *p_cap1, UINT8 *p_cap2)
{
tA2D_M24_CIE cap1;
tA2D_M24_CIE cap2;
UINT8 st;
UINT8 cap1_sf1;
UINT8 cap2_sf1;
UINT8 cap1_sf2;
UINT8 cap2_sf2;
UINT32 cap1_br;
UINT32 cap2_br;
st = A2D_ParsM24Info(&cap1, p_cap1, TRUE);
if (st != A2D_SUCCESS) {
return st;
}
st = A2D_ParsM24Info(&cap2, p_cap2, TRUE);
if (st != A2D_SUCCESS) {
return st;
}
if ((cap1.obj_type & cap2.obj_type) == 0) {
return A2D_NS_OBJ_TYPE;
}
cap1_sf1 = (UINT8)(cap1.samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK);
cap2_sf1 = (UINT8)(cap2.samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK);
cap1_sf2 = (UINT8)(cap1.samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK);
cap2_sf2 = (UINT8)(cap2.samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK);
if (!(cap1_sf1 & cap2_sf1) && !(cap1_sf2 & cap2_sf2)) {
return A2D_NS_SAMP_FREQ;
}
if ((cap1.ch & cap2.ch) == 0) {
return A2D_NS_CHANNEL;
}
cap1_br = bta_av_m24_br(&cap1);
cap2_br = bta_av_m24_br(&cap2);
if (!cap1_br || !cap2_br) {
return A2D_NS_BIT_RATE;
}
return A2D_SUCCESS;
}
UINT8 bta_av_m24_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap)
{
tA2D_M24_CIE cfg;
tA2D_M24_CIE cap;
UINT8 st;
UINT8 cfg_sf1;
UINT8 cap_sf1;
UINT8 cfg_sf2;
UINT8 cap_sf2;
UINT32 cfg_br;
UINT32 cap_br;
st = A2D_ParsM24Info(&cfg, p_cfg, FALSE);
if (st != A2D_SUCCESS) {
return st;
}
st = A2D_ParsM24Info(&cap, p_cap, TRUE);
if (st != A2D_SUCCESS) {
return st;
}
if ((cfg.obj_type & cap.obj_type) != cfg.obj_type) {
return A2D_NS_OBJ_TYPE;
}
if (cfg.drc && !(cap.drc & A2D_M24_IE_DRC_SUPPORT)) {
return A2D_FAIL;
}
cfg_sf1 = (UINT8)(cfg.samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK);
cap_sf1 = (UINT8)(cap.samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK);
cfg_sf2 = (UINT8)(cfg.samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK);
cap_sf2 = (UINT8)(cap.samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK);
if (!((cfg_sf1 & cap_sf1) ^ (cfg_sf2 & cap_sf2))) {
return A2D_NS_SAMP_FREQ;
}
if ((cfg.ch & cap.ch) != cfg.ch) {
return A2D_NS_CHANNEL;
}
if (cfg.vbr && !(cap.vbr & A2D_M24_IE_VBR_SUPPORT)) {
return A2D_NS_VBR;
}
cfg_br = bta_av_m24_br(&cfg);
cap_br = bta_av_m24_br(&cap);
if (cfg_br > cap_br) {
return A2D_NS_BIT_RATE;
}
return A2D_SUCCESS;
}
tA2D_STATUS bta_av_m24_pick_pref_from_src_cap(const tA2D_M24_CIE *src_cap, tA2D_M24_CIE *p_pref)
{
UINT8 i;
UINT32 src_br_max;
UINT32 p_pref_br;
static const UINT8 sf2_order[] = {
A2D_M24_IE_SAMP_FREQ2_96,
A2D_M24_IE_SAMP_FREQ2_88,
A2D_M24_IE_SAMP_FREQ2_64,
A2D_M24_IE_SAMP_FREQ2_48,
};
static const UINT8 sf1_order[] = {
A2D_M24_IE_SAMP_FREQ1_44,
A2D_M24_IE_SAMP_FREQ1_32,
A2D_M24_IE_SAMP_FREQ1_24,
A2D_M24_IE_SAMP_FREQ1_22,
A2D_M24_IE_SAMP_FREQ1_16,
A2D_M24_IE_SAMP_FREQ1_12,
A2D_M24_IE_SAMP_FREQ1_11,
A2D_M24_IE_SAMP_FREQ1_8,
};
if (!((p_pref->obj_type & src_cap->obj_type) & A2D_M24_IE_OBJ_TYPE_MSK)) {
/* Object type: prefer MPEG-4 AAC LC, then MPEG-2 AAC LC */
if (src_cap->obj_type & A2D_M24_IE_OBJ_TYPE_4_AAC_LC) {
p_pref->obj_type = A2D_M24_IE_OBJ_TYPE_4_AAC_LC;
} else if (src_cap->obj_type & A2D_M24_IE_OBJ_TYPE_2_AAC_LC) {
p_pref->obj_type = A2D_M24_IE_OBJ_TYPE_2_AAC_LC;
} else {
UINT8 o = (UINT8)(src_cap->obj_type & A2D_M24_IE_OBJ_TYPE_MSK);
if (o == 0) {
return A2D_FAIL;
}
p_pref->obj_type = (UINT8)(o & (UINT8)(-o));
}
}
if ((p_pref->drc & A2D_M24_IE_DRC_MSK) && !(src_cap->drc & A2D_M24_IE_DRC_MSK)) {
p_pref->drc = A2D_M24_IE_DRC_NS;
}
if ((p_pref->obj_type & A2D_M24_IE_OBJ_TYPE_MSK) == A2D_M24_IE_OBJ_TYPE_2_AAC_LC) {
p_pref->drc = A2D_M24_IE_DRC_NS;
}
if (!((p_pref->samp_freq1 & src_cap->samp_freq1) & A2D_M24_IE_SAMP_FREQ1_MSK) &&
!((p_pref->samp_freq2 & src_cap->samp_freq2) & A2D_M24_IE_SAMP_FREQ2_MSK)) {
/* One frequency: prefer 96..48 kHz, else 44.1..8 kHz (byte 1) */
p_pref->samp_freq1 = 0;
p_pref->samp_freq2 = 0;
for (i = 0; i < sizeof(sf2_order) / sizeof(sf2_order[0]); i++) {
if (src_cap->samp_freq2 & sf2_order[i]) {
p_pref->samp_freq2 = (UINT8)(sf2_order[i] & A2D_M24_IE_SAMP_FREQ2_MSK);
p_pref->samp_freq1 = A2D_M24_IE_SAMP_FREQ1_NS;
goto got_sf;
}
}
for (i = 0; i < sizeof(sf1_order) / sizeof(sf1_order[0]); i++) {
if (src_cap->samp_freq1 & sf1_order[i]) {
p_pref->samp_freq1 = sf1_order[i];
p_pref->samp_freq2 = A2D_M24_IE_SAMP_FREQ2_NS;
goto got_sf;
}
}
return A2D_FAIL;
}
got_sf:
if (!((p_pref->ch & src_cap->ch) & A2D_M24_IE_CH_MSK)) {
/* Channels: prefer stereo */
if (src_cap->ch & A2D_M24_IE_CH_2) {
p_pref->ch = A2D_M24_IE_CH_2;
} else if (src_cap->ch & A2D_M24_IE_CH_1) {
p_pref->ch = A2D_M24_IE_CH_1;
} else if (src_cap->ch & A2D_M24_IE_CH_6) {
p_pref->ch = A2D_M24_IE_CH_6;
} else if (src_cap->ch & A2D_M24_IE_CH_8) {
p_pref->ch = A2D_M24_IE_CH_8;
} else {
return A2D_FAIL;
}
}
if ((p_pref->vbr & A2D_M24_IE_VBR_MSK) && !(src_cap->vbr & A2D_M24_IE_VBR_MSK)) {
p_pref->vbr = A2D_M24_IE_VBR_NS;
}
src_br_max = bta_av_m24_br(src_cap);
p_pref_br = bta_av_m24_br(p_pref);
if (p_pref_br > src_br_max) {
p_pref_br = src_br_max;
}
bta_av_m24_set_br(p_pref, p_pref_br, (UINT8)(p_pref->vbr & A2D_M24_IE_VBR_MSK));
return A2D_SUCCESS;
}
#endif /* (BTA_AV_INCLUDED == TRUE) && (BTA_AV_CODEC_AAC_INCLUDED == TRUE) */
@@ -671,7 +671,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
#if (BTA_AV_EXT_CODEC == FALSE)
while (index < BTA_AV_MAX_SEPS &&
(p_scb->p_cos->init)(&codec_type, cs.cfg.codec_info,
(p_scb->p_cos->init)(index, &codec_type, cs.cfg.codec_info,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg.tsep) == TRUE) {
#if (BTA_AV_SINK_INCLUDED == TRUE)
@@ -807,7 +807,7 @@ static void bta_av_api_reg_sep(tBTA_AV_DATA *p_data)
codec_type = p_data->api_reg_sep.codec_type;
memcpy(cs.cfg.codec_info, p_data->api_reg_sep.codec_info, AVDT_CODEC_SIZE);
if ((p_scb->p_cos->init)(&codec_type, cs.cfg.codec_info,
if ((p_scb->p_cos->init)(index, &codec_type, cs.cfg.codec_info,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg_sep.tsep) == TRUE) {
#if (BTA_AV_SINK_INCLUDED == TRUE)
@@ -30,6 +30,7 @@
#include "stack/avdt_api.h"
#include "stack/a2d_api.h"
#include "bta/bta_api.h"
#include "common/bt_target.h"
#if (BTA_AV_INCLUDED == TRUE)
@@ -96,7 +97,7 @@ typedef UINT8 tBTA_AV_HNDL;
#endif
#ifndef BTA_AV_MAX_SEPS
#define BTA_AV_MAX_SEPS 1
#define BTA_AV_MAX_SEPS AVDT_NUM_SEPS
#endif
#ifndef BTA_AV_MAX_A2DP_MTU
@@ -277,7 +278,7 @@ typedef UINT8 tBTA_AV_GET_TYPE;
/* function types for call-out functions */
typedef BOOLEAN (*tBTA_AV_CO_INIT) (UINT8 *p_codec_type, UINT8 *p_codec_info,
typedef BOOLEAN (*tBTA_AV_CO_INIT) (UINT8 seid, UINT8 *p_codec_type, UINT8 *p_codec_info,
UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 tsep);
typedef void (*tBTA_AV_CO_DISC_RES) (tBTA_AV_HNDL hndl, UINT8 num_seps,
UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local);
@@ -84,7 +84,7 @@ typedef struct {
** Returns Stream codec and content protection capabilities info.
**
*******************************************************************************/
extern BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info,
extern BOOLEAN bta_av_co_audio_init(UINT8 seid, UINT8 *p_codec_type, UINT8 *p_codec_info,
UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 tsep);
/*******************************************************************************
@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/******************************************************************************
*
* Utility functions for MPEG-2, 4 AAC (M24) codec configuration (A2DP).
*
******************************************************************************/
#ifndef BTA_AV_M24_H
#define BTA_AV_M24_H
#include "common/bt_target.h"
#include "stack/a2d_m24.h"
#if (BTA_AV_INCLUDED == TRUE) && (BTA_AV_CODEC_AAC_INCLUDED == TRUE)
#ifdef __cplusplus
extern "C" {
#endif
/*******************************************************************************
**
** Function bta_av_m24_br_info
**
** Description Calculate the bit rate from codec info.
**
** Returns Bit rate value.
**
*******************************************************************************/
extern UINT32 bta_av_m24_br_info(const UINT8 *codec_info);
/*******************************************************************************
**
** Function bta_av_m24_set_br_info
**
** Description set the bit rate for codec info.
**
*******************************************************************************/
extern void bta_av_m24_set_br_info(UINT8 *codec_info, UINT32 bit_rate);
/*******************************************************************************
**
** Function bta_av_m24_cap_matches_cap
**
** Description Compare two proposed M24 codec capability sets (full Media
** Codec Element sequences) to determine if they are compatible,
** i.e., whether all features of cap2 are supported by cap1.
**
** Returns A2D_SUCCESS if ok, nonzero A2D status otherwise.
**
*******************************************************************************/
extern UINT8 bta_av_m24_cap_matches_cap(UINT8 *p_cap1, UINT8 *p_cap2);
/*******************************************************************************
**
** Function bta_av_m24_cfg_in_external_codec_cap
**
** Description Check whether p_cfg is allowable for external codec
** capability buffer p_cap (both full LOSC.. sequences).
**
** Returns A2D_SUCCESS if ok, nonzero A2D status otherwise.
**
*******************************************************************************/
extern UINT8 bta_av_m24_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap);
/*******************************************************************************
**
** Function bta_av_m24_pick_pref_from_src_cap
**
** Description Build a single-configuration tA2D_M24_CIE from peer SRC
** capability bitmask (for Sink initiator GetConfiguration).
**
** Returns A2D_SUCCESS or error.
**
*******************************************************************************/
extern tA2D_STATUS bta_av_m24_pick_pref_from_src_cap(const tA2D_M24_CIE *src_cap, tA2D_M24_CIE *p_pref);
#ifdef __cplusplus
}
#endif
#endif /* (BTA_AV_INCLUDED == TRUE) && (BTA_AV_CODEC_AAC_INCLUDED == TRUE) */
#endif /* BTA_AV_M24_H */
@@ -27,16 +27,23 @@
#include "common/bt_target.h"
#include "stack/a2d_api.h"
#include "stack/a2d_sbc.h"
#include "stack/a2d_m24.h"
#include "bta/bta_sys.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_co.h"
#include "bta/bta_av_ci.h"
#include "bta/bta_av_sbc.h"
#include "bta/bta_av_m24.h"
#include "btc_a2dp.h"
#include "btc_a2dp_source.h"
#include "btc_av_co.h"
#include "btc/btc_util.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#if (BTC_AV_SRC_INCLUDED == TRUE)
#include "esp_a2dp_api.h"
#include "btc_av.h"
#endif
#if BTC_AV_INCLUDED
@@ -106,6 +113,35 @@ const tA2D_SBC_CIE btc_av_sbc_default_config = {
A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */
};
/* Offsets to access codec information in AAC codec */
#define BTA_AV_CO_M24_OBJ_DRC_OFF 3
#define BTA_AV_CO_M24_SAMP_FREQ1_OFF 4
#define BTA_AV_CO_M24_FREQ2_CHAN_OFF 5
#define BTA_AV_CO_M24_VBR_BR1_OFF 6
#define BTA_AV_CO_M24_BR2_OFF 7
#define BTA_AV_CO_M24_BR3_OFF 8
#define BTA_AV_CO_M24_INFO_LEN 9
/* default bit rate: 256000 */
#define BTA_AV_CO_M24_DEFAULT_BR1 0x03
#define BTA_AV_CO_M24_DEFAULT_BR2 0xe8
#define BTA_AV_CO_M24_DEFAULT_BR3 0x00
/* Default MPEG-2,4 AAC codec configuration */
const tA2D_M24_CIE btc_av_m24_default_config = {
A2D_M24_IE_DRC_NS,
A2D_M24_IE_OBJ_TYPE_2_AAC_LC,
A2D_M24_IE_SAMP_FREQ1_44,
A2D_M24_IE_CH_2,
A2D_M24_IE_SAMP_FREQ2_NS,
BTA_AV_CO_M24_DEFAULT_BR1,
A2D_M24_IE_VBR_SUPPORT,
BTA_AV_CO_M24_DEFAULT_BR2,
BTA_AV_CO_M24_DEFAULT_BR3,
};
/* Control block instance */
#if AVRC_DYNAMIC_MEMORY == FALSE
tBTA_AV_CO_CB bta_av_co_cb;
@@ -208,6 +244,60 @@ static tBTA_AV_CO_PEER *bta_av_co_get_peer(tBTA_AV_HNDL hndl)
return &bta_av_co_cb.peers[index];
}
#if (BTC_AV_SRC_INCLUDED == TRUE)
static void bta_av_co_fill_sep_mcc_from_sup_snk(const tBTA_AV_CO_SINK *p_snk, esp_a2d_sep_mcc_t *p_out)
{
p_out->seid = p_snk->seid;
memset(&p_out->mcc, 0, sizeof(p_out->mcc));
switch (p_snk->codec_type) {
case BTA_AV_CODEC_SBC:
p_out->mcc.type = ESP_A2D_MCT_SBC;
memcpy(&p_out->mcc.cie, (const UINT8 *)p_snk->codec_caps + A2D_SBC_CIE_OFF,
A2D_SBC_CIE_LEN);
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTA_AV_CODEC_M24:
p_out->mcc.type = ESP_A2D_MCT_M24;
memcpy(&p_out->mcc.cie, (const UINT8 *)p_snk->codec_caps + A2D_M24_CIE_OFF,
A2D_M24_CIE_LEN);
break;
#endif
default:
p_out->mcc.type = ESP_A2D_MCT_NON_A2DP;
break;
}
}
static void bta_av_co_report_peer_all_snk_codec_caps(tBTA_AV_HNDL hndl)
{
tBTA_AV_CO_PEER *p_peer;
esp_a2d_sep_mcc_t *sep_mc;
UINT8 i, n;
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL || p_peer->num_sup_snks == 0) {
return;
}
n = p_peer->num_sup_snks;
sep_mc = (esp_a2d_sep_mcc_t *)osi_malloc(n * sizeof(esp_a2d_sep_mcc_t));
if (sep_mc == NULL) {
return;
}
for (i = 0; i < n; i++) {
bta_av_co_fill_sep_mcc_from_sup_snk(&p_peer->snks[i], &sep_mc[i]);
}
btc_av_report_all_snk_codec_caps(hndl, sep_mc, n);
osi_free(sep_mc);
}
#endif /* BTC_AV_SRC_INCLUDED */
/*******************************************************************************
**
** Function bta_av_co_audio_init
@@ -221,7 +311,7 @@ static tBTA_AV_CO_PEER *bta_av_co_get_peer(tBTA_AV_HNDL hndl)
** Returns Stream codec and content protection capabilities info.
**
*******************************************************************************/
BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_num_protect,
BOOLEAN bta_av_co_audio_init(UINT8 seid, UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_num_protect,
UINT8 *p_protect_info, UINT8 tsep)
{
FUNC_TRACE();
@@ -234,6 +324,11 @@ BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_
/* reset remote preference through setconfig */
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
/* reset local preferred media codec configuration */
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.cur_seid = 0;
#endif
if (tsep == AVDT_TSEP_SRC) {
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -248,8 +343,8 @@ BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_
#endif
#if (BTC_AV_EXT_CODEC == TRUE)
/* for external codec, we get codec capability from BTA_AV */
bta_av_co_cb.codec_caps.id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps.info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_cb.codec_caps[seid].id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps[seid].info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_audio_codec_reset();
#else
/* Set up for SBC codec for SRC*/
@@ -261,8 +356,8 @@ BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_
} else if (tsep == AVDT_TSEP_SNK) {
#if (BTC_AV_EXT_CODEC == TRUE)
/* for external codec, we get codec capability from BTA_AV */
bta_av_co_cb.codec_caps.id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps.info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_cb.codec_caps[seid].id = *p_codec_type;
memcpy(bta_av_co_cb.codec_caps[seid].info, p_codec_info, AVDT_CODEC_SIZE);
bta_av_co_audio_codec_reset();
#else
*p_codec_type = BTA_AV_CODEC_SBC;
@@ -392,63 +487,121 @@ void bta_av_co_audio_cfg_res(tBTA_AV_HNDL hndl, UINT8 num_seps, UINT8 num_snk,
*******************************************************************************/
void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
{
tA2D_SBC_CIE src_cap;
tA2D_SBC_CIE pref_cap;
UINT8 status = 0;
UINT8 codec_type;
/* initialize it to default SBC configuration */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &btc_av_sbc_default_config, p_pref_cfg);
/* now try to build a preferred one */
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0) {
APPL_TRACE_DEBUG(" Can't parse src cap ret = %d", status);
return ;
if (p_src_cap) {
codec_type = *(p_src_cap + 2);
switch (codec_type) {
case A2D_MEDIA_CT_SBC: {
tA2D_SBC_CIE src_cap_sbc;
tA2D_SBC_CIE pref_cap_sbc;
memcpy(&pref_cap_sbc, &btc_av_sbc_default_config, sizeof(tA2D_SBC_CIE));
/* initialize it to default SBC configuration */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &btc_av_sbc_default_config, p_pref_cfg);
/* now try to build a preferred one */
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&src_cap_sbc, p_src_cap, TRUE)) != 0) {
APPL_TRACE_DEBUG(" Can't parse src cap ret = %d", status);
return;
}
if (!((pref_cap_sbc.samp_freq & src_cap_sbc.samp_freq) & A2D_SBC_IE_SAMP_FREQ_MSK)) {
if (src_cap_sbc.samp_freq & A2D_SBC_IE_SAMP_FREQ_48) {
pref_cap_sbc.samp_freq = A2D_SBC_IE_SAMP_FREQ_48;
} else if (src_cap_sbc.samp_freq & A2D_SBC_IE_SAMP_FREQ_44) {
pref_cap_sbc.samp_freq = A2D_SBC_IE_SAMP_FREQ_44;
}
}
if (!((pref_cap_sbc.ch_mode & src_cap_sbc.ch_mode) & A2D_SBC_IE_CH_MD_MSK)) {
if (src_cap_sbc.ch_mode & A2D_SBC_IE_CH_MD_JOINT) {
pref_cap_sbc.ch_mode = A2D_SBC_IE_CH_MD_JOINT;
} else if (src_cap_sbc.ch_mode & A2D_SBC_IE_CH_MD_STEREO) {
pref_cap_sbc.ch_mode = A2D_SBC_IE_CH_MD_STEREO;
} else if (src_cap_sbc.ch_mode & A2D_SBC_IE_CH_MD_DUAL) {
pref_cap_sbc.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
} else if (src_cap_sbc.ch_mode & A2D_SBC_IE_CH_MD_MONO) {
pref_cap_sbc.ch_mode = A2D_SBC_IE_CH_MD_MONO;
}
}
if (!((pref_cap_sbc.block_len & src_cap_sbc.block_len) & A2D_SBC_IE_BLOCKS_MSK)) {
if (src_cap_sbc.block_len & A2D_SBC_IE_BLOCKS_16) {
pref_cap_sbc.block_len = A2D_SBC_IE_BLOCKS_16;
} else if (src_cap_sbc.block_len & A2D_SBC_IE_BLOCKS_12) {
pref_cap_sbc.block_len = A2D_SBC_IE_BLOCKS_12;
} else if (src_cap_sbc.block_len & A2D_SBC_IE_BLOCKS_8) {
pref_cap_sbc.block_len = A2D_SBC_IE_BLOCKS_8;
} else if (src_cap_sbc.block_len & A2D_SBC_IE_BLOCKS_4) {
pref_cap_sbc.block_len = A2D_SBC_IE_BLOCKS_4;
}
}
if (!((pref_cap_sbc.num_subbands & src_cap_sbc.num_subbands) & A2D_SBC_IE_SUBBAND_MSK)) {
if (src_cap_sbc.num_subbands & A2D_SBC_IE_SUBBAND_8) {
pref_cap_sbc.num_subbands = A2D_SBC_IE_SUBBAND_8;
} else if (src_cap_sbc.num_subbands & A2D_SBC_IE_SUBBAND_4) {
pref_cap_sbc.num_subbands = A2D_SBC_IE_SUBBAND_4;
}
}
if (!((pref_cap_sbc.alloc_mthd & src_cap_sbc.alloc_mthd) & A2D_SBC_IE_ALLOC_MD_MSK)) {
if (src_cap_sbc.alloc_mthd & A2D_SBC_IE_ALLOC_MD_L) {
pref_cap_sbc.alloc_mthd = A2D_SBC_IE_ALLOC_MD_L;
} else if (src_cap_sbc.alloc_mthd & A2D_SBC_IE_ALLOC_MD_S) {
pref_cap_sbc.alloc_mthd = A2D_SBC_IE_ALLOC_MD_S;
}
}
if (pref_cap_sbc.max_bitpool > src_cap_sbc.max_bitpool) {
pref_cap_sbc.max_bitpool = src_cap_sbc.max_bitpool;
}
if (pref_cap_sbc.min_bitpool < src_cap_sbc.min_bitpool) {
pref_cap_sbc.min_bitpool = src_cap_sbc.min_bitpool;
}
if (pref_cap_sbc.min_bitpool > pref_cap_sbc.max_bitpool) {
pref_cap_sbc.max_bitpool = src_cap_sbc.max_bitpool;
pref_cap_sbc.min_bitpool = src_cap_sbc.min_bitpool;
}
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &pref_cap_sbc, p_pref_cfg);
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case A2D_MEDIA_CT_M24: {
tA2D_M24_CIE src_cap_m24;
tA2D_M24_CIE pref_cap_m24;
/* initialize it to default M24 configuration */
A2D_BldM24Info(AVDT_MEDIA_AUDIO, (tA2D_M24_CIE *) &btc_av_m24_default_config, p_pref_cfg);
if (A2D_ParsM24Info(&src_cap_m24, p_src_cap, TRUE) != A2D_SUCCESS) {
APPL_TRACE_DEBUG(" Can't parse M24 src cap");
return;
}
memcpy(&pref_cap_m24, &btc_av_m24_default_config, sizeof(tA2D_M24_CIE));
if (bta_av_m24_pick_pref_from_src_cap(&src_cap_m24, &pref_cap_m24) != A2D_SUCCESS) {
APPL_TRACE_DEBUG(" Can't build M24 pref from src cap");
return;
}
A2D_BldM24Info(AVDT_MEDIA_AUDIO, &pref_cap_m24, p_pref_cfg);
break;
}
#endif
default:
APPL_TRACE_ERROR("bta_av_build_src_cfg unsup_codec type: %d", codec_type);
break;
}
}
memcpy(&pref_cap, &src_cap, sizeof(tA2D_SBC_CIE));
if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_48) {
pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_48;
} else if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_44) {
pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_44;
}
if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_JOINT) {
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_JOINT;
} else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO) {
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO;
} else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL) {
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
} else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO) {
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO;
}
if (src_cap.block_len & A2D_SBC_IE_BLOCKS_16) {
pref_cap.block_len = A2D_SBC_IE_BLOCKS_16;
} else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_12) {
pref_cap.block_len = A2D_SBC_IE_BLOCKS_12;
} else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_8) {
pref_cap.block_len = A2D_SBC_IE_BLOCKS_8;
} else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_4) {
pref_cap.block_len = A2D_SBC_IE_BLOCKS_4;
}
if (src_cap.num_subbands & A2D_SBC_IE_SUBBAND_8) {
pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_8;
} else if (src_cap.num_subbands & A2D_SBC_IE_SUBBAND_4) {
pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_4;
}
if (src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_L) {
pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_L;
} else if (src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_S) {
pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_S;
}
pref_cap.max_bitpool = src_cap.max_bitpool;
pref_cap.min_bitpool = src_cap.min_bitpool;
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &pref_cap, p_pref_cfg);
}
/*******************************************************************************
@@ -498,6 +651,9 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
supported = FALSE;
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTA_AV_CODEC_M24:
#endif
supported = TRUE;
break;
@@ -510,9 +666,10 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
if (p_peer->num_sup_srcs < BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs)) {
p_src = &p_peer->srcs[p_peer->num_sup_srcs++];
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig saved caps[%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig saved caps[%x:%x:%x:%x:%x:%x:%x:%x]",
p_codec_info[1], p_codec_info[2], p_codec_info[3],
p_codec_info[4], p_codec_info[5], p_codec_info[6]);
p_codec_info[4], p_codec_info[5], p_codec_info[6],
p_codec_info[7], p_codec_info[8]);
memcpy(p_src->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
p_src->codec_type = codec_type;
@@ -546,9 +703,10 @@ UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
bta_av_build_src_cfg(pref_cfg, p_src->codec_caps);
memcpy(p_peer->codec_cfg, pref_cfg, AVDT_CODEC_SIZE);
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("bta_av_audio_sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x:%x:%x]",
p_peer->codec_cfg[1], p_peer->codec_cfg[2], p_peer->codec_cfg[3],
p_peer->codec_cfg[4], p_peer->codec_cfg[5], p_peer->codec_cfg[6]);
p_peer->codec_cfg[4], p_peer->codec_cfg[5], p_peer->codec_cfg[6],
p_peer->codec_cfg[7], p_peer->codec_cfg[8]);
/* By default, no content protection */
*p_num_protect = 0;
@@ -619,6 +777,9 @@ UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
supported = FALSE;
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTA_AV_CODEC_M24:
#endif
supported = TRUE;
break;
@@ -631,9 +792,10 @@ UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
if (p_peer->num_sup_snks < BTA_AV_CO_NUM_ELEMENTS(p_peer->snks)) {
p_sink = &p_peer->snks[p_peer->num_sup_snks++];
APPL_TRACE_DEBUG("bta_av_co_audio_getconfig saved caps[%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("bta_av_co_audio_getconfig saved caps[%x:%x:%x:%x:%x:%x:%x:%x]",
p_codec_info[1], p_codec_info[2], p_codec_info[3],
p_codec_info[4], p_codec_info[5], p_codec_info[6]);
p_codec_info[4], p_codec_info[5], p_codec_info[6],
p_codec_info[7], p_codec_info[8]);
memcpy(p_sink->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
p_sink->codec_type = codec_type;
@@ -666,9 +828,10 @@ UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
/* Build the codec configuration for this sink */
if (bta_av_co_audio_codec_build_config(p_sink->codec_caps, codec_cfg)) {
APPL_TRACE_DEBUG("bta_av_co_audio_getconfig reconfig p_codec_info[%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("bta_av_co_audio_getconfig reconfig p_codec_info[%x:%x:%x:%x:%x:%x:%x:%x]",
codec_cfg[1], codec_cfg[2], codec_cfg[3],
codec_cfg[4], codec_cfg[5], codec_cfg[6]);
codec_cfg[4], codec_cfg[5], codec_cfg[6],
codec_cfg[7], codec_cfg[8]);
/* Save the new configuration */
p_peer->p_snk = p_sink;
@@ -705,6 +868,9 @@ UINT8 bta_av_co_audio_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
}
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_unlock();
#if (BTC_AV_SRC_INCLUDED == TRUE)
bta_av_co_report_peer_all_snk_codec_caps(hndl);
#endif
}
return result;
}
@@ -734,9 +900,10 @@ void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
FUNC_TRACE();
APPL_TRACE_DEBUG("bta_av_co_audio_setconfig p_codec_info[%x:%x:%x:%x:%x:%x]",
APPL_TRACE_DEBUG("bta_av_co_audio_setconfig p_codec_info[%x:%x:%x:%x:%x:%x:%x:%x]",
p_codec_info[1], p_codec_info[2], p_codec_info[3],
p_codec_info[4], p_codec_info[5], p_codec_info[6]);
p_codec_info[4], p_codec_info[5], p_codec_info[6],
p_codec_info[7], p_codec_info[8]);
APPL_TRACE_DEBUG("num_protect:0x%02x protect_info:0x%02x%02x%02x",
num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]);
@@ -778,6 +945,9 @@ void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
}
#endif
if (status == A2D_SUCCESS) {
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_lock();
if (AVDT_TSEP_SNK == t_local_sep) {
codec_cfg_status = bta_av_co_audio_sink_supports_config(codec_type, p_codec_info);
APPL_TRACE_DEBUG(" Peer is A2DP SRC ");
@@ -789,9 +959,6 @@ void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
/* Check if codec configuration is supported */
if (codec_cfg_status == A2D_SUCCESS) {
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_lock();
/* Check if the configuration matches the current codec config */
switch (bta_av_co_cb.codec_cfg.id) {
case BTC_AV_CODEC_SBC:
@@ -816,19 +983,36 @@ void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
recfg_needed = FALSE;
}
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
if ((codec_type != BTA_AV_CODEC_M24) || memcmp(p_codec_info, bta_av_co_cb.codec_cfg.info, 9)) {
recfg_needed = TRUE;
} else if ((num_protect == 1) && (!bta_av_co_cb.cp.active)) {
recfg_needed = TRUE;
}
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_M24;
memcpy(bta_av_co_cb.codec_cfg_setconfig.info, p_codec_info, AVDT_CODEC_SIZE);
if (AVDT_TSEP_SNK == t_local_sep) {
/* If Peer is SRC, and our cfg subset matches with what is requested by peer, then
just accept what peer wants */
memcpy(bta_av_co_cb.codec_cfg.info, p_codec_info, AVDT_CODEC_SIZE);
recfg_needed = FALSE;
}
break;
#endif
default:
APPL_TRACE_ERROR("bta_av_co_audio_setconfig unsupported cid %d", bta_av_co_cb.codec_cfg.id);
recfg_needed = TRUE;
break;
}
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_unlock();
} else {
category = AVDT_ASC_CODEC;
status = A2D_FAIL;
}
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_unlock();
}
if (status != A2D_SUCCESS) {
@@ -912,6 +1096,11 @@ void bta_av_co_audio_close(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type, UINT16 m
/* reset remote preference through setconfig */
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
/* reset local preferred media codec configuration */
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.cur_seid = 0;
#endif
}
/*******************************************************************************
@@ -994,6 +1183,12 @@ void *bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, UINT32 *p_len,
bta_av_sbc_bld_hdr(p_buf, p_buf->layer_specific);
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTA_AV_CODEC_M24:
*p_timestamp = *((UINT32 *)(p_buf + 1));
break;
#endif
default:
APPL_TRACE_ERROR("bta_av_co_audio_src_data_path Unsupported codec type (%d)", codec_type);
@@ -1087,6 +1282,25 @@ static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UIN
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF],
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24: {
UINT32 cfg_bit_rate;
UINT32 cap_bit_rate;
/* LOSC(1) + media(1) + codec(1) + CIE(6) = 9 octets */
memcpy(p_codec_cfg, bta_av_co_cb.codec_cfg.info, BTA_AV_CO_M24_INFO_LEN);
cfg_bit_rate = bta_av_m24_br_info(p_codec_cfg);
cap_bit_rate = bta_av_m24_br_info(p_codec_caps);
bta_av_m24_set_br_info(p_codec_cfg, BTA_AV_CO_MIN(cfg_bit_rate, cap_bit_rate));
p_codec_cfg[BTA_AV_CO_M24_VBR_BR1_OFF] &= (p_codec_caps[BTA_AV_CO_M24_VBR_BR1_OFF] & A2D_M24_IE_VBR_MSK) | ~A2D_M24_IE_VBR_MSK;
APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config bit rate: 0x%u(cfg_br: %u/cap_br: %u, min: %u)", bta_av_m24_br_info(p_codec_cfg), cfg_bit_rate, cap_bit_rate, BTA_AV_CO_MIN(cfg_bit_rate, cap_bit_rate));
break;
}
#endif
default:
APPL_TRACE_ERROR("bta_av_co_audio_codec_build_config: unsupported codec id %d", bta_av_co_cb.codec_cfg.id);
return FALSE;
@@ -1129,6 +1343,20 @@ static BOOLEAN bta_av_co_audio_codec_cfg_matches_caps(UINT8 codec_id, const UINT
}
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24: {
APPL_TRACE_EVENT("bta_av_co_audio_codec_cfg_matches_caps bit rate: cap/cfg %u/%u",
bta_av_m24_br_info(p_codec_caps), bta_av_m24_br_info(p_codec_cfg));
if ((~p_codec_caps[BTA_AV_CO_M24_OBJ_DRC_OFF] & p_codec_cfg[BTA_AV_CO_M24_OBJ_DRC_OFF]) ||
(~p_codec_caps[BTA_AV_CO_M24_SAMP_FREQ1_OFF] & p_codec_cfg[BTA_AV_CO_M24_SAMP_FREQ1_OFF]) ||
(~p_codec_caps[BTA_AV_CO_M24_FREQ2_CHAN_OFF] & p_codec_cfg[BTA_AV_CO_M24_FREQ2_CHAN_OFF])) {
return FALSE;
}
break;
}
#endif
default:
APPL_TRACE_ERROR("bta_av_co_audio_codec_cfg_matches_caps: unsupported codec id %d", codec_id);
@@ -1265,31 +1493,44 @@ static BOOLEAN bta_av_co_audio_sink_supports_cp(const tBTA_AV_CO_SINK *p_sink)
static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_snk_index)
{
int index;
UINT8 codec_type;
FUNC_TRACE();
/* Configure the codec type to look for */
codec_type = bta_av_co_cb.codec_cfg.id;
#if (BTC_AV_EXT_CODEC == TRUE)
while (bta_av_co_cb.cur_seid < BTA_AV_MAX_SEPS) {
#endif
for (index = 0; index < p_peer->num_sup_snks; index++) {
if (p_peer->snks[index].codec_type == bta_av_co_cb.codec_cfg.id) {
switch (bta_av_co_cb.codec_cfg.id) {
case BTC_AV_CODEC_SBC:
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
#endif
if (p_snk_index) {
*p_snk_index = index;
}
if (bta_av_co_audio_codec_match(p_peer->snks[index].codec_caps)) {
return TRUE;
}
break;
for (index = 0; index < p_peer->num_sup_snks; index++) {
if (p_peer->snks[index].codec_type == codec_type) {
switch (bta_av_co_cb.codec_cfg.id) {
case BTC_AV_CODEC_SBC:
if (p_snk_index) {
*p_snk_index = index;
default:
APPL_TRACE_ERROR("bta_av_co_audio_peer_supports_codec: unsupported codec id %d", bta_av_co_cb.codec_cfg.id);
return FALSE;
break;
}
return bta_av_co_audio_codec_match(p_peer->snks[index].codec_caps);
break;
default:
APPL_TRACE_ERROR("bta_av_co_audio_peer_supports_codec: unsupported codec id %d", bta_av_co_cb.codec_cfg.id);
return FALSE;
break;
}
}
#if (BTC_AV_EXT_CODEC == TRUE)
if (bta_av_co_cb.cur_seid + 1 >= BTA_AV_MAX_SEPS) {
bta_av_co_cb.cur_seid = 0;
bta_av_co_audio_codec_reset();
return FALSE;
} else {
bta_av_co_cb.cur_seid++;
bta_av_co_audio_codec_reset();
}
}
#endif
return FALSE;
}
@@ -1305,34 +1546,71 @@ static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT
static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index)
{
int index;
UINT8 codec_type;
FUNC_TRACE();
/* Configure the codec type to look for */
codec_type = bta_av_co_cb.codec_cfg.id;
#if (BTC_AV_EXT_CODEC == TRUE)
while (bta_av_co_cb.cur_seid < BTA_AV_MAX_SEPS) {
bta_av_co_audio_codec_reset();
for (index = 0; index < p_peer->num_sup_srcs; index++) {
if (p_peer->srcs[index].codec_type == bta_av_co_cb.codec_cfg.id) {
switch (bta_av_co_cb.codec_cfg.id) {
case BTC_AV_CODEC_SBC: {
tA2D_SBC_CIE sbc_cap_cie;
if (p_src_index) {
*p_src_index = index;
}
if (A2D_ParsSbcInfo(&sbc_cap_cie, bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].info, TRUE) != A2D_SUCCESS) {
APPL_TRACE_ERROR("peer_src_supports_codec ParsSbcInfo failed");
return FALSE;
}
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps, &sbc_cap_cie)) {
return TRUE;
}
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24: {
tA2D_M24_CIE m24_cap_cie;
if (p_src_index) {
*p_src_index = index;
}
if (A2D_ParsM24Info(&m24_cap_cie, bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].info, TRUE) != A2D_SUCCESS) {
APPL_TRACE_ERROR("peer_src_supports_codec ParsM24Info failed");
return FALSE;
}
if (bta_av_m24_cap_matches_cap(p_peer->srcs[index].codec_caps, bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].info) == A2D_SUCCESS) {
return TRUE;
}
break;
}
#endif
default:
APPL_TRACE_ERROR("peer_src_supports_codec: unsupported codec id %d",
bta_av_co_cb.codec_cfg.id);
return FALSE;
break;
}
}
}
bta_av_co_cb.cur_seid++;
}
bta_av_co_cb.cur_seid = 0;
bta_av_co_audio_codec_reset();
return FALSE;
#else
for (index = 0; index < p_peer->num_sup_srcs; index++) {
if (p_peer->srcs[index].codec_type == codec_type) {
if (p_peer->srcs[index].codec_type == bta_av_co_cb.codec_cfg.id) {
switch (bta_av_co_cb.codec_cfg.id) {
case BTC_AV_CODEC_SBC:
if (p_src_index) {
*p_src_index = index;
}
#if (BTC_AV_EXT_CODEC == TRUE)
tA2D_SBC_CIE cap_cie;
if (A2D_ParsSbcInfo(&cap_cie, bta_av_co_cb.codec_caps.info, TRUE) != A2D_SUCCESS) {
return FALSE;
}
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps, &cap_cie)) {
return TRUE;
}
#else
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps,
(tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps)) {
return TRUE;
}
#endif
break;
default:
@@ -1343,6 +1621,7 @@ static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer,
}
}
}
#endif
return FALSE;
}
@@ -1359,17 +1638,44 @@ static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8
{
FUNC_TRACE();
UINT8 status = A2D_BAD_CODEC_TYPE;
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 index;
#endif
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_EXT_CODEC == TRUE)
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps.info);
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
if (bta_av_co_cb.codec_caps[index].id == BTA_AV_CODEC_SBC) {
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps[index].info);
if (status == A2D_SUCCESS) {
bta_av_co_cb.cur_seid = index;
bta_av_co_audio_codec_reset();
return status;
}
}
}
#else
status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps);
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_M24:
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
if (bta_av_co_cb.codec_caps[index].id == BTA_AV_CODEC_M24) {
status = bta_av_m24_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps[index].info);
if (status == A2D_SUCCESS) {
bta_av_co_cb.cur_seid = index;
bta_av_co_audio_codec_reset();
return status;
}
}
}
#else
status = A2D_NS_CODEC_TYPE;
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_ATRAC:
status = A2D_NS_CODEC_TYPE;
APPL_TRACE_ERROR("bta_av_co_audio_sink_supports_config unsupported codec type %d", codec_type);
@@ -1394,17 +1700,44 @@ static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8
{
FUNC_TRACE();
UINT8 status = A2D_BAD_CODEC_TYPE;
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 index;
#endif
switch (codec_type) {
case BTA_AV_CODEC_SBC:
#if (BTC_AV_EXT_CODEC == TRUE)
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps.info);
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
if (bta_av_co_cb.codec_caps[index].id == BTA_AV_CODEC_SBC) {
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps[index].info);
if (status == A2D_SUCCESS) {
bta_av_co_cb.cur_seid = index;
bta_av_co_audio_codec_reset();
break;
}
}
}
#else
status = bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_caps);
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_M24:
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
if (bta_av_co_cb.codec_caps[index].id == BTA_AV_CODEC_M24) {
status = bta_av_m24_cfg_in_external_codec_cap((UINT8 *)p_codec_cfg, (UINT8 *)bta_av_co_cb.codec_caps[index].info);
if (status == A2D_SUCCESS) {
bta_av_co_cb.cur_seid = index;
bta_av_co_audio_codec_reset();
break;
}
}
}
#else
status = A2D_NS_CODEC_TYPE;
#endif
break;
case BTA_AV_CODEC_M12:
case BTA_AV_CODEC_ATRAC:
status = A2D_NS_CODEC_TYPE;
APPL_TRACE_ERROR("bta_av_co_audio_media_supports_config unsupported codec type %d", codec_type);
@@ -1428,9 +1761,33 @@ static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8
static BOOLEAN bta_av_co_audio_src_supports_pref_cfg(void)
{
#if (BTC_AV_EXT_CODEC == TRUE)
return (bta_av_co_cb.codec_pref_cfg.id == bta_av_co_cb.codec_caps.id &&
bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
(UINT8 *)&bta_av_co_cb.codec_caps.info) == A2D_SUCCESS);
UINT8 index;
UINT8 status = A2D_FAIL;
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
if (bta_av_co_cb.codec_pref_cfg.id != bta_av_co_cb.codec_caps[index].id) {
continue;
}
switch (bta_av_co_cb.codec_pref_cfg.id) {
case BTC_AV_CODEC_SBC:
status = bta_av_sbc_cfg_in_external_codec_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
(UINT8 *)&bta_av_co_cb.codec_caps[index].info);
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
status = bta_av_m24_cfg_in_external_codec_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
(UINT8 *)&bta_av_co_cb.codec_caps[index].info);
break;
#endif
default:
return FALSE;
}
if (status == A2D_SUCCESS) {
bta_av_co_cb.cur_seid = index;
return TRUE;
}
}
return FALSE;
#else
return (bta_av_co_cb.codec_pref_cfg.id == BTC_AV_CODEC_SBC &&
bta_av_sbc_cfg_in_cap((UINT8 *)&bta_av_co_cb.codec_pref_cfg.info,
@@ -1560,11 +1917,15 @@ BOOLEAN bta_av_co_audio_set_pref_mcc(tBTA_AV_HNDL hndl, tBTC_AV_CODEC_INFO *pref
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL) {
APPL_TRACE_ERROR("bta_av_co_audio_set_pref_mcc could not find peer entry for handle 0x%x", hndl);
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
memset(bta_av_co_cb.codec_pref_cfg.info, 0, AVDT_CODEC_SIZE);
return FALSE;
}
if (!p_peer->opened) {
APPL_TRACE_WARNING("bta_av_co_audio_set_pref_mcc connection 0x%x is not opened", hndl);
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
memset(bta_av_co_cb.codec_pref_cfg.info, 0, AVDT_CODEC_SIZE);
return FALSE;
}
@@ -1758,6 +2119,9 @@ void bta_av_co_audio_codec_reset(void)
APPL_TRACE_DEBUG("bta_av_co_audio_codec_reset preferred codec configuration is not set");
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
#endif
case BTC_AV_CODEC_SBC: {
if (bta_av_co_audio_src_supports_pref_cfg()) {
pref_cfg_supported = TRUE;
@@ -1777,8 +2141,12 @@ void bta_av_co_audio_codec_reset(void)
bta_av_co_cb.codec_cfg = bta_av_co_cb.codec_pref_cfg;
} else {
#if (BTC_AV_EXT_CODEC == TRUE)
bta_av_co_cb.codec_cfg.id = bta_av_co_cb.codec_caps.id;
bta_av_build_src_cfg(bta_av_co_cb.codec_cfg.info, bta_av_co_cb.codec_caps.info);
if (bta_av_co_cb.cur_seid >= BTA_AV_MAX_SEPS) {
APPL_TRACE_WARNING("bta_av_co_audio_codec_reset OOB cur_seid: %d", bta_av_co_cb.cur_seid);
bta_av_co_cb.cur_seid = 0;
}
bta_av_co_cb.codec_cfg.id = bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].id;
bta_av_build_src_cfg(bta_av_co_cb.codec_cfg.info, bta_av_co_cb.codec_caps[bta_av_co_cb.cur_seid].info);
#else
/* Reset the current configuration to SBC */
bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC;
@@ -1989,7 +2357,13 @@ void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps)
memset(&bta_av_co_cb, 0, sizeof(bta_av_co_cb));
if (codec_caps) {
memcpy(&bta_av_co_cb.codec_caps, codec_caps, sizeof(tBTC_AV_CODEC_INFO));
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 index;
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
memcpy(&bta_av_co_cb.codec_caps[index], codec_caps + index, sizeof(tBTC_AV_CODEC_INFO));
}
bta_av_co_cb.cur_seid = 0;
#endif
}
bta_av_co_cb.codec_pref_cfg.id = BTC_AV_CODEC_NONE;
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
@@ -2030,11 +2404,15 @@ BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl)
for (index = 0; index < p_peer->num_sup_snks; index++) {
p_sink = &p_peer->snks[index];
if (p_sink->codec_type == A2D_MEDIA_CT_SBC) {
if (p_sink->codec_type == A2D_MEDIA_CT_SBC
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
|| p_sink->codec_type == A2D_MEDIA_CT_M24
#endif
) {
return bta_av_co_audio_sink_has_scmst(p_sink);
}
}
APPL_TRACE_ERROR("bta_av_co_peer_cp_supported did not find SBC sink");
APPL_TRACE_ERROR("bta_av_co_peer_cp_supported did not find SBC/AAC sink");
return FALSE;
}
@@ -2068,7 +2446,7 @@ BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max)
**
** Function bta_av_co_get_peer_sink_caps
**
** Description Get the sink codec capabilities of the peer
** Description Get currently selected sink codec capabilities of the peer
**
** Returns TRUE if sink capabilities are available, FALSE otherwise
**
@@ -2108,6 +2486,30 @@ BOOLEAN bta_av_co_get_peer_sink_caps(tBTA_AV_HNDL hndl, UINT8 *p_codec_caps, UIN
return TRUE;
}
/*******************************************************************************
**
** Function bta_av_co_get_cur_codec_type
**
** Description Get current codec type
**
** Returns codec type
**
*******************************************************************************/
UINT8 bta_av_co_get_cur_codec_type(void)
{
UINT8 codec_id = BTC_AV_CODEC_NONE;
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_lock();
codec_id = bta_av_co_cb.codec_cfg.id;
/* Protect access to bta_av_co_cb.codec_cfg */
osi_mutex_global_unlock();
return codec_id;
}
/* the call out functions for audio stream */
const tBTA_AV_CO_FUNCTS bta_av_a2d_cos = {
bta_av_co_audio_init,
@@ -0,0 +1,530 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* MPEG-4 LATM AudioMuxElement parsing for A2DP AAC (MPEG-2,4): skip StreamMuxConfig
* when useSameStreamMux is 0, then extract one raw AAC frame (payloadLengthInfo +
* RawDataBlock). Used by the BTC A2DP sink external-codec path.
*/
#include <stddef.h>
#include <string.h>
#include "btc_a2dp_latm_raw.h"
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
/*
* ISO/IEC 14496-3 (LATM / AudioSpecificConfig / GASpecificConfig) — numeric literals
* used only where needed for parsing StreamMuxConfig; names reflect MPEG-4 audio tables.
*/
#define BTC_LATM_MPEG4_AUDIO_SPECIFIC_CFG_BITS_MAX (8192U)
#define BTC_LATM_MPEG4_OT_EXTENDED_ESCAPE (31U) /* object_type escape → +6 bits */
#define BTC_LATM_MPEG4_OT_EXTENDED_BASE (32U)
#define BTC_LATM_MPEG4_OT_AAC_MAIN (1U)
#define BTC_LATM_MPEG4_OT_AAC_LC (2U)
#define BTC_LATM_MPEG4_OT_AAC_SSR (3U)
#define BTC_LATM_MPEG4_OT_AAC_LTP (4U)
#define BTC_LATM_MPEG4_OT_AAC_SCALABLE (6U)
#define BTC_LATM_MPEG4_OT_TWINVQ (7U)
#define BTC_LATM_MPEG4_OT_ER_AAC_LC (17U)
#define BTC_LATM_MPEG4_OT_ER_AAC_LTP (19U)
#define BTC_LATM_MPEG4_OT_ER_AAC_SCALABLE (20U)
#define BTC_LATM_MPEG4_OT_ER_TWINVQ (21U)
#define BTC_LATM_MPEG4_OT_ER_BSAC (22U)
#define BTC_LATM_MPEG4_OT_ER_AAC_LD (23U)
#define BTC_LATM_MPEG4_OT_AAC_HE_SBR (5U) /* HE-AAC dual-rate wrapper */
#define BTC_LATM_MPEG4_OT_AAC_HE_PS_WRAPPER (29U) /* HE-AAC v2 style wrapper */
#define BTC_LATM_SAMPLING_FREQ_INDEX_ESCAPE (15U) /* explicit 24-bit Hz follows */
#define BTC_LATM_MPEG4_SYNC_EXT_SYNCWORD (0x2b7U)
#define BTC_LATM_MPEG4_SYNC_EXT_TAG_BITS (11U) /* syncExtensionType tag width */
#define BTC_LATM_MPEG4_SYNC_EXT_TYPE_SBR (5U)
#define BTC_LATM_MPEG4_PS_EXT_SYNCWORD (0x548U)
#define BTC_LATM_PAYLOAD_LEN_ESC_BYTE (255U)
#define BTC_LATM_PAYLOAD_LEN_ESC_MAX_LOOPS (32U)
#define BTC_LATM_STREAM_MUX_ALL_FRAMING_AND_SUBFR_BITS (7U) /* 1 + 6 bits */
#define BTC_LATM_MUX_PROGRAM_IDX_FIRST (0U)
#define BTC_LATM_MUX_LAYER_IDX_FIRST (0U)
#define BTC_LATM_SYNC_EXT_SCAN_MIN_REMAINING_BITS (15U)
#define BTC_LATM_PS_EXT_PROBE_MIN_REMAINING_BITS (11U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_PAYLOAD (0U)
#define BTC_LATM_FRAME_LENGTH_TYPE_VARIABLE_PAYLOAD (1U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_3 (3U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_4 (4U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_5 (5U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_6 (6U)
#define BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_7 (7U)
typedef struct {
const uint8_t *buf;
size_t byte_len;
unsigned bit_pos;
} btc_a2dp_latm_br_t;
static inline unsigned btc_a2dp_latm_bits_total(const btc_a2dp_latm_br_t *r)
{
return (unsigned)(r->byte_len * 8);
}
static inline int btc_a2dp_latm_get1(btc_a2dp_latm_br_t *r)
{
if (r->bit_pos >= btc_a2dp_latm_bits_total(r)) {
return -1;
}
unsigned byte = r->bit_pos / 8U;
unsigned bit_in_byte = 7U - (r->bit_pos % 8U);
int v = (int)((r->buf[byte] >> bit_in_byte) & 1U);
r->bit_pos++;
return v;
}
static int btc_a2dp_latm_get_bits(btc_a2dp_latm_br_t *r, unsigned n, unsigned *out)
{
unsigned v = 0;
for (unsigned i = 0; i < n; i++) {
int b = btc_a2dp_latm_get1(r);
if (b < 0) {
return -1;
}
v = (v << 1) | (unsigned)b;
}
*out = v;
return 0;
}
/* LATM value: 2-bit length L, then (L+1)*8 bits payload (ISO / common mux). */
static int btc_a2dp_latm_get_value(btc_a2dp_latm_br_t *r, unsigned *out_value)
{
unsigned l = 0;
if (btc_a2dp_latm_get_bits(r, 2, &l) != 0) {
return -1;
}
unsigned nbytes = l + 1U;
unsigned acc = 0;
for (unsigned i = 0; i < nbytes * 8U; i++) {
int b = btc_a2dp_latm_get1(r);
if (b < 0) {
return -1;
}
acc = (acc << 1) | (unsigned)b;
}
*out_value = acc;
return 0;
}
static int btc_a2dp_latm_skip_bits(btc_a2dp_latm_br_t *r, unsigned n)
{
for (unsigned i = 0; i < n; i++) {
if (btc_a2dp_latm_get1(r) < 0) {
return -1;
}
}
return 0;
}
/* Skip AudioSpecificConfig / GASpecific for common AAC-LC and typical extensions. */
static int btc_a2dp_latm_skip_audio_specific_config(btc_a2dp_latm_br_t *r)
{
unsigned object_type = 0;
if (btc_a2dp_latm_get_bits(r, 5, &object_type) != 0) {
return -1;
}
if (object_type == BTC_LATM_MPEG4_OT_EXTENDED_ESCAPE) {
unsigned ext = 0;
if (btc_a2dp_latm_get_bits(r, 6, &ext) != 0) {
return -1;
}
object_type = BTC_LATM_MPEG4_OT_EXTENDED_BASE + ext;
}
unsigned sf_index = 0;
if (btc_a2dp_latm_get_bits(r, 4, &sf_index) != 0) {
return -1;
}
if (sf_index == BTC_LATM_SAMPLING_FREQ_INDEX_ESCAPE) {
if (btc_a2dp_latm_skip_bits(r, 24) != 0) {
return -1;
}
}
if (btc_a2dp_latm_skip_bits(r, 4) != 0) { /* channelConfiguration */
return -1;
}
/* HE-AAC v1/v2 dual-rate wrapper (AAC scalable / PS wrapper object types). */
if (object_type == BTC_LATM_MPEG4_OT_AAC_HE_SBR || object_type == BTC_LATM_MPEG4_OT_AAC_HE_PS_WRAPPER) {
unsigned ext_sf = 0;
if (btc_a2dp_latm_get_bits(r, 4, &ext_sf) != 0) {
return -1;
}
if (ext_sf == BTC_LATM_SAMPLING_FREQ_INDEX_ESCAPE) {
if (btc_a2dp_latm_skip_bits(r, 24) != 0) {
return -1;
}
}
if (btc_a2dp_latm_get_bits(r, 5, &object_type) != 0) {
return -1;
}
if (object_type == BTC_LATM_MPEG4_OT_EXTENDED_ESCAPE) {
unsigned ext = 0;
if (btc_a2dp_latm_get_bits(r, 6, &ext) != 0) {
return -1;
}
object_type = BTC_LATM_MPEG4_OT_EXTENDED_BASE + ext;
}
if (object_type == BTC_LATM_MPEG4_OT_ER_BSAC) {
if (btc_a2dp_latm_skip_bits(r, 4) != 0) {
return -1;
}
}
}
/* GASpecificConfig for core object types (incl. AAC LC). */
switch (object_type) {
case BTC_LATM_MPEG4_OT_AAC_MAIN:
case BTC_LATM_MPEG4_OT_AAC_LC:
case BTC_LATM_MPEG4_OT_AAC_SSR:
case BTC_LATM_MPEG4_OT_AAC_LTP:
case BTC_LATM_MPEG4_OT_AAC_SCALABLE:
case BTC_LATM_MPEG4_OT_TWINVQ:
case BTC_LATM_MPEG4_OT_ER_AAC_LC:
case BTC_LATM_MPEG4_OT_ER_AAC_LTP:
case BTC_LATM_MPEG4_OT_ER_AAC_SCALABLE:
case BTC_LATM_MPEG4_OT_ER_TWINVQ:
case BTC_LATM_MPEG4_OT_ER_BSAC:
case BTC_LATM_MPEG4_OT_ER_AAC_LD:
if (btc_a2dp_latm_skip_bits(r, 1) != 0) { /* frameLengthFlag */
return -1;
}
{
unsigned dep = 0;
if (btc_a2dp_latm_get_bits(r, 1, &dep) != 0) {
return -1;
}
if (dep == 0U) {
if (btc_a2dp_latm_skip_bits(r, 14) != 0) { /* coreCoderDelay */
return -1;
}
}
}
{
unsigned ext_flag = 0;
if (btc_a2dp_latm_get_bits(r, 1, &ext_flag) != 0) {
return -1;
}
if (ext_flag != 0U) {
/* Uncommon for A2DP; skip conservatively until alignment risk. */
return -1;
}
}
break;
default:
/* Fall through to generic sync-extension scan. */
break;
}
/* Sync extension and trailing bits (similar role to ff_mpeg4audio_get_config_gb). */
while (btc_a2dp_latm_bits_total(r) - r->bit_pos > BTC_LATM_SYNC_EXT_SCAN_MIN_REMAINING_BITS) {
unsigned show = 0;
unsigned save = r->bit_pos;
if (btc_a2dp_latm_get_bits(r, BTC_LATM_MPEG4_SYNC_EXT_TAG_BITS, &show) != 0) {
r->bit_pos = save;
break;
}
if (show == BTC_LATM_MPEG4_SYNC_EXT_SYNCWORD) {
unsigned ext_type = 0;
if (btc_a2dp_latm_get_bits(r, 5, &ext_type) != 0) {
return -1;
}
if (ext_type == BTC_LATM_MPEG4_SYNC_EXT_TYPE_SBR) {
if (btc_a2dp_latm_skip_bits(r, 1) != 0) {
return -1;
}
unsigned sbr_rate = 0;
if (btc_a2dp_latm_get_bits(r, 4, &sbr_rate) != 0) {
return -1;
}
if (sbr_rate == BTC_LATM_SAMPLING_FREQ_INDEX_ESCAPE) {
if (btc_a2dp_latm_skip_bits(r, 24) != 0) {
return -1;
}
}
if (btc_a2dp_latm_bits_total(r) - r->bit_pos > BTC_LATM_PS_EXT_PROBE_MIN_REMAINING_BITS) {
unsigned show2 = 0;
unsigned s2 = r->bit_pos;
if (btc_a2dp_latm_get_bits(r, BTC_LATM_MPEG4_SYNC_EXT_TAG_BITS, &show2) != 0) {
r->bit_pos = s2;
break;
}
if (show2 == BTC_LATM_MPEG4_PS_EXT_SYNCWORD) {
if (btc_a2dp_latm_skip_bits(r, 1) != 0) {
return -1;
}
} else {
r->bit_pos = s2;
}
}
} else {
r->bit_pos = save;
if (btc_a2dp_latm_get1(r) < 0) {
break;
}
}
} else {
r->bit_pos = save;
if (btc_a2dp_latm_get1(r) < 0) {
break;
}
}
}
return 0;
}
static int btc_a2dp_latm_skip_stream_mux_config(btc_a2dp_latm_br_t *r, unsigned *audio_mux_version_a, unsigned *frame_length_type_o)
{
unsigned audio_mux_version = 0;
if (btc_a2dp_latm_get_bits(r, 1, &audio_mux_version) != 0) {
return -1;
}
*audio_mux_version_a = 0;
if (audio_mux_version != 0U) {
if (btc_a2dp_latm_get_bits(r, 1, audio_mux_version_a) != 0) {
return -1;
}
}
if (*audio_mux_version_a != 0U) {
/* audioMuxVersionA == 1 path not supported in this example stripper. */
return -1;
}
if (audio_mux_version != 0U) {
unsigned dummy = 0;
if (btc_a2dp_latm_get_value(r, &dummy) != 0) {
return -1;
}
}
if (btc_a2dp_latm_skip_bits(r, BTC_LATM_STREAM_MUX_ALL_FRAMING_AND_SUBFR_BITS) != 0) {
/* allStreamsSameTimeFraming + numSubFrames */
return -1;
}
unsigned num_programs = 0;
if (btc_a2dp_latm_get_bits(r, 4, &num_programs) != 0) {
return -1;
}
if (num_programs != BTC_LATM_MUX_PROGRAM_IDX_FIRST) {
return -1;
}
unsigned num_layer = 0;
if (btc_a2dp_latm_get_bits(r, 3, &num_layer) != 0) {
return -1;
}
if (num_layer != BTC_LATM_MUX_LAYER_IDX_FIRST) {
return -1;
}
if (audio_mux_version == 0U) {
if (btc_a2dp_latm_skip_audio_specific_config(r) != 0) {
return -1;
}
} else {
unsigned asc_bits = 0;
if (btc_a2dp_latm_get_value(r, &asc_bits) != 0) {
return -1;
}
if (asc_bits > BTC_LATM_MPEG4_AUDIO_SPECIFIC_CFG_BITS_MAX) {
return -1;
}
if (btc_a2dp_latm_skip_bits(r, asc_bits) != 0) {
return -1;
}
}
unsigned frame_length_type = 0;
if (btc_a2dp_latm_get_bits(r, 3, &frame_length_type) != 0) {
return -1;
}
if (frame_length_type_o != NULL) {
*frame_length_type_o = frame_length_type;
}
switch (frame_length_type) {
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_PAYLOAD:
if (btc_a2dp_latm_skip_bits(r, 8) != 0) {
return -1;
}
break;
case BTC_LATM_FRAME_LENGTH_TYPE_VARIABLE_PAYLOAD:
if (btc_a2dp_latm_skip_bits(r, 9) != 0) {
return -1;
}
break;
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_3:
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_4:
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_5:
if (btc_a2dp_latm_skip_bits(r, 6) != 0) {
return -1;
}
break;
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_6:
case BTC_LATM_FRAME_LENGTH_TYPE_FIXED_IDX_7:
if (btc_a2dp_latm_skip_bits(r, 1) != 0) {
return -1;
}
break;
default:
break;
}
unsigned other_data = 0;
if (btc_a2dp_latm_get_bits(r, 1, &other_data) != 0) {
return -1;
}
if (other_data != 0U) {
if (audio_mux_version != 0U) {
unsigned bits = 0;
if (btc_a2dp_latm_get_value(r, &bits) != 0) {
return -1;
}
if (bits > BTC_LATM_MPEG4_AUDIO_SPECIFIC_CFG_BITS_MAX) {
return -1;
}
if (btc_a2dp_latm_skip_bits(r, bits) != 0) {
return -1;
}
} else {
unsigned esc_u = 0;
do {
if (btc_a2dp_latm_bits_total(r) - r->bit_pos < 9U) {
return -1;
}
if (btc_a2dp_latm_get_bits(r, 1, &esc_u) != 0) {
return -1;
}
if (btc_a2dp_latm_skip_bits(r, 8) != 0) {
return -1;
}
} while (esc_u != 0U);
}
}
unsigned crc = 0;
if (btc_a2dp_latm_get_bits(r, 1, &crc) != 0) {
return -1;
}
if (crc != 0U) {
if (btc_a2dp_latm_skip_bits(r, 8) != 0) {
return -1;
}
}
return 0;
}
static int btc_a2dp_latm_get_payload_length_type0(btc_a2dp_latm_br_t *r, unsigned *payload_len_bytes)
{
unsigned total = 0;
unsigned loops = 0;
while (1) {
unsigned tmp = 0;
if (btc_a2dp_latm_get_bits(r, 8, &tmp) != 0) {
return -1;
}
total += tmp;
loops++;
if (tmp != BTC_LATM_PAYLOAD_LEN_ESC_BYTE) {
break;
}
/* Defensive loop bound for malformed data. */
if (loops > BTC_LATM_PAYLOAD_LEN_ESC_MAX_LOOPS) {
return -1;
}
}
*payload_len_bytes = total;
return 0;
}
int btc_a2dp_latm_extract_raw_data_block(const uint8_t *in, size_t in_len,
uint8_t *out, size_t out_cap, size_t *out_len)
{
if (in == NULL || out == NULL || out_len == NULL || in_len == 0) {
return -1;
}
if (in == out) {
return -1;
}
btc_a2dp_latm_br_t r = { .buf = in, .byte_len = in_len, .bit_pos = 0 };
unsigned use_same = 0;
if (btc_a2dp_latm_get_bits(&r, 1, &use_same) != 0) {
return -1;
}
unsigned frame_length_type = 0;
if (use_same == 0U) {
unsigned audio_mux_version_a = 0;
btc_a2dp_latm_br_t mux = { .buf = in, .byte_len = in_len, .bit_pos = 1U };
if (btc_a2dp_latm_skip_stream_mux_config(&mux, &audio_mux_version_a, &frame_length_type) != 0) {
return -1;
}
r.bit_pos = mux.bit_pos;
} else {
/*
* useSameStreamMux=1 reuses previous StreamMuxConfig. In A2DP AAC-LATM
* streams this is typically frameLengthType=0.
*/
frame_length_type = BTC_LATM_FRAME_LENGTH_TYPE_FIXED_PAYLOAD;
}
if (frame_length_type != BTC_LATM_FRAME_LENGTH_TYPE_FIXED_PAYLOAD) {
return -1;
}
unsigned payload_len_bytes = 0;
if (btc_a2dp_latm_get_payload_length_type0(&r, &payload_len_bytes) != 0) {
return -1;
}
if (payload_len_bytes > out_cap) {
return -1;
}
unsigned payload_bits = payload_len_bytes * 8U;
if (btc_a2dp_latm_bits_total(&r) - r.bit_pos < payload_bits) {
return -1;
}
/* Payload may start mid-byte after StreamMuxConfig. */
if ((r.bit_pos % 8U) == 0U) {
const size_t off = (size_t)(r.bit_pos / 8U);
if (off + (size_t)payload_len_bytes > in_len) {
return -1;
}
memcpy(out, in + off, (size_t)payload_len_bytes);
r.bit_pos += payload_bits;
} else {
for (unsigned j = 0; j < payload_len_bytes; j++) {
unsigned v = 0;
if (btc_a2dp_latm_get_bits(&r, 8, &v) != 0) {
return -1;
}
out[j] = (uint8_t)v;
}
}
*out_len = payload_len_bytes;
return 0;
}
#endif /* #if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_CODEC_AAC_INCLUDED == TRUE) */
@@ -17,6 +17,7 @@
#include "bta/bta_av_api.h"
#include "bta/bta_av_ci.h"
#include "btc_av_co.h"
#include "btc_a2dp_latm_raw.h"
#include "btc_a2dp.h"
#include "btc_a2dp_control.h"
#include "btc_a2dp_sink.h"
@@ -29,6 +30,10 @@
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE)
#ifndef BTC_A2DP_LATM_RAW_SCRATCH_BYTES
#define BTC_A2DP_LATM_RAW_SCRATCH_BYTES 2048
#endif
#define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (25)
#define BTC_A2DP_SNK_DATA_QUEUE_IDX (1)
@@ -231,18 +236,12 @@ static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
{
UINT8 *sbc_start_frame;
int num_sbc_frames;
UINT32 sbc_frame_len;
UINT32 timestamp;
if (p_msg->len < 1) {
osi_free(p_msg);
return;
}
sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
num_sbc_frames = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
sbc_frame_len = (UINT32)p_msg->len - 1U;
UINT8 *payload = (UINT8 *)(p_msg + 1) + p_msg->offset;
UINT8 *frame_ptr = payload;
UINT32 frame_len = p_msg->len;
UINT16 num_frames = 0;
UINT32 timestamp;
UINT8 codec_id = bta_av_co_get_cur_codec_type();
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush) {
osi_free(p_msg);
@@ -267,11 +266,49 @@ static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = p_msg->layer_specific + 1;
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = false;
APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
switch (codec_id) {
case BTC_AV_CODEC_SBC:
if (p_msg->len < 1) {
APPL_TRACE_WARNING("%s: SBC packet too short (%d)", __func__, p_msg->len);
osi_free(p_msg);
return;
}
num_frames = payload[0] & 0x0F;
frame_ptr = payload + 1;
frame_len = p_msg->len - 1;
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24: {
uint8_t scratch[BTC_A2DP_LATM_RAW_SCRATCH_BYTES];
size_t raw_len = 0;
num_frames = 1;
frame_ptr = payload;
frame_len = p_msg->len;
if (p_msg->len > 0 &&
btc_a2dp_latm_extract_raw_data_block(payload, (size_t)p_msg->len, scratch,
sizeof(scratch), &raw_len) == 0 &&
raw_len > 0 && raw_len <= (size_t)p_msg->len && raw_len <= 0xffffU) {
memmove(payload, scratch, raw_len);
frame_ptr = payload;
frame_len = (UINT32)raw_len;
}
break;
}
#endif
default:
APPL_TRACE_WARNING("%s: unsupported codec type 0x%02x, drop", __func__, codec_id);
osi_free(p_msg);
return;
}
APPL_TRACE_DEBUG("codec 0x%02x, frames %d, frame_len %d", codec_id, num_frames, (int)frame_len);
memcpy(&timestamp, (UINT8 *)(p_msg + 1), sizeof(UINT32));
UINT16 conn_hdl = btc_a2d_conn_handle_get();
btc_a2d_audio_data_cb_to_app(conn_hdl, (uint8_t *)p_msg, sbc_start_frame, sbc_frame_len, num_sbc_frames, timestamp);
btc_a2d_audio_data_cb_to_app(conn_hdl, (uint8_t *)p_msg, frame_ptr, frame_len, num_frames, timestamp);
/* dont free p_msg here */
}
@@ -17,6 +17,7 @@
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "stack/btu.h"
#include "stack/a2d_m24.h"
#include "bta/bta_av_api.h"
#include "btc/btc_dm.h"
#include "btc/btc_common.h"
@@ -80,6 +81,8 @@ typedef enum {
#define BTC_AV_SBC_CIE_OFFSET 3
#define BTC_AV_SBC_CIE_LEN 4
#define BTC_AV_M24_CIE_OFFSET 3
#define BTC_AV_M24_CIE_LEN 6
/*****************************************************************************
** Local type definitions
@@ -94,7 +97,9 @@ typedef struct {
UINT8 flags;
tBTA_AV_EDR edr;
UINT8 peer_sep; /* sep type of peer device */
tBTC_AV_CODEC_INFO codec_caps;
#if (BTC_AV_EXT_CODEC == TRUE)
tBTC_AV_CODEC_INFO codec_caps[BTA_AV_MAX_SEPS];
#endif
#if BTC_AV_SRC_INCLUDED
tBTC_AV_CODEC_INFO pref_mcc; /* preferred media codec configuration */
osi_alarm_t *tle_av_open_on_rc;
@@ -183,6 +188,7 @@ static void btc_av_event_free_data(btc_msg_t *msg);
extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
extern tBTA_AVRC_CO_FUNCTS bta_avrc_cos;
extern tA2D_SBC_CIE btc_av_sbc_default_config;
extern tA2D_M24_CIE btc_av_m24_default_config;
/*****************************************************************************
** Local helper functions
******************************************************************************/
@@ -272,6 +278,20 @@ static void btc_initiate_av_open_tmr_hdlr(void *arg)
BTC_TRACE_ERROR("%s No connected RC peers", __FUNCTION__);
}
}
void btc_av_report_all_snk_codec_caps(tBTA_AV_HNDL hndl, esp_a2d_sep_mcc_t *sep_mcc, UINT8 sep_num)
{
esp_a2d_cb_param_t param;
if (sep_mcc == NULL || sep_num == 0) {
return;
}
memset(&param, 0, sizeof(param));
param.a2d_report_snk_all_codec_caps_stat.conn_hdl = hndl;
param.a2d_report_snk_all_codec_caps_stat.sep_mcc = sep_mcc;
param.a2d_report_snk_all_codec_caps_stat.sep_num = sep_num;
btc_a2d_cb_to_app(ESP_A2D_REPORT_SNK_ALL_CODEC_CAPS_EVT, &param);
}
#endif /* BTC_AV_SRC_INCLUDED */
/*****************************************************************************
@@ -548,7 +568,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
if (bta_av_co_get_peer_sink_caps(btc_av_cb.bta_handle, codec_caps, &codec_type)) {
switch (codec_type) {
/* Currently only supports SBC */
/* Currently only support SBC/AAC */
case BTA_AV_CODEC_SBC: {
param.a2d_report_snk_codec_caps_stat.conn_hdl = btc_av_cb.bta_handle;
param.a2d_report_snk_codec_caps_stat.mcc.type = ESP_A2D_MCT_SBC;
@@ -556,6 +576,15 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
btc_a2d_cb_to_app(ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT, &param);
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTA_AV_CODEC_M24: {
param.a2d_report_snk_codec_caps_stat.conn_hdl = btc_av_cb.bta_handle;
param.a2d_report_snk_codec_caps_stat.mcc.type = ESP_A2D_MCT_M24;
memcpy(&param.a2d_report_snk_codec_caps_stat.mcc.cie, (uint8_t *)codec_caps + BTC_AV_M24_CIE_OFFSET, BTC_AV_M24_CIE_LEN);
btc_a2d_cb_to_app(ESP_A2D_REPORT_SNK_CODEC_CAPS_EVT, &param);
break;
}
#endif
default: {
BTC_TRACE_WARNING("Unsupported codec type %d", codec_type);
@@ -863,6 +892,13 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
break;
}
case BTC_AV_CONFIG_EVT: {
param.audio_cfg.conn_hdl = btc_av_cb.bta_handle;
memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, &param);
} break;
case BTA_AV_RECONFIG_EVT:
if ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) &&
(p_av->reconfig.status == BTA_AV_SUCCESS)) {
@@ -1203,6 +1239,14 @@ static bt_status_t btc_av_init(int service_id)
memset((void *)btc_av_cb_ptr, 0, sizeof(btc_av_cb_t));
#endif
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 index;
for (index = 0; index < BTA_AV_MAX_SEPS; index++) {
btc_av_cb.codec_caps[index].id = BTC_AV_CODEC_NONE;
memset(btc_av_cb.codec_caps[index].info, 0, AVDT_CODEC_SIZE);
}
#endif
if (btc_av_cb.sm_handle == NULL) {
btc_av_cb.service_id = service_id;
bool stat = false;
@@ -1435,7 +1479,6 @@ static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
btc_sm_state_t state;
UINT8 que_len;
tA2D_STATUS a2d_status;
tA2D_SBC_CIE sbc_cie;
if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTC_MEDIA context */
state = btc_sm_get_state(btc_av_cb.sm_handle);
@@ -1454,23 +1497,50 @@ static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
/* send a command to BT Media Task */
btc_a2dp_sink_reset_decoder((UINT8 *)p_data);
#endif
UINT8 *p_codec = (UINT8 *)p_data;
/* currently only supports SBC */
a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
if (p_codec[BTA_AV_CODEC_TYPE_IDX] == A2D_MEDIA_CT_SBC) {
tA2D_SBC_CIE sbc_cie;
a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_codec, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_SBC;
memcpy(&arg.mcc.cie.sbc_info, (uint8_t *)p_data + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_SBC;
memcpy(&arg.mcc.cie.sbc_info, p_codec + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsSbcInfo fail:%d", a2d_status);
}
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
else if (p_codec[BTA_AV_CODEC_TYPE_IDX] == A2D_MEDIA_CT_M24) {
tA2D_M24_CIE m24_cie;
a2d_status = A2D_ParsM24Info(&m24_cie, p_codec, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_M24;
memcpy(&arg.mcc.cie.m24_info, p_codec + BTC_AV_M24_CIE_OFFSET, BTC_AV_M24_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsM24Info fail:%d", a2d_status);
}
}
#endif
else {
BTC_TRACE_ERROR("bte_av_media_sink_callback: unsup_codec 0x%02x", p_codec[BTA_AV_CODEC_TYPE_IDX]);
}
}
UNUSED(que_len);
@@ -1488,25 +1558,52 @@ static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
static void bte_av_media_source_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
tA2D_STATUS a2d_status;
tA2D_SBC_CIE sbc_cie;
if (event == BTA_AV_MEDIA_CFG_EVT) {
/* currently only supports SBC */
a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
UINT8 *p_codec = (UINT8 *)p_data;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
if (p_codec[BTA_AV_CODEC_TYPE_IDX] == A2D_MEDIA_CT_SBC) {
tA2D_SBC_CIE sbc_cie;
a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_codec, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_SBC;
memcpy(&arg.mcc.cie.sbc_info, (uint8_t *)p_data + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsSbcInfo fail:%d\n", a2d_status);
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_SBC;
memcpy(&arg.mcc.cie.sbc_info, p_codec + BTC_AV_SBC_CIE_OFFSET, BTC_AV_SBC_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsSbcInfo fail:%d", a2d_status);
}
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
else if (p_codec[BTA_AV_CODEC_TYPE_IDX] == A2D_MEDIA_CT_M24) {
tA2D_M24_CIE m24_cie;
a2d_status = A2D_ParsM24Info(&m24_cie, p_codec, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;
btc_av_args_t arg;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_CONFIG_EVT;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.mcc.type = ESP_A2D_MCT_M24;
memcpy(&arg.mcc.cie.m24_info, p_codec + BTC_AV_M24_CIE_OFFSET, BTC_AV_M24_CIE_LEN);
btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
} else {
BTC_TRACE_ERROR("A2D_ParsM24Info fail:%d", a2d_status);
}
}
#endif
else {
BTC_TRACE_WARNING("bte_av_media_source_callback: unsup_codec 0x%02x", p_codec[BTA_AV_CODEC_TYPE_IDX]);
}
}
}
@@ -1523,13 +1620,14 @@ static void bte_av_media_source_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_dat
tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void)
{
return &btc_av_cb.codec_caps;
return btc_av_cb.codec_caps;
}
static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc)
{
tBTA_AV_DATA_CBACK *p_data_cback = NULL;
esp_a2d_cb_param_t param;
tBTA_AV_CODEC codec_type = ESP_A2D_MCT_NON_A2DP;
param.a2d_sep_reg_stat.seid = seid;
if (btc_av_cb.sm_handle == NULL || btc_sm_get_state(btc_av_cb.sm_handle) != BTC_AV_STATE_IDLE) {
@@ -1539,24 +1637,40 @@ static void btc_av_reg_sep(uint8_t tsep, uint8_t seid, esp_a2d_mcc_t *mcc)
return;
}
if (mcc->type == ESP_A2D_MCT_SBC) {
A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, btc_av_cb.codec_caps.info);
/* overwrite sbc cie */
memcpy(btc_av_cb.codec_caps.info + A2D_SBC_CIE_OFF, &mcc->cie, A2D_SBC_CIE_LEN);
if (tsep == AVDT_TSEP_SNK) {
p_data_cback = bte_av_media_sink_callback;
}
else {
p_data_cback = bte_av_media_source_callback;
}
BTA_AvRegSEP(BTA_AV_CHNL_AUDIO, seid, tsep, BTA_AV_CODEC_SBC, btc_av_cb.codec_caps.info, p_data_cback);
if (tsep == AVDT_TSEP_SNK) {
p_data_cback = bte_av_media_sink_callback;
}
else {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_UNSUPPORTED;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: unsupported codec type %d", __func__, mcc->type);
p_data_cback = bte_av_media_source_callback;
}
switch (mcc->type) {
case ESP_A2D_MCT_SBC: {
codec_type = BTA_AV_CODEC_SBC;
btc_av_cb.codec_caps[seid].id = BTC_AV_CODEC_SBC;
A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, btc_av_cb.codec_caps[seid].info);
/* overwrite sbc cie */
memcpy(btc_av_cb.codec_caps[seid].info + A2D_SBC_CIE_OFF, &mcc->cie, A2D_SBC_CIE_LEN);
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case ESP_A2D_MCT_M24: {
codec_type = BTA_AV_CODEC_M24;
btc_av_cb.codec_caps[seid].id = BTC_AV_CODEC_M24;
A2D_BldM24Info(A2D_MEDIA_TYPE_AUDIO, (tA2D_M24_CIE *)&btc_av_m24_default_config, btc_av_cb.codec_caps[seid].info);
/* overwrite m24 cie */
memcpy(btc_av_cb.codec_caps[seid].info + A2D_M24_CIE_OFF, &mcc->cie, A2D_M24_CIE_LEN);
break;
}
#endif
default: {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_UNSUPPORTED;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: unsupported codec type %d", __func__, mcc->type);
return;
}
}
BTA_AvRegSEP(BTA_AV_CHNL_AUDIO, seid, tsep, codec_type, btc_av_cb.codec_caps[seid].info, p_data_cback);
}
#endif
@@ -2009,26 +2123,7 @@ static void btc_a2d_src_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_mcc_t
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, btc_av_cb.pref_mcc.info) == A2D_SUCCESS) {
/* overwrite sbc cie */
memcpy(btc_av_cb.pref_mcc.info + A2D_SBC_CIE_OFF, &pref_mcc->cie, A2D_SBC_CIE_LEN);
/* Note: Return value only indicates if the config is supported and reconfig is initiated.
* Actual success/failure will be reported via ESP_A2D_SRC_SET_PREF_MCC_EVT
* when BTA_AV_RECONFIG_EVT is received. */
if (!btc_a2dp_source_set_pref_mcc(conn_hdl, &btc_av_cb.pref_mcc)) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc bad params");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
} else {
/* Check if reconfig was actually initiated */
if (!bta_av_co_audio_pref_mcc_reconfig_initiated(conn_hdl)) {
/* Configuration unchanged, no reconfig needed.
* Preferred config is already in use, notify success immediately. */
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_SUCCESS;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc already active");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
}
goto set_pref_mcc;
} else {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_FAIL;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
@@ -2037,6 +2132,22 @@ static void btc_a2d_src_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_mcc_t
}
break;
}
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case ESP_A2D_MCT_M24: {
btc_av_cb.pref_mcc.id = BTC_AV_CODEC_M24;
if (A2D_BldM24Info(A2D_MEDIA_TYPE_AUDIO, (tA2D_M24_CIE *)&btc_av_m24_default_config, btc_av_cb.pref_mcc.info) == A2D_SUCCESS) {
/* overwrite m24 cie */
memcpy(btc_av_cb.pref_mcc.info + A2D_M24_CIE_OFF, &pref_mcc->cie, A2D_M24_CIE_LEN);
goto set_pref_mcc;
} else {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_FAIL;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc build M24 info failed");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
break;
}
#endif
default: {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
@@ -2046,6 +2157,28 @@ static void btc_a2d_src_set_pref_mcc(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_mcc_t
break;
}
}
return;
set_pref_mcc:
/* Note: Return value only indicates if the config is supported and reconfig is initiated.
* Actual success/failure will be reported via ESP_A2D_SRC_SET_PREF_MCC_EVT
* when BTA_AV_RECONFIG_EVT is received. */
if (!btc_a2dp_source_set_pref_mcc(conn_hdl, &btc_av_cb.pref_mcc)) {
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_UNSUPPORTED;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc bad params");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
} else {
/* Check if reconfig was actually initiated */
if (!bta_av_co_audio_pref_mcc_reconfig_initiated(conn_hdl)) {
/* Configuration unchanged, no reconfig needed.
* Preferred config is already in use, notify success immediately. */
param.a2d_set_pref_mcc_stat.set_status = ESP_BT_STATUS_SUCCESS;
param.a2d_set_pref_mcc_stat.conn_hdl = btc_av_cb.bta_handle;
BTC_TRACE_DEBUG("btc_a2d_src_set_pref_mcc already active");
btc_a2d_cb_to_app(ESP_A2D_SRC_SET_PREF_MCC_EVT, &param);
}
}
}
}
@@ -2070,7 +2203,23 @@ bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_aud
return BT_STATUS_FAIL;
}
BT_HDR *p_buf = (BT_HDR *)audio_buf;
assert(audio_buf->data - (UINT8 *)(p_buf + 1) >= BTC_AUDIO_BUFF_OFFSET);
uint8_t buf_offset = BTC_AUDIO_BUFF_OFFSET;
UINT8 codec_id = bta_av_co_get_cur_codec_type();
switch (codec_id) {
case BTC_AV_CODEC_SBC:
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
buf_offset -= BTA_AV_SBC_HDR_SIZE;
break;
#endif
default:
return BT_STATUS_UNSUPPORTED;
}
assert(audio_buf->data - (UINT8 *)(p_buf + 1) >= buf_offset);
/* since p_buf and audio_buf point to the same memory, backup those value before modify p_buf */
uint16_t number_frame = audio_buf->number_frame;
uint16_t data_len = audio_buf->data_len;
@@ -2113,7 +2262,8 @@ uint16_t btc_a2d_conn_handle_get(void)
void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data)
{
/* todo */
uint8_t buf_offset = BTC_AUDIO_BUFF_OFFSET;
if (pp_buff == NULL || pp_data == NULL) {
return;
}
@@ -2121,10 +2271,27 @@ void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data
*pp_buff = NULL;
*pp_data = NULL;
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + BTC_AUDIO_BUFF_OFFSET + size);
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 codec_id = bta_av_co_get_cur_codec_type();
switch (codec_id) {
case BTC_AV_CODEC_SBC:
break;
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
case BTC_AV_CODEC_M24:
buf_offset -= BTA_AV_SBC_HDR_SIZE;
break;
#endif
default:
return;
}
#endif
/* todo */
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + buf_offset + size);
if (p_buf != NULL) {
*pp_buff = (uint8_t *)p_buf;
*pp_data = (uint8_t *)(p_buf + 1) + BTC_AUDIO_BUFF_OFFSET;
*pp_data = (uint8_t *)(p_buf + 1) + buf_offset;
}
}
@@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
/**
* @brief Extract one AAC RawDataBlock from one MPEG-4 LATM AudioMuxElement.
*
* Parses useSameStreamMux, skips StreamMuxConfig when useSameStreamMux is 0,
* then reads payloadLengthInfo and copies raw AAC bytes to @p out.
* Supported subset: single program/layer, frameLengthType 0 (typical A2DP).
*
* @return 0 on success, -1 on error or if @p out_cap is too small
*/
int btc_a2dp_latm_extract_raw_data_block(const uint8_t *in, size_t in_len,
uint8_t *out, size_t out_cap, size_t *out_len);
#endif
#ifdef __cplusplus
}
#endif
@@ -18,6 +18,10 @@
enum {
BTC_SV_AV_AA_SBC_INDEX = 0,
BTC_SV_AV_AA_SBC_SINK_INDEX,
#if (BTC_AV_CODEC_AAC_INCLUDED == TRUE)
BTC_SV_AV_AA_M24_INDEX,
BTC_SV_AV_AA_M24_SINK_INDEX,
#endif
BTC_SV_AV_AA_SEP_INDEX /* Last index */
};
@@ -65,7 +69,10 @@ typedef struct {
typedef struct {
/* Connected peer information */
tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS];
tBTC_AV_CODEC_INFO codec_caps;
#if (BTC_AV_EXT_CODEC == TRUE)
UINT8 cur_seid; /* current stream endpoint id */
tBTC_AV_CODEC_INFO codec_caps[BTA_AV_MAX_SEPS];
#endif
/* Current codec configuration - access to this variable must be protected */
tBTC_AV_CODEC_INFO codec_cfg;
tBTC_AV_CODEC_INFO codec_pref_cfg; /* preferred media codec configuration for source */
@@ -277,13 +284,24 @@ BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max);
**
** Function bta_av_co_get_peer_sink_caps
**
** Description Get the sink codec capabilities of the peer
** Description Get currently selected sink codec capabilities of the peer
**
** Returns TRUE if sink capabilities are available, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_av_co_get_peer_sink_caps(tBTA_AV_HNDL hndl, UINT8 *p_codec_caps, UINT8 *p_codec_type);
/*******************************************************************************
**
** Function bta_av_co_get_cur_codec_type
**
** Description Get current codec type
**
** Returns codec type
**
*******************************************************************************/
UINT8 bta_av_co_get_cur_codec_type(void);
#endif ///BTA_AV_INCLUDED == TRUE
#endif
@@ -287,6 +287,17 @@ void btc_av_clear_remote_suspend_flag(void);
******************************************************************************/
uint8_t btc_av_get_service_id(void);
#if (BTC_AV_SRC_INCLUDED == TRUE)
/*******************************************************************************
**
** Function btc_av_report_all_snk_codec_caps
**
** Description Report all sink codec capabilities to the application.
**
*******************************************************************************/
void btc_av_report_all_snk_codec_caps(tBTA_AV_HNDL hndl, esp_a2d_sep_mcc_t *sep_mcc, UINT8 sep_num);
#endif /* BTC_AV_SRC_INCLUDED */
#endif ///BTC_AV_INCLUDED == TRUE
/*******************************************************************************
@@ -33,6 +33,7 @@ extern "C" {
/* Codec type */
#define BTC_AV_CODEC_NONE 0xFF
#define BTC_AV_CODEC_SBC A2D_MEDIA_CT_SBC /* SBC media codec type */
#define BTC_AV_CODEC_M24 A2D_MEDIA_CT_M24 /* M24 media codec type */
#define BTC_AV_CODEC_PCM 0x5 /* Raw PCM */
@@ -45,6 +45,18 @@
#define UC_BT_A2DP_USE_EXTERNAL_CODEC FALSE
#endif
#ifdef CONFIG_BT_A2DP_CODEC_AAC_ENABLED
#define UC_BT_A2DP_CODEC_AAC_ENABLED CONFIG_BT_A2DP_CODEC_AAC_ENABLED
#else
#define UC_BT_A2DP_CODEC_AAC_ENABLED FALSE
#endif
#ifdef CONFIG_BT_A2DP_SEP_NUM_MAX
#define UC_BT_A2DP_SEP_NUM_MAX CONFIG_BT_A2DP_SEP_NUM_MAX
#else
#define UC_BT_A2DP_SEP_NUM_MAX 1
#endif
//AVRCP
#ifdef CONFIG_BT_AVRCP_ENABLED
#define UC_BT_AVRCP_ENABLED TRUE
@@ -98,6 +98,11 @@
#define SBC_DEC_INCLUDED TRUE
#define SBC_ENC_INCLUDED TRUE
#endif
#if (UC_BT_A2DP_CODEC_AAC_ENABLED == TRUE)
#define BTC_AV_CODEC_AAC_INCLUDED TRUE
#define BTA_AV_CODEC_AAC_INCLUDED TRUE
#define A2D_AV_CODEC_AAC_INCLUDED TRUE
#endif
#if UC_BT_AVRCP_CT_COVER_ART_ENABLED
#define BTA_AV_CA_INCLUDED TRUE
#define BTC_AV_CA_INCLUDED TRUE
@@ -2306,7 +2311,7 @@
/* Number of simultaneous stream endpoints. */
#ifndef AVDT_NUM_SEPS
#define AVDT_NUM_SEPS 3
#define AVDT_NUM_SEPS UC_BT_A2DP_SEP_NUM_MAX
#endif
/* Number of transport channels setup per media stream(audio or video) */
@@ -0,0 +1,253 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/******************************************************************************
*
* Utility functions to help build and parse MPEG-2, 4 AAC (M24) Codec
* Information Element per Bluetooth A2DP specification (Media Codec Capabilities).
*
* Layout of Media Codec Capability (starting at LOSC octet):
* Octet 0: LOSC = 8 (number of octets following LOSC: media type + codec type + 6 CIE)
* Octet 1: Media type (Audio = 0x00)
* Octet 2: Media codec type (MPEG-2,4 AAC = 0x02)
* Octets 3-8: Codec Specific Information Element (6 octets)
*
******************************************************************************/
#include <string.h>
#include "common/bt_defs.h"
#include "common/bt_target.h"
#include "stack/a2d_api.h"
#include "stack/a2d_m24.h"
#include "a2d_int.h"
#if (defined(A2D_AV_CODEC_AAC_INCLUDED) && A2D_AV_CODEC_AAC_INCLUDED == TRUE)
/******************************************************************************
** Local helpers
******************************************************************************/
/* Count how many bits are set in the lower nibble (used for 4-bit freq2 field). */
static UINT8 m24_count_bits_4(UINT8 v)
{
UINT8 c = 0;
UINT8 i;
v &= 0x0F;
for (i = 0; i < 4; i++) {
if (v & (1U << i)) {
c++;
}
}
return c;
}
/* Total number of set bits in one byte. */
static UINT8 m24_popcount8(UINT8 v)
{
UINT8 c = 0;
UINT8 i;
for (i = 0; i < 8; i++) {
if (v & (1U << i)) {
c++;
}
}
return c;
}
static tA2D_STATUS m24_validate_ie_for_build(const tA2D_M24_CIE *p_ie)
{
if (p_ie == NULL) {
return A2D_INVALID_PARAMS;
}
/* Object type uses b7-b1; b0 is carried in drc */
if ((p_ie->obj_type & ~A2D_M24_IE_OBJ_TYPE_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->drc & ~A2D_M24_IE_DRC_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->samp_freq1 & ~A2D_M24_IE_SAMP_FREQ1_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->samp_freq2 & ~A2D_M24_IE_SAMP_FREQ2_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->ch & ~A2D_M24_IE_CH_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->vbr & ~A2D_M24_IE_VBR_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->br1 & ~A2D_M24_IE_BR1_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->br2 & ~A2D_M24_IE_BR2_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
if ((p_ie->br3 & ~A2D_M24_IE_BR3_MSK) != 0) {
return A2D_INVALID_PARAMS;
}
/* Capabilities must select at least one object type (bits b7-b1). */
if ((p_ie->obj_type & A2D_M24_IE_OBJ_TYPE_MSK) == 0) {
return A2D_INVALID_PARAMS;
}
/* At least one sampling frequency (12 bits across two octets) and one channel mode. */
if (m24_popcount8(p_ie->samp_freq1) + m24_count_bits_4(((p_ie->samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK) >> 4)) < 1) {
return A2D_INVALID_PARAMS;
}
if (m24_count_bits_4(p_ie->ch & A2D_M24_IE_CH_MSK) < 1) {
return A2D_INVALID_PARAMS;
}
return A2D_SUCCESS;
}
/******************************************************************************
**
** Function A2D_BldM24Info
**
** Description This function is called by an application to build
** the M24 Media Codec Capabilities byte sequence
** beginning from the LOSC octet.
** Input Parameters:
** media_type: Indicates Audio, or Multimedia.
**
** p_ie: The M24 Codec Information Element information.
**
** Output Parameters:
** p_result: the resulting codec info byte sequence.
**
** Returns A2D_SUCCESS if function execution succeeded.
** Error status code, otherwise.
******************************************************************************/
tA2D_STATUS A2D_BldM24Info(UINT8 media_type, tA2D_M24_CIE *p_ie, UINT8 *p_result)
{
tA2D_STATUS status;
if (p_ie == NULL || p_result == NULL) {
return A2D_INVALID_PARAMS;
}
status = m24_validate_ie_for_build(p_ie);
if (status != A2D_SUCCESS) {
return status;
}
*p_result++ = A2D_M24_INFO_LEN;
*p_result++ = media_type;
*p_result++ = A2D_MEDIA_CT_M24;
/* Codec Specific Information Element (6 octets), A2DP spec */
*p_result++ = (p_ie->obj_type & A2D_M24_IE_OBJ_TYPE_MSK) | (p_ie->drc & A2D_M24_IE_DRC_MSK);
*p_result++ = p_ie->samp_freq1 & A2D_M24_IE_SAMP_FREQ1_MSK;
*p_result++ = (p_ie->samp_freq2 & A2D_M24_IE_SAMP_FREQ2_MSK) | (p_ie->ch & A2D_M24_IE_CH_MSK);
*p_result++ = (p_ie->vbr & A2D_M24_IE_VBR_MSK) | (p_ie->br1 & A2D_M24_IE_BR1_MSK);
*p_result++ = p_ie->br2 & A2D_M24_IE_BR2_MSK;
*p_result = p_ie->br3 & A2D_M24_IE_BR3_MSK;
return A2D_SUCCESS;
}
/******************************************************************************
**
** Function A2D_ParsM24Info
**
** Description This function is called by an application to parse
** the M24 Media Codec Capabilities byte sequence
** beginning from the LOSC octet.
** Input Parameters:
** p_info: the byte sequence to parse.
**
** for_caps: TRUE, if the byte sequence is for get capabilities response.
**
** Output Parameters:
** p_ie: The M24 Codec Information Element information.
**
** Returns A2D_SUCCESS if function execution succeeded.
** Error status code, otherwise.
******************************************************************************/
tA2D_STATUS A2D_ParsM24Info(tA2D_M24_CIE *p_ie, UINT8 *p_info, BOOLEAN for_caps)
{
tA2D_STATUS status = A2D_SUCCESS;
UINT8 losc;
UINT8 media_type;
UINT8 cie[6];
UINT8 i;
UINT8 sf2_hi;
UINT8 freq_bits_total;
UINT8 ch_bits;
if (p_ie == NULL || p_info == NULL) {
return A2D_INVALID_PARAMS;
}
memset(p_ie, 0, sizeof(tA2D_M24_CIE));
losc = *p_info++;
media_type = *p_info++;
if (losc != A2D_M24_INFO_LEN || media_type != A2D_MEDIA_TYPE_AUDIO || *p_info != A2D_MEDIA_CT_M24) {
return A2D_WRONG_CODEC;
}
p_info++; /* skip media codec type */
for (i = 0; i < A2D_M24_CIE_LEN; i++) {
cie[i] = *p_info++;
}
/* Unpack CIE */
p_ie->drc = cie[0] & A2D_M24_IE_DRC_MSK;
p_ie->obj_type = cie[0] & A2D_M24_IE_OBJ_TYPE_MSK;
p_ie->samp_freq1 = cie[1] & A2D_M24_IE_SAMP_FREQ1_MSK;
p_ie->samp_freq2 = cie[2] & A2D_M24_IE_SAMP_FREQ2_MSK;
p_ie->ch = cie[2] & A2D_M24_IE_CH_MSK;
p_ie->vbr = cie[3] & A2D_M24_IE_VBR_MSK;
p_ie->br1 = cie[3] & A2D_M24_IE_BR1_MSK;
p_ie->br2 = cie[4] & A2D_M24_IE_BR2_MSK;
p_ie->br3 = cie[5] & A2D_M24_IE_BR3_MSK;
if (for_caps) {
/* Get capabilities: need at least one object type and one frequency / channel bit. */
if ((p_ie->obj_type & A2D_M24_IE_OBJ_TYPE_MSK) == 0) {
status = A2D_BAD_OBJ_TYPE;
} else {
sf2_hi = p_ie->samp_freq2 >> 4;
freq_bits_total = m24_popcount8(p_ie->samp_freq1) + m24_count_bits_4(sf2_hi);
ch_bits = m24_count_bits_4(p_ie->ch);
if (freq_bits_total < 1) {
status = A2D_NS_SAMP_FREQ;
} else if (ch_bits < 1) {
status = A2D_NS_CHANNEL;
}
}
} else {
/* Stream configuration: single choice for object type, frequency, channels. */
if (A2D_BitsSet(p_ie->obj_type) != A2D_SET_ONE_BIT) {
status = A2D_BAD_OBJ_TYPE;
} else {
sf2_hi = p_ie->samp_freq2 >> 4;
freq_bits_total = m24_popcount8(p_ie->samp_freq1) + m24_count_bits_4(sf2_hi);
if (freq_bits_total != 1) {
status = A2D_BAD_SAMP_FREQ;
} else if (A2D_BitsSet(p_ie->ch) != A2D_SET_ONE_BIT) {
status = A2D_BAD_CHANNEL;
}
}
}
return status;
}
#endif /* #if (defined(A2D_AV_CODEC_AAC_INCLUDED) && A2D_AV_CODEC_AAC_INCLUDED == TRUE) */
@@ -0,0 +1,145 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/******************************************************************************
*
* interface to MPEG-2, 4 AAC codec
*
******************************************************************************/
#ifndef A2D_M24_H
#define A2D_M24_H
#include "stack/a2d_api.h"
#if (A2D_INCLUDED == TRUE)
/*****************************************************************************
** Constants
*****************************************************************************/
/* CIE offset in the info byte sequence */
#define A2D_M24_CIE_OFF 3
/* CIE length in the info byte sequence */
#define A2D_M24_CIE_LEN 6
/* the LOSC of M24 media codec capability */
#define A2D_M24_INFO_LEN 8
/* for Codec Specific Information Element */
#define A2D_M24_IE_OBJ_TYPE_MSK 0xFE /* b7-b1: Object type */
#define A2D_M24_IE_OBJ_TYPE_2_AAC_LC 0x80 /* b7: MPEG-2 AAC LC */
#define A2D_M24_IE_OBJ_TYPE_4_AAC_LC 0x40 /* b6: MPEG-4 AAC LC */
#define A2D_M24_IE_OBJ_TYPE_4_AAC_LTP 0x20 /* b5: MPEG-4 AAC LTP */
#define A2D_M24_IE_OBJ_TYPE_4_AAC_SCALABLE 0x10 /* b4: MPEG-4 AAC Scalable */
#define A2D_M24_IE_OBJ_TYPE_4_HE_AAC 0x08 /* b3: MPEG-4 HE-AAC */
#define A2D_M24_IE_OBJ_TYPE_4_HE_AAC_V2 0x04 /* b2: MPEG-4 HE-AAC v2 */
#define A2D_M24_IE_OBJ_TYPE_4_AAC_ELD_V2 0x02 /* b1: MPEG-4 AAC-ELD v2 */
#define A2D_M24_IE_DRC_MSK 0x01 /* b0: DRC */
#define A2D_M24_IE_DRC_SUPPORT 0x01 /* DRC support */
#define A2D_M24_IE_DRC_NS 0x00 /* DRC not support */
#define A2D_M24_IE_SAMP_FREQ1_MSK 0xFF /* b7-b0: Sampling frequency part 1 */
#define A2D_M24_IE_SAMP_FREQ1_8 0x80 /* b7:8 kHz */
#define A2D_M24_IE_SAMP_FREQ1_11 0x40 /* b6:11.025 kHz */
#define A2D_M24_IE_SAMP_FREQ1_12 0x20 /* b5:12 kHz */
#define A2D_M24_IE_SAMP_FREQ1_16 0x10 /* b4:16 kHz */
#define A2D_M24_IE_SAMP_FREQ1_22 0x08 /* b3:22.05 kHz */
#define A2D_M24_IE_SAMP_FREQ1_24 0x04 /* b2:24 kHz */
#define A2D_M24_IE_SAMP_FREQ1_32 0x02 /* b1:32 kHz */
#define A2D_M24_IE_SAMP_FREQ1_44 0x01 /* b0:44.1kHz */
#define A2D_M24_IE_SAMP_FREQ1_NS 0x00 /* Sampling frequency part 1: not support */
#define A2D_M24_IE_SAMP_FREQ2_MSK 0xF0 /* b7-b4: Sampling frequency part 2 */
#define A2D_M24_IE_SAMP_FREQ2_48 0x80 /* b7:48 kHz */
#define A2D_M24_IE_SAMP_FREQ2_64 0x40 /* b6:64 kHz */
#define A2D_M24_IE_SAMP_FREQ2_88 0x20 /* b5:88.2kHz */
#define A2D_M24_IE_SAMP_FREQ2_96 0x10 /* b4:96 kHz */
#define A2D_M24_IE_SAMP_FREQ2_NS 0x00 /* Sampling frequency part 2: not support */
#define A2D_M24_IE_CH_MSK 0x0F /* b3-b0: Channels */
#define A2D_M24_IE_CH_1 0x08 /* b3: 1 */
#define A2D_M24_IE_CH_2 0x04 /* b2: 2 */
#define A2D_M24_IE_CH_6 0x02 /* b1: 6 */
#define A2D_M24_IE_CH_8 0x01 /* b0: 8 */
#define A2D_M24_IE_VBR_MSK 0x80 /* b7: VBR */
#define A2D_M24_IE_VBR_SUPPORT 0x80 /* VBR support */
#define A2D_M24_IE_VBR_NS 0x00 /* VBR not support */
#define A2D_M24_IE_BR1_MSK 0x7F /* b6-b0: Bit rate part 1 */
#define A2D_M24_IE_BR2_MSK 0xFF /* b7-b0: Bit rate part 2 */
#define A2D_M24_IE_BR3_MSK 0xFF /* b7-b0: Bit rate part 3 */
/*****************************************************************************
** Type Definitions
*****************************************************************************/
/* data type for the M24 Codec Information Element*/
typedef struct {
UINT8 drc; /* DRC */
UINT8 obj_type; /* Object type */
UINT8 samp_freq1; /* Sampling frequency part 1 */
UINT8 ch; /* Channels */
UINT8 samp_freq2; /* Sampling frequency part 2 */
UINT8 br1; /* Bit rate part 1 */
UINT8 vbr; /* VBR */
UINT8 br2; /* Bit rate part 2 */
UINT8 br3; /* Bit rate part 3 */
} tA2D_M24_CIE;
/*****************************************************************************
** External Function Declarations
*****************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
/******************************************************************************
**
** Function A2D_BldM24Info
**
** Description This function is called by an application to build
** the M24 Media Codec Capabilities byte sequence
** beginning from the LOSC octet.
** Input Parameters:
** media_type: Indicates Audio, or Multimedia.
**
** p_ie: The M24 Codec Information Element information.
**
** Output Parameters:
** p_result: the resulting codec info byte sequence.
**
** Returns A2D_SUCCESS if function execution succeeded.
** Error status code, otherwise.
******************************************************************************/
extern tA2D_STATUS A2D_BldM24Info(UINT8 media_type, tA2D_M24_CIE *p_ie,
UINT8 *p_result);
/******************************************************************************
**
** Function A2D_ParsM24Info
**
** Description This function is called by an application to parse
** the M24 Media Codec Capabilities byte sequence
** beginning from the LOSC octet.
** Input Parameters:
** p_info: the byte sequence to parse.
**
** for_caps: TRUE, if the byte sequence is for get capabilities response.
**
** Output Parameters:
** p_ie: The M24 Codec Information Element information.
**
** Returns A2D_SUCCESS if function execution succeeded.
** Error status code, otherwise.
******************************************************************************/
extern tA2D_STATUS A2D_ParsM24Info(tA2D_M24_CIE *p_ie, UINT8 *p_info,
BOOLEAN for_caps);
#ifdef __cplusplus
}
#endif
#endif ///A2D_INCLUDED == TRUE
#endif /* A2D_M24_H */