diff --git a/components/bt/host/bluedroid/CMakeLists.txt b/components/bt/host/bluedroid/CMakeLists.txt index 6d353315eca..f02b1b11bfc 100644 --- a/components/bt/host/bluedroid/CMakeLists.txt +++ b/components/bt/host/bluedroid/CMakeLists.txt @@ -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" diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index a7379f6db15..e5d6cecc0e5 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -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 diff --git a/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h b/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h index 61894d3dbe3..9811a07ecfa 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h @@ -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 diff --git a/components/bt/host/bluedroid/bta/av/bta_av_aact.c b/components/bt/host/bluedroid/bta/av/bta_av_aact.c index 1120c9fb0b5..884dd25a5ae 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_aact.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_aact.c @@ -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; diff --git a/components/bt/host/bluedroid/bta/av/bta_av_m24.c b/components/bt/host/bluedroid/bta/av/bta_av_m24.c new file mode 100644 index 00000000000..fc5330e302b --- /dev/null +++ b/components/bt/host/bluedroid/bta/av/bta_av_m24.c @@ -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) */ diff --git a/components/bt/host/bluedroid/bta/av/bta_av_main.c b/components/bt/host/bluedroid/bta/av/bta_av_main.c index 5e08ef5d947..d5a4edf46b4 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_main.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_main.c @@ -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) diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h index d1331bf1cb6..a998bc21fca 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h @@ -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); diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h index 03c07c33f91..1a1def9a036 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_co.h @@ -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); /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h new file mode 100644 index 00000000000..a67eb0c3711 --- /dev/null +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_m24.h @@ -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 */ diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c index 758a859f866..fa6d5326dea 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/bta_av_co.c @@ -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, diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_latm_raw.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_latm_raw.c new file mode 100644 index 00000000000..29f2d06ef04 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_latm_raw.c @@ -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 +#include +#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) */ diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c index f01146ab6a8..c64d8853137 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c @@ -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(×tamp, (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 */ } diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c index ed7c81e7489..d7ba6f5fcf5 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -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(¶m, 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, ¶m); +} #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, ¶m); 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(¶m.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, ¶m); + 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(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t)); + btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, ¶m); + } 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, ¶m); - 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, ¶m); + 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, ¶m); - } 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, ¶m); - } - } + 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, ¶m); + } + 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, ¶m); + } 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, ¶m); + } + } } } @@ -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; } } diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_a2dp_latm_raw.h b/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_a2dp_latm_raw.h new file mode 100644 index 00000000000..5b597fc37e5 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_a2dp_latm_raw.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#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 diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h b/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h index 2400fd302eb..66820c88900 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/include/btc_av_co.h @@ -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 diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_av.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_av.h index ecad1a9a7b4..f0025db3a1d 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_av.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_av.h @@ -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 /******************************************************************************* diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h index 57d14fbf2d8..c2e93c0cd75 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_av_api.h @@ -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 */ diff --git a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h index 762a9e7c287..e992e4ee794 100644 --- a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h +++ b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -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 diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 0fd318dc4f4..9764d6b9b07 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -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) */ diff --git a/components/bt/host/bluedroid/stack/a2dp/a2d_m24.c b/components/bt/host/bluedroid/stack/a2dp/a2d_m24.c new file mode 100644 index 00000000000..060923b6361 --- /dev/null +++ b/components/bt/host/bluedroid/stack/a2dp/a2d_m24.c @@ -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 +#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) */ diff --git a/components/bt/host/bluedroid/stack/include/stack/a2d_m24.h b/components/bt/host/bluedroid/stack/include/stack/a2d_m24.h new file mode 100644 index 00000000000..c18160c247f --- /dev/null +++ b/components/bt/host/bluedroid/stack/include/stack/a2d_m24.h @@ -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 */