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