refactor(bt/bluedroid): Refactor a2dp audio data path

- Refactor audio APIs, optimize audio data path, reduce memory copy operations
- Support using external codec in application layer
- The internal codec will not be compiled if not use
This commit is contained in:
linruihao
2025-02-12 20:06:10 +08:00
parent eda7ea3f49
commit 74fab3a7b1
26 changed files with 1715 additions and 263 deletions

View File

@@ -288,7 +288,9 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_control.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_sink_ext_coedc.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source.c"
"host/bluedroid/btc/profile/std/a2dp/btc_a2dp_source_ext_codec.c"
"host/bluedroid/btc/profile/std/a2dp/btc_av.c"
"host/bluedroid/btc/profile/std/avrc/btc_avrc.c"
"host/bluedroid/btc/profile/std/avrc/bta_avrc_co.c"

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,6 +14,37 @@
#if BTC_AV_INCLUDED
esp_a2d_audio_buff_t *esp_a2d_audio_buff_alloc(uint16_t size)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return NULL;
}
if (size == 0) {
return NULL;
}
uint8_t *p_buf = NULL, *p_data;
btc_av_audio_buff_alloc(size, &p_buf, &p_data);
if (p_buf == NULL) {
return NULL;
}
esp_a2d_audio_buff_t *audio_buf = (esp_a2d_audio_buff_t *)p_buf;
audio_buf->buff_size = size;
audio_buf->data_len = 0;
audio_buf->data = p_data;
return audio_buf;
}
void esp_a2d_audio_buff_free(esp_a2d_audio_buff_t *audio_buf)
{
if (audio_buf == NULL) {
return;
}
btc_av_audio_buff_free((uint8_t *)audio_buf);
}
#if BTC_AV_SINK_INCLUDED
esp_err_t esp_a2d_sink_init(void)
{
@@ -36,6 +67,35 @@ esp_err_t esp_a2d_sink_init(void)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
if (seid >= ESP_A2D_MAX_SEPS) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SINK_API_REG_SEP_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.reg_sep.seid = seid;
memcpy(&arg.reg_sep.mcc, mcc, sizeof(esp_a2d_mcc_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_deinit(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -81,6 +141,30 @@ esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_register_audio_data_callback(esp_a2d_sink_audio_data_cb_t callback)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_sink_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.audio_data_cb = callback;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -251,6 +335,35 @@ esp_err_t esp_a2d_source_init(void)
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (g_a2dp_on_deinit || g_a2dp_sink_ongoing_deinit) {
return ESP_ERR_INVALID_STATE;
}
if (seid >= ESP_A2D_MAX_SEPS) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SRC_API_REG_SEP_EVT;
btc_av_args_t arg;
memset(&arg, 0, sizeof(btc_av_args_t));
arg.reg_sep.seid = seid;
memcpy(&arg.reg_sep.mcc, mcc, sizeof(esp_a2d_mcc_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_deinit(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
@@ -348,6 +461,26 @@ esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callbac
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_a2d_source_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED || !btc_av_is_started()) {
return ESP_ERR_INVALID_STATE;
}
if (audio_buf == NULL || audio_buf->data_len == 0 || conn_hdl == 0) {
return ESP_ERR_INVALID_ARG;
}
if (!btc_a2d_src_audio_mtu_check(audio_buf->data_len)) {
return ESP_ERR_INVALID_SIZE;
}
if (btc_a2d_src_audio_data_send(conn_hdl, audio_buf) != BT_STATUS_SUCCESS) {
return ESP_FAIL;
}
return ESP_OK;
}
#endif /* BTC_AV_SRC_INCLUDED */
#endif /* #if BTC_AV_INCLUDED */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,11 +9,16 @@
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_a2dp_legacy_api.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_A2D_MAX_SEPS 1 /*!< Maximum number of Stream Endpoint that supported */
typedef uint16_t esp_a2d_conn_hdl_t; /*!< Connection handle, associate with specific device that connected */
/**
* @brief Media codec types supported by A2DP.
*/
@@ -30,6 +35,102 @@ typedef uint8_t esp_a2d_mct_t;
#define ESP_A2D_PSC_DELAY_RPT (1<<0) /*!< Delay Report */
typedef uint16_t esp_a2d_psc_t;
/**
* @brief A2DP SBC sampling frequency bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_SF_16K (0x8) /*!< SBC sampling frequency 16kHz */
#define ESP_A2D_SBC_CIE_SF_32K (0x4) /*!< SBC sampling frequency 32kHz */
#define ESP_A2D_SBC_CIE_SF_44K (0x2) /*!< SBC sampling frequency 44.1kHz */
#define ESP_A2D_SBC_CIE_SF_48K (0x1) /*!< SBC sampling frequency 48kHz */
/**
* @brief A2DP SBC channel mode bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_CH_MODE_MONO (0x8) /*!< SBC channel mode Mono */
#define ESP_A2D_SBC_CIE_CH_MODE_DUAL_CHANNEL (0x4) /*!< SBC channel mode Dual Channel */
#define ESP_A2D_SBC_CIE_CH_MODE_STEREO (0x2) /*!< SBC channel mode Stereo */
#define ESP_A2D_SBC_CIE_CH_MODE_JOINT_STEREO (0x1) /*!< SBC channel mode Stereo */
/**
* @brief A2DP SBC block length bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_BLOCK_LEN_4 (0x8) /*!< SBC block length 4 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_8 (0x4) /*!< SBC block length 8 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_12 (0x2) /*!< SBC block length 12 */
#define ESP_A2D_SBC_CIE_BLOCK_LEN_16 (0x1) /*!< SBC block length 16 */
/**
* @brief A2DP SBC number of subbands bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_NUM_SUBBANDS_4 (0x2) /*!< SBC number of subbands 4 */
#define ESP_A2D_SBC_CIE_NUM_SUBBANDS_8 (0x1) /*!< SBC number of subbands 8 */
/**
* @brief A2DP SBC allocation method bit mask in CIE
*/
#define ESP_A2D_SBC_CIE_ALLOC_MTHD_SRN (0x2) /*!< SBC allocation method SNR */
#define ESP_A2D_SBC_CIE_ALLOC_MTHD_LOUDNESS (0x1) /*!< SBC allocation method Loudness */
/**
* @brief A2DP SBC media codec capabilities information struct
*/
typedef struct {
uint8_t ch_mode : 4; /*!< Channel mode */
uint8_t samp_freq : 4; /*!< Sampling frequency */
uint8_t alloc_mthd : 2; /*!< Allocation method */
uint8_t num_subbands : 2; /*!< Number of subbands */
uint8_t block_len : 4; /*!< Block length */
uint8_t min_bitpool; /*!< Minimum bitpool */
uint8_t max_bitpool; /*!< Maximum bitpool */
} __attribute__((packed)) esp_a2d_cie_sbc_t;
/**
* @brief A2DP MPEG-1, 2 media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t ch_mode : 4; /*!< Channel mode */
uint8_t crc : 1; /*!< CRC protection */
uint8_t layer : 3; /*!< Layers of MPEG-1,2 Audio */
uint8_t samp_freq : 6; /*!< Sampling frequency */
uint8_t mpf : 1; /*!< Media payload format */
uint8_t rfu : 1; /*!< Reserved */
uint8_t bri1 : 7; /*!< Bit rate index part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t bri2; /*!< Bit rate index part 2 */
} __attribute__((packed)) esp_a2d_cie_m12_t;
/**
* @brief A2DP MPEG-2, 4 media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t drc : 1; /*!< Support of MPEG-D DRC */
uint8_t obj_type : 7; /*!< Object type */
uint8_t samp_freq1 : 8; /*!< Sampling frequency part 1 */
uint8_t ch : 4; /*!< Channels */
uint8_t samp_freq2 : 4; /*!< Sampling frequency part 2 */
uint8_t br1 : 7; /*!< Bit rate part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t br2; /*!< Bit rate part 2 */
uint8_t br3; /*!< Bit rate part 3 */
} __attribute__((packed)) esp_a2d_cie_m24_t;
/**
* @brief A2DP ATRAC media codec capabilities information struct (Not supported yet)
*/
typedef struct {
uint8_t rfu1 : 2; /*!< Reserved */
uint8_t ch_mode : 3; /*!< Channel mode */
uint8_t version : 3; /*!< Version */
uint8_t bri1 : 3; /*!< Bit rate index part 1 */
uint8_t vbr : 1; /*!< Support of VBR */
uint8_t samp_freq : 2; /*!< Sampling frequency */
uint8_t rfu2 : 2; /*!< Reserved */
uint8_t bri2; /*!< Bit rate index part 2 */
uint8_t bri3; /*!< Bit rate index part 3 */
uint16_t max_sul; /*!< Maximum SUL */
uint8_t rfu3; /*!< Reserved */
} __attribute__((packed)) esp_a2d_cie_atrac_t;
/**
* @brief A2DP media codec capabilities union
*/
@@ -40,10 +141,15 @@ typedef struct {
#define ESP_A2D_CIE_LEN_M24 (6)
#define ESP_A2D_CIE_LEN_ATRAC (7)
union {
uint8_t sbc[ESP_A2D_CIE_LEN_SBC]; /*!< SBC codec capabilities */
uint8_t m12[ESP_A2D_CIE_LEN_M12]; /*!< MPEG-1,2 audio codec capabilities */
uint8_t m24[ESP_A2D_CIE_LEN_M24]; /*!< MPEG-2, 4 AAC audio codec capabilities */
uint8_t atrac[ESP_A2D_CIE_LEN_ATRAC]; /*!< ATRAC family codec capabilities */
uint8_t sbc[ESP_A2D_CIE_LEN_SBC] __attribute__((deprecated)); /*!< SBC codec capabilities, deprecated, use sbc_info instead */
uint8_t m12[ESP_A2D_CIE_LEN_M12] __attribute__((deprecated)); /*!< MPEG-1,2 audio codec capabilities, deprecated, use m12_info instead */
uint8_t m24[ESP_A2D_CIE_LEN_M24] __attribute__((deprecated)); /*!< MPEG-2, 4 AAC audio codec capabilities, deprecated, use m24_info instead */
uint8_t atrac[ESP_A2D_CIE_LEN_ATRAC] __attribute__((deprecated)); /*!< ATRAC family codec capabilities, deprecated, use atrac_info instead */
esp_a2d_cie_sbc_t sbc_info; /*!< SBC codec capabilities */
esp_a2d_cie_m12_t m12_info; /*!< MPEG-1,2 audio codec capabilities */
esp_a2d_cie_m24_t m24_info; /*!< MPEG-2, 4 AAC audio codec capabilities */
esp_a2d_cie_atrac_t atrac_info; /*!< ATRAC family codec capabilities */
} cie; /*!< A2DP codec information element */
} __attribute__((packed)) esp_a2d_mcc_t;
@@ -103,6 +209,16 @@ typedef enum {
ESP_A2D_INIT_SUCCESS /*!< A2DP profile deinit successful event */
} esp_a2d_init_state_t;
/**
* @brief Bluetooth A2DP SEP register states
*/
typedef enum {
ESP_A2D_SEP_REG_SUCCESS = 0, /*!< A2DP stream endpoint register success */
ESP_A2D_SEP_REG_FAIL, /*!< A2DP stream endpoint register generic fail */
ESP_A2D_SEP_REG_UNSUPPORTED, /*!< A2DP stream endpoint register fail, unsupported codec type or param */
ESP_A2D_SEP_REG_INVALID_STATE, /*!< A2DP stream endpoint register fail, invalid state */
} esp_a2d_sep_reg_state_t;
/**
* @brief Bluetooth A2DP set delay report value states
*/
@@ -117,15 +233,27 @@ typedef enum {
typedef enum {
ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
ESP_A2D_AUDIO_STATE_EVT, /*!< audio stream transmission state changed event */
ESP_A2D_AUDIO_CFG_EVT, /*!< audio codec is configured, only used for A2DP SINK */
ESP_A2D_AUDIO_CFG_EVT, /*!< audio codec is configured */
ESP_A2D_MEDIA_CTRL_ACK_EVT, /*!< acknowledge event in response to media control commands */
ESP_A2D_PROF_STATE_EVT, /*!< indicate a2dp init&deinit complete */
ESP_A2D_SEP_REG_STATE_EVT, /*!< indicate a2dp steam endpoint register status */
ESP_A2D_SNK_PSC_CFG_EVT, /*!< protocol service capabilities configuredonly used for A2DP SINK */
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_cb_event_t;
/**
* @brief A2DP audio buffer
*/
typedef struct {
uint16_t buff_size; /*!< audio buff size */
uint16_t number_frame; /*!< number of encoded frame in this buff */
uint32_t timestamp; /*!< timestamp of the first frame */
uint16_t data_len; /*!< audio data len */
uint8_t *data; /*!< pointer to audio data start */
} esp_a2d_audio_buff_t; /*!< struct to store audio data */
/**
* @brief A2DP state callback parameters
*/
@@ -136,6 +264,8 @@ typedef union {
struct a2d_conn_stat_param {
esp_a2d_connection_state_t state; /*!< one of values from esp_a2d_connection_state_t */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
uint16_t audio_mtu; /*!< MTU of audio connection */
esp_a2d_disc_rsn_t disc_rsn; /*!< reason of disconnection for "DISCONNECTED" */
} conn_stat; /*!< A2DP connection status */
@@ -144,7 +274,8 @@ typedef union {
*/
struct a2d_audio_stat_param {
esp_a2d_audio_state_t state; /*!< one of the values from esp_a2d_audio_state_t */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address*/
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
} audio_stat; /*!< audio stream playing state */
/**
@@ -152,6 +283,7 @@ typedef union {
*/
struct a2d_audio_cfg_param {
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_conn_hdl_t conn_hdl; /*!< connection handle */
esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */
} audio_cfg; /*!< media codec configuration information */
@@ -170,6 +302,14 @@ typedef union {
esp_a2d_init_state_t init_state; /*!< a2dp profile state param */
} a2d_prof_stat; /*!< status to indicate a2d prof init or deinit */
/**
* @brief ESP_A2D_SEP_REG_STATE_EVT
*/
struct a2d_sep_reg_stat_param {
uint8_t seid; /*!< the stream endpoint to register */
esp_a2d_sep_reg_state_t reg_state; /*!< stream endpoint register state */
} a2d_sep_reg_stat; /*!< status to indicate a2d sep register success or not */
/**
* @brief ESP_A2D_SNK_PSC_CFG_EVT
*/
@@ -213,25 +353,31 @@ typedef void (* esp_a2d_cb_t)(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *para
/**
* @brief A2DP sink data callback function
*
* @param[in] buf : pointer to the data received from A2DP source device and is PCM format decoded from SBC decoder;
* buf references to a static memory block and can be overwritten by upcoming data
* @param[in] conn_hdl: connection handle
*
* @param[in] len : size(in bytes) in buf
* @param[in] audio_buf: pointer to the data received from A2DP source device, should be freed by
* calling esp_a2d_audio_buff_free
*/
typedef void (* esp_a2d_sink_data_cb_t)(const uint8_t *buf, uint32_t len);
typedef void (* esp_a2d_sink_audio_data_cb_t)(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
* @brief A2DP source data read callback function
* @brief Allocate a audio buffer to store and send audio data, can be used in both sink and source.
*
* @param[in] buf : buffer to be filled with PCM data stream from higher layer
* @param[in] size: buffer size to allocate
*
* @param[in] len : size(in bytes) of data block to be copied to buf. -1 is an indication to user
* that data buffer shall be flushed
*
* @return size of bytes read successfully, if the argument len is -1, this value is ignored.
* @return allocated audio buffer, if Bluedroid is not enabled, no memory, or size is
* zeros, will return NULL
*
*/
typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
esp_a2d_audio_buff_t *esp_a2d_audio_buff_alloc(uint16_t size);
/**
* @brief Free a audio buffer allocated by esp_a2d_audio_buff_alloc.
*
* @param[in] audio_buf: audio buffer to free
*
*/
void esp_a2d_audio_buff_free(esp_a2d_audio_buff_t *audio_buf);
/**
* @brief Register application callback function to A2DP module. This function should be called
@@ -248,23 +394,20 @@ typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
*/
esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback);
/**
* @brief Register A2DP sink data output function; For now the output is PCM data stream decoded
* from SBC format. This function should be called only after esp_bluedroid_enable()
* completes successfully, used only by A2DP sink. The callback is invoked in the context
* of A2DP sink task whose stack size is configurable through menuconfig.
* @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.
*
* @param[in] callback: A2DP sink data callback function
* @param[in] callback: A2DP sink audio data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
esp_err_t esp_a2d_sink_register_audio_data_callback(esp_a2d_sink_audio_data_cb_t callback);
/**
*
@@ -282,6 +425,23 @@ esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
*/
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.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently only supports SBC
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_sink_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc);
/**
*
@@ -297,7 +457,6 @@ esp_err_t esp_a2d_sink_init(void);
*/
esp_err_t esp_a2d_sink_deinit(void);
/**
*
* @brief Connect to remote bluetooth A2DP source device. This API must be called after
@@ -313,7 +472,6 @@ esp_err_t esp_a2d_sink_deinit(void);
*/
esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda);
/**
*
* @brief Disconnect from the remote A2DP source device. This API must be called after
@@ -359,7 +517,6 @@ esp_err_t esp_a2d_sink_set_delay_value(uint16_t delay_value);
*/
esp_err_t esp_a2d_sink_get_delay_value(void);
/**
*
* @brief Media control commands. This API can be used for both A2DP sink and source
@@ -375,7 +532,6 @@ esp_err_t esp_a2d_sink_get_delay_value(void);
*/
esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl);
/**
*
* @brief Initialize the bluetooth A2DP source module. A2DP can work independently.
@@ -392,6 +548,23 @@ esp_err_t esp_a2d_media_ctrl(esp_a2d_media_ctrl_t ctrl);
*/
esp_err_t esp_a2d_source_init(void);
/**
* @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.
*
* @param[in] seid: local SEP identifier, start from 0, less than ESP_A2D_MAX_SEPS
*
* @param[in] mcc: codec capability, currently, only SBC supported
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_FAIL: others
*
*/
esp_err_t esp_a2d_source_register_stream_endpoint(uint8_t seid, const esp_a2d_mcc_t *mcc);
/**
*
@@ -407,23 +580,24 @@ esp_err_t esp_a2d_source_init(void);
*/
esp_err_t esp_a2d_source_deinit(void);
/**
* @brief Register A2DP source data input function. For now, the input shoule be PCM data stream.
* This function should be called only after esp_bluedroid_enable() completes
* successfully. The callback is invoked in the context of A2DP source task whose
* stack size is configurable through menuconfig.
* @brief Send a audio buff with encoded audio data to sink, the audio data len shall not bigger than
* audio connection mtu (retrieved from ESP_A2D_CONNECTION_STATE_EVT). if the return value is
* ESP_OK, then the audio buff is consumed, otherwise, audio buff can be reused by user.
*
* @param[in] callback: A2DP source data callback function
* @param[in] conn_hdl: connection handle
*
* @param[in] audio_buf: encoded audio data
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
* - ESP_ERR_INVALID_ARG: invalid parameter
* - ESP_ERR_INVALID_SIZE: data len bigger than mtu
* - ESP_FAIL: buffer queue is full, try again later
*
*/
esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback);
esp_err_t esp_a2d_source_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
/**
*
@@ -440,7 +614,6 @@ esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callbac
*/
esp_err_t esp_a2d_source_connect(esp_bd_addr_t remote_bda);
/**
*
* @brief Disconnect from the remote A2DP sink device. This API must be called

View File

@@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Some legacy APIs of A2DP, will be removed in the future
*/
#pragma once
#include "esp_err.h"
#include "esp_bt_defs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief [Deprecated] A2DP sink data callback function
*
* @param[in] buf : pointer to the data received from A2DP source device and is PCM format decoded from SBC decoder;
* buf references to a static memory block and can be overwritten by upcoming data
*
* @param[in] len : size(in bytes) in buf
*/
typedef void (* esp_a2d_sink_data_cb_t)(const uint8_t *buf, uint32_t len);
/**
* @brief [Deprecated] A2DP source data read callback function
*
* @param[in] buf : buffer to be filled with PCM data stream from higher layer
*
* @param[in] len : size(in bytes) of data block to be copied to buf. -1 is an indication to user
* that data buffer shall be flushed
*
* @return size of bytes read successfully, if the argument len is -1, this value is ignored.
*
*/
typedef int32_t (* esp_a2d_source_data_cb_t)(uint8_t *buf, int32_t len);
/**
* @brief [Deprecated] Register A2DP sink data output function; For now the output is PCM data stream decoded
* from SBC format. This function should be called only after esp_bluedroid_enable()
* completes successfully, used only by A2DP sink. The callback is invoked in the context
* of A2DP sink task whose stack size is configurable through menuconfig.
*
* @param[in] callback: A2DP sink data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_a2d_sink_register_data_callback(esp_a2d_sink_data_cb_t callback);
/**
* @brief [Deprecated] Register A2DP source data input function. For now, the input should be PCM data stream.
* This function should be called only after esp_bluedroid_enable() completes
* successfully. The callback is invoked in the context of A2DP source task whose
* stack size is configurable through menuconfig.
*
* @param[in] callback: A2DP source data callback function
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not yet enabled
* - ESP_FAIL: if callback is a NULL function pointer
*
*/
esp_err_t esp_a2d_source_register_data_callback(esp_a2d_source_data_cb_t callback);
#ifdef __cplusplus
}
#endif

View File

@@ -57,7 +57,7 @@
#define BTA_AV_CLOSE_REQ_TIME_VAL 4000
#endif
/* number to retry on reconfigure failure - some headsets requirs this number to be more than 1 */
/* number to retry on reconfigure failure - some headsets requires this number to be more than 1 */
#ifndef BTA_AV_RECONFIG_RETRY
#define BTA_AV_RECONFIG_RETRY 6
#endif
@@ -239,7 +239,7 @@ static UINT8 bta_av_get_scb_handle(tBTA_AV_SCB *p_scb, UINT8 local_sep)
return (p_scb->seps[xx].av_handle);
}
}
APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropiate sep_type not found")
APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropriate sep_type not found")
return 0; /* return invalid handle */
}
@@ -260,7 +260,7 @@ static UINT8 bta_av_get_scb_sep_type(tBTA_AV_SCB *p_scb, UINT8 tavdt_handle)
return (p_scb->seps[xx].tsep);
}
}
APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropiate handle not found")
APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropriate handle not found")
return 3; /* return invalid sep type */
}
@@ -586,9 +586,18 @@ void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UI
osi_free(p_pkt);
return;
}
if (p_pkt->offset >= 4) {
/* The offset of avdt packet will always greater than 4, it is safe to add timestamp here */
*((UINT32 *) (p_pkt + 1)) = time_stamp;
}
else {
APPL_TRACE_WARNING("bta_av_stream_data_cback avdt packet offset small than 4");
}
p_pkt->event = BTA_AV_MEDIA_DATA_EVT;
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_DATA_EVT, (tBTA_AV_MEDIA *)p_pkt);
osi_free(p_pkt); /* a copy of packet had been delivered, we free this buffer */
/* packet will be free by upper */
}
/*******************************************************************************
@@ -843,7 +852,7 @@ void bta_av_role_res (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
bdcpy(av_open.bd_addr, p_scb->peer_addr);
av_open.chnl = p_scb->chnl;
av_open.hndl = p_scb->hndl;
start.status = BTA_AV_FAIL_ROLE;
av_open.status = BTA_AV_FAIL_ROLE;
if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC ) {
av_open.sep = AVDT_TSEP_SNK;
} else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK ) {
@@ -1271,7 +1280,7 @@ void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
if (AVDT_TSEP_SNK == local_sep) {
if ((p_data->ci_setconfig.err_code == AVDT_SUCCESS) &&
(p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL)) {
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) {
@@ -1384,7 +1393,7 @@ void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
/* TODO check if other audio channel is open.
* If yes, check if reconfig is needed
* Rigt now we do not do this kind of checking.
* Right now we do not do this kind of checking.
* BTA-AV is INT for 2nd audio connection.
* The application needs to make sure the current codec_info is proper.
* If one audio connection is open and another SNK attempts to connect to AV,
@@ -1397,6 +1406,7 @@ void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
open.status = BTA_AV_SUCCESS;
open.starting = bta_av_chk_start(p_scb);
open.edr = 0;
open.mtu = mtu;
if ( NULL != (p = BTM_ReadRemoteFeatures(p_scb->peer_addr))) {
if (HCI_EDR_ACL_2MPS_SUPPORTED(p)) {
open.edr |= BTA_AV_EDR_2MBPS;
@@ -1533,7 +1543,7 @@ void bta_av_do_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
AVDT_CloseReq(p_scb->avdt_handle);
/* just in case that the link is congested, link is flow controled by peer or
/* just in case that the link is congested, link is flow controlled by peer or
* for whatever reason the the close request can not be sent in time.
* when this timer expires, AVDT_DisconnectReq will be called to disconnect the link
*/
@@ -1814,7 +1824,7 @@ void bta_av_open_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
}
/* if there is already an active AV connnection with the same bd_addr,
/* if there is already an active AV connection with the same bd_addr,
don't send disconnect req, just report the open event with BTA_AV_FAIL_GET_CAP status */
if (is_av_opened == TRUE) {
bdcpy(open.bd_addr, p_scb->peer_addr);
@@ -1856,7 +1866,7 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
tAVDT_CFG cfg;
UINT8 media_type;
tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx];
UINT16 uuid_int; /* UUID for which connection was initiatied */
UINT16 uuid_int; /* UUID for which connection was initiated */
tBTA_AV_SNK_PSC_CFG psc_cfg = {0};
memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
@@ -1899,9 +1909,9 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
p_scb->cur_psc_mask = cfg.psc_mask;
if (uuid_int == UUID_SERVCLASS_AUDIO_SINK) {
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
APPL_TRACE_DEBUG(" Configure Deoder for Sink Connection ");
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT) {
@@ -1909,6 +1919,13 @@ void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
}
(*bta_av_cb.p_cback)(BTA_AV_SNK_PSC_CFG_EVT, (tBTA_AV *)&psc_cfg);
}
else {
/* UUID_SERVCLASS_AUDIO_SOURCE */
if (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL) {
p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_CFG_EVT,
(tBTA_AV_MEDIA *)p_scb->cfg.codec_info);
}
}
/* open the stream */
AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,
@@ -2387,7 +2404,7 @@ void bta_av_start_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
/* If sink starts stream, disable sniff mode here */
if (!initiator) {
/* If souce is the master role, disable role switch during streaming.
/* If source is the master role, disable role switch during streaming.
* Otherwise allow role switch, if source is slave.
* Because it would not hurt source, if the peer device wants source to be master */
if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) &&
@@ -2502,7 +2519,7 @@ void bta_av_str_closed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
bta_av_str_stopped(p_scb, NULL);
}
/* Update common mtu shared by remaining connectons */
/* Update common mtu shared by remaining connections */
mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU);
{

View File

@@ -129,6 +129,23 @@ void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id,
}
}
void BTA_AvRegSEP(tBTA_AV_CHNL chnl, UINT8 seid, UINT8 tsep, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, tBTA_AV_DATA_CBACK *p_data_cback)
{
tBTA_AV_API_REG_SEP *p_buf;
if ((p_buf = (tBTA_AV_API_REG_SEP *) osi_malloc(sizeof(tBTA_AV_API_REG_SEP))) != NULL) {
p_buf->hdr.layer_specific = chnl;
p_buf->hdr.event = BTA_AV_API_REG_SEP_EVT;
p_buf->seid = seid;
p_buf->tsep = tsep;
p_buf->codec_type = codec_type;
memcpy(p_buf->codec_info, p_codec_info, AVDT_CODEC_SIZE);
p_buf->p_data_cback = p_data_cback;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AvDeregister

View File

@@ -151,6 +151,7 @@ static const tBTA_AV_ST_TBL bta_av_st_tbl[] = {
typedef void (*tBTA_AV_NSM_ACT)(tBTA_AV_DATA *p_data);
static void bta_av_api_enable(tBTA_AV_DATA *p_data);
static void bta_av_api_register(tBTA_AV_DATA *p_data);
static void bta_av_api_reg_sep(tBTA_AV_DATA *p_data);
#if (BTA_AV_SINK_INCLUDED == TRUE)
static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data);
static void bta_av_api_get_delay_value(tBTA_AV_DATA *p_data);
@@ -170,6 +171,7 @@ static void bta_av_sys_rs_cback (tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8 ap
const tBTA_AV_NSM_ACT bta_av_nsm_act[] = {
bta_av_api_enable, /* BTA_AV_API_ENABLE_EVT */
bta_av_api_register, /* BTA_AV_API_REGISTER_EVT */
bta_av_api_reg_sep, /* BTA_AV_API_REG_SEP_EVT */
bta_av_api_deregister, /* BTA_AV_API_DEREGISTER_EVT */
bta_av_api_disconnect, /* BTA_AV_API_DISCONNECT_EVT */
bta_av_ci_data, /* BTA_AV_CI_SRC_DATA_READY_EVT */
@@ -533,9 +535,11 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
tAVDT_REG reg;
tAVDT_CS cs;
char *p_service_name;
tBTA_AV_CODEC codec_type;
tBTA_UTL_COD cod;
#if (BTA_AV_EXT_CODEC == FALSE)
tBTA_AV_CODEC codec_type;
UINT8 index = 0;
#endif
char p_avk_service_name[BTA_SERVICE_NAME_LEN + 1];
BCM_STRNCPY_S(p_avk_service_name, BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN);
p_avk_service_name[BTA_SERVICE_NAME_LEN] = '\0';
@@ -658,6 +662,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
/* keep the configuration in the stream control block */
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,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg.tsep) == TRUE) {
@@ -673,12 +678,13 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
#if (BTA_AV_SINK_INCLUDED == TRUE)
p_scb->seps[index].tsep = cs.tsep;
if (cs.tsep == AVDT_TSEP_SNK) {
p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
} else {
p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */
}
// if (cs.tsep == AVDT_TSEP_SNK) {
// p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
// } else {
// p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */
// }
#endif
p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
APPL_TRACE_DEBUG("audio[%d] av_handle: %d codec_type: %d\n",
index, p_scb->seps[index].av_handle, p_scb->seps[index].codec_type);
@@ -687,6 +693,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
break;
}
}
#endif
if (!bta_av_cb.reg_audio) {
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
@@ -744,6 +751,86 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
(*bta_av_cb.p_cback)(BTA_AV_REGISTER_EVT, (tBTA_AV *)&registr);
}
static void bta_av_api_reg_sep(tBTA_AV_DATA *p_data)
{
UNUSED(p_data);
#if (BTA_AV_EXT_CODEC == TRUE)
tAVDT_CS cs;
tBTA_AV_CODEC codec_type;
UINT8 index = p_data->api_reg_sep.seid;
tBTA_AV_SEP_REG sep_reg;
sep_reg.seid = p_data->api_reg_sep.seid;
sep_reg.reg_state = BTA_AV_FAIL;
if (index > BTA_AV_MAX_SEPS || p_data->hdr.layer_specific != BTA_AV_CHNL_AUDIO) {
(*bta_av_cb.p_cback)(BTA_AV_SEP_REG_EVT, (tBTA_AV *)&sep_reg);
APPL_TRACE_WARNING("%s invalid parameter: seid %d, ch %d", __FUNCTION__, index, p_data->hdr.layer_specific);
return;
}
memset(&cs, 0, sizeof(tAVDT_CS));
cs.cfg.num_codec = 1;
cs.tsep = p_data->api_reg_sep.tsep;
cs.nsc_mask = AVDT_NSC_RECONFIG | ((bta_av_cb.features & BTA_AV_FEAT_PROTECT) ? 0 : AVDT_NSC_SECURITY);
for (int xx = 0; xx < BTA_AV_NUM_STRS; xx++) {
if (bta_av_cb.p_scb[xx] != NULL && bta_av_cb.p_scb[xx]->chnl == BTA_AV_CHNL_AUDIO) {
tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[xx];
cs.p_ctrl_cback = bta_av_dt_cback[p_scb->hdi];
cs.cfg.psc_mask = AVDT_PSC_TRANS;
cs.media_type = AVDT_MEDIA_AUDIO;
cs.mtu = p_bta_av_cfg->audio_mtu;
cs.flush_to = L2CAP_DEFAULT_FLUSH_TO;
#if AVDT_REPORTING == TRUE
if (bta_av_cb.features & BTA_AV_FEAT_REPORT) {
cs.cfg.psc_mask |= AVDT_PSC_REPORT;
cs.p_report_cback = bta_av_a2dp_report_cback;
#if AVDT_MULTIPLEXING == TRUE
cs.cfg.mux_tsid_report = 2;
#endif
}
#endif
if (bta_av_cb.features & BTA_AV_FEAT_DELAY_RPT) {
cs.cfg.psc_mask |= AVDT_PSC_DELAY_RPT;
}
/* todo: check whether this memcpy is necessary */
memcpy(&p_scb->cfg, &cs.cfg, sizeof(tAVDT_CFG));
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,
&cs.cfg.num_protect, cs.cfg.protect_info, p_data->api_reg_sep.tsep) == TRUE) {
#if (BTA_AV_SINK_INCLUDED == TRUE)
if (p_data->api_reg_sep.tsep == AVDT_TSEP_SNK) {
cs.p_data_cback = bta_av_stream_data_cback;
}
#endif
if ((p_scb->seps[index].av_handle != 0) && (AVDT_RemoveStream(p_scb->seps[index].av_handle) != AVDT_SUCCESS)) {
APPL_TRACE_WARNING("%s fail to remove exist sep", __FUNCTION__);
}
if (AVDT_CreateStream(&p_scb->seps[index].av_handle, &cs) == AVDT_SUCCESS) {
p_scb->seps[index].codec_type = codec_type;
p_scb->seps[index].tsep = cs.tsep;
p_scb->seps[index].p_app_data_cback = p_data->api_reg_sep.p_data_cback;
} else {
APPL_TRACE_WARNING("%s fail to create sep", __FUNCTION__);
break;
}
}
else {
APPL_TRACE_WARNING("%s invalid codec capability", __FUNCTION__);
}
}
}
sep_reg.reg_state = BTA_AV_SUCCESS;
(*bta_av_cb.p_cback)(BTA_AV_SEP_REG_EVT, (tBTA_AV *)&sep_reg);
#endif
}
/*******************************************************************************
**
** Function bta_av_api_deregister
@@ -1415,7 +1502,8 @@ char *bta_av_action_code(UINT16 action_code)
case 46: return "BTA_AV_DELAY_CO";
case 47: return "BTA_AV_OPEN_AT_INC";
case 48: return "BTA_AV_OPEN_FAIL_SDP";
case 49: return "NULL";
case 49: return "BTA_AV_SET_DELAY_VALUE";
case 50: return "NULL";
default: return "unknown";
}
}

View File

@@ -581,6 +581,64 @@ UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap)
return status;
}
/*******************************************************************************
**
** Function bta_av_sbc_cfg_in_external_codec_cap
**
** Description This function checks whether an SBC codec configuration
** is allowable for the given external codec capabilities.
**
** Returns 0 if ok, nonzero if error.
**
*******************************************************************************/
UINT8 bta_av_sbc_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap)
{
UINT8 status = 0;
tA2D_SBC_CIE cfg_cie;
tA2D_SBC_CIE cap_cie;
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, FALSE)) != 0) {
return status;
}
/* parse capability */
if ((status = A2D_ParsSbcInfo(&cap_cie, p_cap, TRUE)) != 0) {
return status;
}
/* verify that each parameter is in range */
/* sampling frequency */
if ((cfg_cie.samp_freq & cap_cie.samp_freq) == 0) {
status = A2D_NS_SAMP_FREQ;
}
/* channel mode */
else if ((cfg_cie.ch_mode & cap_cie.ch_mode) == 0) {
status = A2D_NS_CH_MODE;
}
/* block length */
else if ((cfg_cie.block_len & cap_cie.block_len) == 0) {
status = A2D_BAD_BLOCK_LEN;
}
/* subbands */
else if ((cfg_cie.num_subbands & cap_cie.num_subbands) == 0) {
status = A2D_NS_SUBBANDS;
}
/* allocation method */
else if ((cfg_cie.alloc_mthd & cap_cie.alloc_mthd) == 0) {
status = A2D_NS_ALLOC_MTHD;
}
/* max bitpool */
else if (cfg_cie.max_bitpool > cap_cie.max_bitpool) {
status = A2D_NS_MAX_BITPOOL;
}
/* min bitpool */
else if (cfg_cie.min_bitpool < cap_cie.min_bitpool) {
status = A2D_NS_MIN_BITPOOL;
}
return status;
}
/*******************************************************************************
**
** Function bta_av_sbc_bld_hdr

View File

@@ -98,6 +98,7 @@ enum {
/* these events are handled outside of the state machine */
BTA_AV_API_ENABLE_EVT,
BTA_AV_API_REGISTER_EVT,
BTA_AV_API_REG_SEP_EVT,
BTA_AV_API_DEREGISTER_EVT,
BTA_AV_API_DISCONNECT_EVT,
BTA_AV_CI_SRC_DATA_READY_EVT,
@@ -195,6 +196,15 @@ typedef struct {
tBTA_AVRC_CO_FUNCTS *bta_avrc_cos;
} tBTA_AV_API_REG;
/* data type for BTA_AV_API_REG_SEP_EVT */
typedef struct {
BT_HDR hdr;
UINT8 seid;
UINT8 tsep;
tBTA_AV_CODEC codec_type;
UINT8 codec_info[AVDT_CODEC_SIZE];
tBTA_AV_DATA_CBACK *p_data_cback;
} tBTA_AV_API_REG_SEP;
enum {
BTA_AV_RS_NONE, /* straight API call */
@@ -425,6 +435,7 @@ typedef union {
BT_HDR hdr;
tBTA_AV_API_ENABLE api_enable;
tBTA_AV_API_REG api_reg;
tBTA_AV_API_REG_SEP api_reg_sep;
tBTA_AV_API_OPEN api_open;
tBTA_AV_API_STOP api_stop;
tBTA_AV_API_DISCNT api_discnt;

View File

@@ -256,18 +256,19 @@ typedef UINT8 tBTA_AV_GET_TYPE;
#define BTA_AV_META_MSG_EVT 17 /* metadata messages */
#define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */
#define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */
#define BTA_AV_MEDIA_SINK_CFG_EVT 20 /* command to configure codec */
#define BTA_AV_MEDIA_CFG_EVT 20 /* command to configure codec */
#define BTA_AV_MEDIA_DATA_EVT 21 /* sending data to Media Task */
#define BTA_AV_SET_DELAY_VALUE_EVT 22 /* set delay reporting value */
#define BTA_AV_GET_DELAY_VALUE_EVT 23 /* get delay reporting value */
#define BTA_AV_SNK_PSC_CFG_EVT 24 /* Protocol service capabilities. */
#define BTA_AV_SEP_REG_EVT 25 /* stream endpoint registered */
/* still keep Cover Art event here if Cover Art feature not enabled */
#define BTA_AV_CA_STATUS_EVT 25 /* Cover Art Client status event */
#define BTA_AV_CA_DATA_EVT 26 /* Cover Art response body data */
#define BTA_AV_CA_STATUS_EVT 26 /* Cover Art Client status event */
#define BTA_AV_CA_DATA_EVT 27 /* Cover Art response body data */
/* Max BTA event */
#define BTA_AV_MAX_EVT 27
#define BTA_AV_MAX_EVT 28
/* function types for call-out functions */
@@ -341,6 +342,12 @@ typedef struct {
tBTA_AVRC_CO_FUNCTS *p_bta_avrc_cos;
} tBTA_AV_REGISTER;
/* Event associated with BTA_AV_SEP_REG_EVT */
typedef struct {
UINT8 seid;
tBTA_AV_STATUS reg_state;
} tBTA_AV_SEP_REG;
/* data associated with BTA_AV_OPEN_EVT */
#define BTA_AV_EDR_2MBPS 0x01
#define BTA_AV_EDR_3MBPS 0x02
@@ -354,6 +361,7 @@ typedef struct {
BOOLEAN starting;
tBTA_AV_EDR edr; /* 0, if peer device does not support EDR */
UINT8 sep; /* sep type of peer device */
UINT16 mtu;
} tBTA_AV_OPEN;
/* data associated with BTA_AV_CLOSE_EVT */
@@ -519,6 +527,7 @@ typedef union {
tBTA_AV_CHNL chnl;
tBTA_AV_ENABLE enable;
tBTA_AV_REGISTER registr;
tBTA_AV_SEP_REG sep_reg;
tBTA_AV_OPEN open;
tBTA_AV_CLOSE close;
tBTA_AV_START start;
@@ -657,6 +666,8 @@ void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name,
tBTA_AV_CO_FUNCTS *bta_av_cos, tBTA_AVRC_CO_FUNCTS *bta_avrc_cos,
UINT8 tsep);
void BTA_AvRegSEP(tBTA_AV_CHNL chnl, UINT8 idx, UINT8 tsep, tBTA_AV_CODEC codec_type, UINT8 *p_codec_info, tBTA_AV_DATA_CBACK *p_data_cback);
/*******************************************************************************
**
** Function BTA_AvDeregister

View File

@@ -194,6 +194,18 @@ extern UINT8 bta_av_sbc_cfg_for_cap(UINT8 *p_peer, tA2D_SBC_CIE *p_cap, tA2D_SBC
*******************************************************************************/
extern UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap);
/*******************************************************************************
**
** Function bta_av_sbc_cfg_in_external_codec_cap
**
** Description This function checks whether an SBC codec configuration
** is allowable for the given external codec capabilities.
**
** Returns 0 if ok, nonzero if error.
**
*******************************************************************************/
extern UINT8 bta_av_sbc_cfg_in_external_codec_cap(UINT8 *p_cfg, UINT8 *p_cap);
/*******************************************************************************
**
** Function bta_av_sbc_cfg_matches_cap

View File

@@ -122,7 +122,6 @@ static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8
static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index);
/*******************************************************************************
**
** Function bta_av_co_cp_is_active
@@ -245,18 +244,29 @@ BOOLEAN bta_av_co_audio_init(UINT8 *p_codec_type, UINT8 *p_codec_info, UINT8 *p_
UINT16_TO_STREAM(p, BTA_AV_CP_SCMS_T_ID);
} while (0);
#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_audio_codec_reset();
#else
/* Set up for SBC codec for SRC*/
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_caps, p_codec_info);
#endif
return TRUE;
} 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_audio_codec_reset();
#else
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_sink_caps, p_codec_info);
#endif
/* Codec is valid */
return TRUE;
} else {
@@ -387,7 +397,7 @@ void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
/* now try to build a preferred one */
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0) {
APPL_TRACE_DEBUG(" Cant parse src cap ret = %d", status);
APPL_TRACE_DEBUG(" Can't parse src cap ret = %d", status);
return ;
}
@@ -1062,12 +1072,12 @@ static BOOLEAN bta_av_co_audio_codec_build_config(const UINT8 *p_codec_caps, UIN
memcpy(p_codec_cfg, bta_av_co_cb.codec_cfg.info, BTA_AV_CO_SBC_MAX_BITPOOL_OFF + 1);
/* Update the bit pool boundaries with the codec capabilities */
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF];
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] = p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF];
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF] = BTA_AV_CO_MAX(p_codec_caps[BTA_AV_CO_SBC_MIN_BITPOOL_OFF], p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF]);
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF] = BTA_AV_CO_MIN(p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF], p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
APPL_TRACE_EVENT("bta_av_co_audio_codec_build_config : bitpool min %d, max %d",
p_codec_cfg[BTA_AV_CO_SBC_MIN_BITPOOL_OFF],
p_codec_caps[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
p_codec_cfg[BTA_AV_CO_SBC_MAX_BITPOOL_OFF]);
break;
default:
APPL_TRACE_ERROR("bta_av_co_audio_codec_build_config: unsupported codec id %d", bta_av_co_cb.codec_cfg.id);
@@ -1301,10 +1311,20 @@ static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer,
if (p_src_index) {
*p_src_index = index;
}
if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps,
(tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps)) {
#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:
@@ -1334,7 +1354,11 @@ static UINT8 bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8
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);
#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:
@@ -1365,7 +1389,11 @@ static UINT8 bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8
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);
#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:
@@ -1478,12 +1506,16 @@ void bta_av_co_audio_codec_reset(void)
osi_mutex_global_lock();
FUNC_TRACE();
#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);
#else
/* Reset the current configuration to SBC */
bta_av_co_cb.codec_cfg.id = BTC_AV_CODEC_SBC;
if (A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, (tA2D_SBC_CIE *)&btc_av_sbc_default_config, bta_av_co_cb.codec_cfg.info) != A2D_SUCCESS) {
APPL_TRACE_ERROR("bta_av_co_audio_codec_reset A2D_BldSbcInfo failed");
}
#endif
osi_mutex_global_unlock();
}
@@ -1564,7 +1596,6 @@ BOOLEAN bta_av_co_audio_set_codec(const tBTC_AV_MEDIA_FEEDINGS *p_feeding, tBTC_
/* The new config was correctly built */
bta_av_co_cb.codec_cfg = new_cfg;
/* Check all devices support it */
*p_status = BTC_AV_SUCCESS;
return bta_av_co_audio_codec_supported(p_status);
@@ -1666,13 +1697,16 @@ void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl)
** Returns Nothing
**
*******************************************************************************/
void bta_av_co_init(void)
void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps)
{
FUNC_TRACE();
/* Reset the control block */
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));
}
bta_av_co_cb.codec_cfg_setconfig.id = BTC_AV_CODEC_NONE;
#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
@@ -1685,7 +1719,6 @@ void bta_av_co_init(void)
bta_av_co_audio_codec_reset();
}
/*******************************************************************************
**
** Function bta_av_co_peer_cp_supported
@@ -1729,7 +1762,7 @@ BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl)
** of our exported bitpool range. If set we will set the
** remote preference.
**
** Returns TRUE if config set, FALSE otherwize
** Returns TRUE if config set, FALSE otherwise
**
*******************************************************************************/

View File

@@ -49,7 +49,12 @@ void btc_a2dp_on_idle(void)
}
#endif // BTC_AV_SRC_INCLUDED
bta_av_co_init();
#if (BTC_AV_EXT_CODEC == TRUE)
tBTC_AV_CODEC_INFO *codec_caps = btc_av_codec_cap_get();
#else
tBTC_AV_CODEC_INFO *codec_caps = NULL;
#endif
bta_av_co_init(codec_caps);
#if BTC_AV_SINK_INCLUDED
if (btc_av_get_peer_sep() == AVDT_TSEP_SRC && btc_av_get_service_id() == BTA_A2DP_SINK_SERVICE_ID) {
@@ -88,9 +93,11 @@ BOOLEAN btc_a2dp_on_started(tBTA_AV_START *p_av, BOOLEAN pending_start)
ack = TRUE;
}
} else {
#if (BTC_AV_EXT_CODEC == FALSE)
/* we were remotely started, make sure codec
is setup before datapath is started */
btc_a2dp_source_setup_codec();
#endif
}
/* media task is autostarted upon a2dp audiopath connection */

View File

@@ -71,8 +71,10 @@ static void btc_a2dp_datapath_open(void)
/* Start the media task to encode SBC */
btc_a2dp_source_start_audio_req();
#if (BTC_AV_EXT_CODEC == FALSE)
/* make sure we update any changed sbc encoder params */
btc_a2dp_source_encoder_update();
#endif
}
#endif
#if (BTC_AV_SINK_INCLUDED == TRUE)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -30,12 +30,13 @@
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "oi_codec_sbc.h"
#include "oi_status.h"
#include "osi/future.h"
#include <assert.h>
#if (BTC_AV_SINK_INCLUDED == TRUE)
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE)
#include "oi_codec_sbc.h"
#include "oi_status.h"
/*****************************************************************************
** Constants
@@ -85,13 +86,6 @@ typedef struct {
void *param;
} a2dp_sink_task_evt_t;
typedef struct {
UINT16 num_frames_to_be_processed;
UINT16 len;
UINT16 offset;
UINT16 layer_specific;
} tBT_SBC_HDR;
typedef struct {
BOOLEAN rx_flush; /* discards any incoming data when true */
UINT8 channel_count;
@@ -121,7 +115,7 @@ static void btc_a2dp_sink_rx_flush(void);
static int btc_a2dp_sink_get_track_frequency(UINT8 frequency);
static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype);
/* Handle incoming media packets A2DP SINK streaming*/
static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg);
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg);
static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg);
static void btc_a2dp_sink_handle_clear_track(void);
static BOOLEAN btc_a2dp_sink_clear_track(void);
@@ -346,7 +340,7 @@ void btc_a2dp_sink_reset_decoder(UINT8 *p_av)
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
{
tBT_SBC_HDR *p_msg;
BT_HDR *p_msg;
int nb_of_msgs_to_process = 0;
if (fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ)) {
@@ -362,13 +356,12 @@ static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
return;
}
p_msg = (tBT_SBC_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0);
p_msg = (BT_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, 0);
if ( p_msg == NULL ) {
APPL_TRACE_ERROR("Insufficient data in que ");
break;
}
btc_a2dp_sink_handle_inc_media(p_msg);
osi_free(p_msg);
nb_of_msgs_to_process--;
}
APPL_TRACE_DEBUG(" Process Frames - ");
@@ -526,25 +519,27 @@ static void btc_a2dp_sink_handle_decoder_reset(tBTC_MEDIA_SINK_CFG_UPDATE *p_msg
** Returns void
**
*******************************************************************************/
static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
{
UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
int count;
UINT32 pcmBytes, availPcmBytes;
OI_INT16 *pcmDataPointer = a2dp_sink_local_param.pcmData; /*Will be overwritten on next packet receipt*/
OI_STATUS status;
int num_sbc_frames = p_msg->num_frames_to_be_processed;
int num_sbc_frames = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
UINT32 sbc_frame_len = p_msg->len - 1;
availPcmBytes = sizeof(a2dp_sink_local_param.pcmData);
/* XXX: Check if the below check is correct, we are checking for peer to be sink when we are sink */
if (btc_av_get_peer_sep() == AVDT_TSEP_SNK || (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush)) {
APPL_TRACE_DEBUG(" State Changed happened in this tick ");
osi_free(p_msg);
return;
}
// ignore data if no one is listening
if (!btc_a2dp_control_get_datachnl_stat()) {
osi_free(p_msg);
return;
}
@@ -565,9 +560,9 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) {
pcmBytes = availPcmBytes;
status = OI_CODEC_SBC_DecodeFrame(&a2dp_sink_local_param.context, (const OI_BYTE **)&sbc_start_frame,
(OI_UINT32 *)&sbc_frame_len,
(OI_INT16 *)pcmDataPointer,
(OI_UINT32 *)&pcmBytes);
(OI_UINT32 *)&sbc_frame_len,
(OI_INT16 *)pcmDataPointer,
(OI_UINT32 *)&pcmBytes);
if (!OI_SUCCESS(status)) {
APPL_TRACE_ERROR("Decoding failure: %d\n", status);
break;
@@ -577,6 +572,7 @@ static void btc_a2dp_sink_handle_inc_media(tBT_SBC_HDR *p_msg)
p_msg->offset += (p_msg->len - 1) - sbc_frame_len;
p_msg->len = sbc_frame_len + 1;
}
osi_free(p_msg);
btc_a2d_data_cb_to_app((uint8_t *)a2dp_sink_local_param.pcmData, (sizeof(a2dp_sink_local_param.pcmData) - availPcmBytes));
}
@@ -663,35 +659,28 @@ static int btc_a2dp_sink_get_track_channel_count(UINT8 channeltype)
*******************************************************************************/
UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt)
{
tBT_SBC_HDR *p_msg;
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
osi_free(p_pkt);
return 0;
}
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) { /* Flush enabled, do not enqueue */
osi_free(p_pkt);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ);
}
if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) {
osi_free(p_pkt);
APPL_TRACE_WARNING("Pkt dropped\n");
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ);
}
APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + ");
/* allocate and Queue this buffer */
if ((p_msg = (tBT_SBC_HDR *) osi_malloc(sizeof(tBT_SBC_HDR) +
p_pkt->offset + p_pkt->len)) != NULL) {
memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len));
p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
APPL_TRACE_VERBOSE("btc_a2dp_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed);
fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_msg, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
} else {
/* let caller deal with a failed allocation */
APPL_TRACE_WARNING("btc_a2dp_sink_enque_buf No Buffer left - ");
}
// p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ, p_pkt, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.RxSbcQ);
}
@@ -749,4 +738,4 @@ static void btc_a2dp_sink_thread_cleanup(UNUSED_ATTR void *context)
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = NULL;
}
#endif /* BTC_AV_SINK_INCLUDED */
#endif /* (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE) */

View File

@@ -0,0 +1,286 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#include "common/bt_trace.h"
#include <string.h>
#include <stdint.h>
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#include "osi/thread.h"
#include "osi/fixed_queue.h"
#include "stack/a2d_api.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_ci.h"
#include "btc_av_co.h"
#include "btc_a2dp.h"
#include "btc_a2dp_control.h"
#include "btc_a2dp_sink.h"
#include "btc/btc_manage.h"
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "osi/future.h"
#include <assert.h>
#if (BTC_AV_SINK_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE)
#define MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ (25)
#define BTC_A2DP_SNK_DATA_QUEUE_IDX (1)
enum {
BTC_A2DP_SINK_STATE_OFF = 0,
BTC_A2DP_SINK_STATE_ON = 1,
};
typedef struct {
BOOLEAN rx_flush; /* discards any incoming data when true */
struct osi_event *data_ready_event;
fixed_queue_t *audio_rx_q;
} tBTC_A2DP_SINK_CB;
typedef struct {
uint16_t expected_seq_num;
bool seq_num_recount;
} a2dp_sink_media_pkt_seq_num_t;
typedef struct {
tBTC_A2DP_SINK_CB btc_aa_snk_cb;
osi_thread_t *btc_aa_snk_task_hdl;
a2dp_sink_media_pkt_seq_num_t media_pkt_seq_num;
} a2dp_sink_local_param_t;
static int btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF;
static esp_a2d_sink_audio_data_cb_t bt_aa_snk_audio_data_cb = NULL;
#if A2D_DYNAMIC_MEMORY == FALSE
static a2dp_sink_local_param_t a2dp_sink_local_param;
#else
static a2dp_sink_local_param_t *a2dp_sink_local_param_ptr;
#define a2dp_sink_local_param (*a2dp_sink_local_param_ptr)
#endif ///A2D_DYNAMIC_MEMORY == FALSE
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context);
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg);
void btc_a2dp_sink_reg_audio_data_cb(esp_a2d_sink_audio_data_cb_t callback)
{
bt_aa_snk_audio_data_cb = callback;
}
static inline void btc_a2d_audio_data_cb_to_app(uint16_t conn_hdl, uint8_t *buf, uint8_t *data, uint16_t len, uint16_t number_frame, uint32_t timestamp)
{
if (bt_aa_snk_audio_data_cb) {
/* AVDT media packet offset is larger than sizeof(esp_a2d_audio_buff_t), it safe to do this */
esp_a2d_audio_buff_t *audio_buff = (esp_a2d_audio_buff_t *)buf;
audio_buff->buff_size = len;
audio_buff->number_frame = number_frame;
audio_buff->data_len = len;
audio_buff->data = data;
audio_buff->timestamp = timestamp;
bt_aa_snk_audio_data_cb(conn_hdl, audio_buff);
}
else {
osi_free(buf);
}
}
static void btc_a2dp_sink_rx_flush(void)
{
while (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
osi_free(fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, 0));
}
}
bool btc_a2dp_sink_startup(void)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_OFF) {
APPL_TRACE_ERROR("a2dp sink already start up");
return false;
}
#if A2D_DYNAMIC_MEMORY == TRUE
if ((a2dp_sink_local_param_ptr = (a2dp_sink_local_param_t *)osi_malloc(sizeof(a2dp_sink_local_param_t))) == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
return false;
}
memset((void *)a2dp_sink_local_param_ptr, 0, sizeof(a2dp_sink_local_param_t));
#endif
a2dp_sink_local_param.btc_aa_snk_task_hdl = btc_get_current_thread();
struct osi_event *data_event = osi_event_create(btc_a2dp_sink_data_ready, NULL);
assert (data_event != NULL);
osi_event_bind(data_event, a2dp_sink_local_param.btc_aa_snk_task_hdl, BTC_A2DP_SNK_DATA_QUEUE_IDX);
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = data_event;
a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q = fixed_queue_new(QUEUE_SIZE_MAX);
btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON;
btc_a2dp_control_init();
return true;
}
void btc_a2dp_sink_shutdown(void)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON) {
APPL_TRACE_ERROR("a2dp sink already shutdown");
return;
}
btc_a2dp_control_set_datachnl_stat(FALSE);
btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_OFF;
btc_a2dp_control_cleanup();
fixed_queue_free(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, osi_free_func);
a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q = NULL;
osi_event_delete(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event);
a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event = NULL;
a2dp_sink_local_param.btc_aa_snk_task_hdl = NULL;
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_sink_local_param_ptr);
a2dp_sink_local_param_ptr = NULL;
#endif
}
void btc_a2dp_sink_on_idle(void)
{
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_on_stopped(tBTA_AV_SUSPEND *p_av)
{
UNUSED(p_av);
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_control_set_datachnl_stat(FALSE);
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_on_suspended(tBTA_AV_SUSPEND *p_av)
{
UNUSED(p_av);
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = TRUE;
btc_a2dp_sink_rx_flush();
}
void btc_a2dp_sink_set_rx_flush(BOOLEAN enable)
{
if (enable == FALSE) {
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num = 0x1;
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount = true;
}
a2dp_sink_local_param.btc_aa_snk_cb.rx_flush = enable;
btc_a2dp_sink_rx_flush();
}
static void btc_a2dp_sink_data_ready(UNUSED_ATTR void *context)
{
BT_HDR *p_msg;
int nb_of_msgs_to_process = 0;
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
return;
}
if (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) {
btc_a2dp_sink_rx_flush();
return;
}
nb_of_msgs_to_process = fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
APPL_TRACE_DEBUG("%s nb msgs:%d", __func__, nb_of_msgs_to_process);
while (nb_of_msgs_to_process > 0) {
p_msg = (BT_HDR *)fixed_queue_dequeue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, 0);
if ( p_msg == NULL ) {
APPL_TRACE_ERROR("%s insufficient data in queue", __func__);
break;
}
btc_a2dp_sink_handle_inc_media(p_msg);
nb_of_msgs_to_process--;
}
if (!fixed_queue_is_empty(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q)) {
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
}
}
}
static void btc_a2dp_sink_handle_inc_media(BT_HDR *p_msg)
{
UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
int num_sbc_frames = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
UINT32 sbc_frame_len = p_msg->len - 1;
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush) {
osi_free(p_msg);
return;
}
/* ignore data if no one is listening */
if (!btc_a2dp_control_get_datachnl_stat()) {
osi_free(p_msg);
return;
}
if (p_msg->layer_specific != a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num) {
/* Because the sequence number of some devices is not recounted */
if (!a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount ||
a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num != 0x1) {
APPL_TRACE_WARNING("Sequence numbers error, recv:0x%x, expect:0x%x, recount:0x%x",
p_msg->layer_specific, a2dp_sink_local_param.media_pkt_seq_num.expected_seq_num,
a2dp_sink_local_param.media_pkt_seq_num.seq_num_recount);
}
}
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);
UINT32 timestamp = *((UINT32 *) (p_msg + 1));
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);
/* dont free p_msg here */
}
UINT8 btc_a2dp_sink_enque_buf(BT_HDR *p_pkt)
{
if (btc_a2dp_sink_state != BTC_A2DP_SINK_STATE_ON){
osi_free(p_pkt);
return 0;
}
if (a2dp_sink_local_param.btc_aa_snk_cb.rx_flush == TRUE) {
/* Flush enabled, do not enqueue */
osi_free(p_pkt);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
if (fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q) >= MAX_OUTPUT_A2DP_SNK_FRAME_QUEUE_SZ) {
osi_free(p_pkt);
APPL_TRACE_WARNING("Pkt dropped\n");
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
APPL_TRACE_DEBUG("btc_a2dp_sink_enque_buf + ");
// p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
fixed_queue_enqueue(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q, p_pkt, FIXED_QUEUE_MAX_TIMEOUT);
osi_thread_post_event(a2dp_sink_local_param.btc_aa_snk_cb.data_ready_event, OSI_THREAD_MAX_TIMEOUT);
return fixed_queue_length(a2dp_sink_local_param.btc_aa_snk_cb.audio_rx_q);
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -36,11 +36,12 @@
#include "btc_av.h"
#include "btc/btc_util.h"
#include "esp_a2dp_api.h"
#include "sbc_encoder.h"
#include "osi/future.h"
#include <assert.h>
#if BTC_AV_SRC_INCLUDED
#if (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE)
#include "sbc_encoder.h"
/*****************************************************************************
** BQB global variables
@@ -83,12 +84,6 @@ enum {
#define BTC_MEDIA_AA_BUF_SIZE (4096+16)
#if (BTA_AV_CO_CP_SCMS_T == TRUE)
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
#else
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
#endif
#ifndef BTC_MEDIA_BITRATE_STEP
#define BTC_MEDIA_BITRATE_STEP 5
#endif
@@ -170,6 +165,10 @@ typedef struct {
UINT64 last_frame_us;
} a2dp_source_local_param_t;
static BOOLEAN btc_a2dp_source_stop_audio_req(void);
static BOOLEAN btc_a2dp_source_tx_flush_req(void);
static BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg);
static void btc_a2dp_source_thread_init(UNUSED_ATTR void *context);
static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context);
static void btc_a2dp_source_flush_q(fixed_queue_t *p_q);
@@ -226,11 +225,6 @@ static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t
** BTC ADAPTATION
*****************************************************************************/
bool btc_a2dp_source_is_streaming(void)
{
return a2dp_source_local_param.btc_aa_src_cb.is_tx_timer == TRUE;
}
bool btc_a2dp_source_is_task_shutting_down(void)
{
return btc_a2dp_source_state == BTC_A2DP_SOURCE_STATE_SHUTTING_DOWN;
@@ -505,7 +499,7 @@ BOOLEAN btc_a2dp_source_start_audio_req(void)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_stop_audio_req(void)
static BOOLEAN btc_a2dp_source_stop_audio_req(void)
{
/*
* Explicitly check whether the btc_aa_src_ctrl_queue is not NULL to
@@ -536,7 +530,7 @@ BOOLEAN btc_a2dp_source_stop_audio_req(void)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
static BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
{
tBTC_MEDIA_INIT_AUDIO *p_buf;
if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_INIT_AUDIO)))) {
@@ -559,7 +553,7 @@ BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg)
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg)
static BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg)
{
tBTC_MEDIA_UPDATE_AUDIO *p_buf;
if (NULL == (p_buf = osi_malloc(sizeof(tBTC_MEDIA_UPDATE_AUDIO)))) {
@@ -1451,6 +1445,7 @@ static void btc_a2dp_source_prep_2_send(UINT8 nb_frame)
*******************************************************************************/
static void btc_a2dp_source_send_aa_frame(void)
{
/* if external codec is used, skip generate audio frame */
UINT8 nb_frame_2_send;
/* get the number of frame to send */
@@ -1461,8 +1456,8 @@ static void btc_a2dp_source_send_aa_frame(void)
btc_a2dp_source_prep_2_send(nb_frame_2_send);
}
/* send it */
BTC_TRACE_VERBOSE("%s: send %d frames", __FUNCTION__, nb_frame_2_send);
BTC_TRACE_VERBOSE("%s: send %d new frames", __FUNCTION__, nb_frame_2_send);
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
}
@@ -1539,8 +1534,8 @@ static void btc_a2dp_source_aa_stop_tx(void)
when the DUT and the remote device issue SUSPEND simultaneously
and due to the processing of the SUSPEND request from the remote,
the media path is torn down. If the A2DP HAL happens to wait
for ACK for the initiated SUSPEND, it would never receive it casuing
a block/wait. Due to this acknowledgement, the A2DP HAL is guranteed
for ACK for the initiated SUSPEND, it would never receive it causing
a block/wait. Due to this acknowledgement, the A2DP HAL is guaranteed
to get the ACK for any pending command in such cases. */
if (send_ack) {
@@ -1647,4 +1642,4 @@ static void btc_a2dp_source_thread_cleanup(UNUSED_ATTR void *context)
a2dp_source_local_param.btc_aa_src_cb.poll_data = NULL;
}
#endif /* BTC_AV_INCLUDED */
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == FALSE) */

View File

@@ -0,0 +1,235 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#include "common/bt_trace.h"
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "osi/allocator.h"
#include "osi/fixed_queue.h"
#include "stack/a2d_api.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_ci.h"
#include "btc/btc_manage.h"
#include "btc/btc_common.h"
#include "btc_av_co.h"
#include "btc_a2dp.h"
#include "btc_a2dp_control.h"
#include "btc_a2dp_source.h"
#include "btc_av.h"
#include "esp_a2dp_api.h"
#include <assert.h>
#if (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE)
#define MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ (27)
enum {
BTC_A2DP_SOURCE_STATE_OFF = 0,
BTC_A2DP_SOURCE_STATE_ON = 1,
};
typedef struct {
BOOLEAN stream_started;
BOOLEAN tx_flush;
fixed_queue_t *audio_tx_q;
} a2dp_source_local_param_t;
static int btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
#if A2D_DYNAMIC_MEMORY == FALSE
static a2dp_source_local_param_t a2dp_source_local_param;
#else
static a2dp_source_local_param_t *a2dp_source_local_param_ptr;
#define a2dp_source_local_param (*a2dp_source_local_param_ptr)
#endif ///A2D_DYNAMIC_MEMORY == FALSE
static inline void btc_aa_cb_to_app(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
{
esp_a2d_cb_t btc_aa_cb = (esp_a2d_cb_t)btc_profile_cb_get(BTC_PID_A2DP);
if (btc_aa_cb) {
btc_aa_cb(event, param);
}
}
static void btc_a2dp_source_tx_flush(void)
{
while (!fixed_queue_is_empty(a2dp_source_local_param.audio_tx_q)) {
osi_free(fixed_queue_dequeue(a2dp_source_local_param.audio_tx_q, 0));
}
}
static void btc_a2dp_source_tx_stop(void)
{
if (a2dp_source_local_param.stream_started == TRUE) {
a2dp_source_local_param.stream_started = FALSE;
/* ack to command */
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_SUCCESS);
}
/* flush tx queue */
btc_a2dp_source_tx_flush();
a2dp_source_local_param.tx_flush = FALSE;
}
BOOLEAN btc_a2dp_source_enqueue_audio_frame(BT_HDR *p_buf)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON) {
APPL_TRACE_WARNING("%s source not start up", __func__);
return FALSE;
}
if (a2dp_source_local_param.tx_flush) {
APPL_TRACE_WARNING("%s try to send data when tx flush enable", __func__);
return FALSE;
}
if (fixed_queue_length(a2dp_source_local_param.audio_tx_q) > MAX_OUTPUT_A2DP_SRC_FRAME_QUEUE_SZ) {
APPL_TRACE_WARNING("%s audio tx queue overflow: %d", __func__, fixed_queue_length(a2dp_source_local_param.audio_tx_q));
return FALSE;
}
fixed_queue_enqueue(a2dp_source_local_param.audio_tx_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);
return TRUE;
}
void btc_source_report_delay_value(UINT16 delay_value)
{
esp_a2d_cb_param_t param;
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON){
return;
}
param.a2d_report_delay_value_stat.delay_value = delay_value;
btc_aa_cb_to_app(ESP_A2D_REPORT_SNK_DELAY_VALUE_EVT, &param);
}
BOOLEAN btc_a2dp_source_start_audio_req(void)
{
a2dp_source_local_param.stream_started = TRUE;
return TRUE;
}
BT_HDR *btc_a2dp_source_audio_readbuf(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON || a2dp_source_local_param.tx_flush){
return NULL;
}
return fixed_queue_dequeue(a2dp_source_local_param.audio_tx_q, 0);
}
void btc_a2dp_source_set_tx_flush(BOOLEAN enable)
{
a2dp_source_local_param.tx_flush = enable;
if (enable) {
btc_a2dp_source_tx_flush();
}
}
void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av)
{
/* check for status failures */
if (p_av->status != BTA_AV_SUCCESS) {
if (p_av->initiator == TRUE) {
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
}
}
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av)
{
/* allow using this api for other than suspend */
if (p_av != NULL) {
if (p_av->status != BTA_AV_SUCCESS) {
if (p_av->initiator) {
btc_a2dp_control_command_ack(ESP_A2D_MEDIA_CTRL_ACK_FAILURE);
}
return;
}
}
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
void btc_a2dp_source_on_idle(void)
{
/* stop tx, ack to cmd, flush tx queue */
btc_a2dp_source_tx_stop();
}
bool btc_a2dp_source_is_task_shutting_down(void)
{
/* always return false, remove this api when internal codec is remove */
return false;
}
bool btc_a2dp_source_startup(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_OFF) {
APPL_TRACE_ERROR("%s A2DP source already start up", __func__);
return false;
}
#if A2D_DYNAMIC_MEMORY == TRUE
if ((a2dp_source_local_param_ptr = (a2dp_source_local_param_t *)osi_malloc(sizeof(a2dp_source_local_param_t))) == NULL) {
APPL_TRACE_ERROR("%s malloc failed!", __func__);
return false;
}
memset((void *)a2dp_source_local_param_ptr, 0, sizeof(a2dp_source_local_param_t));
#endif
btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_ON;
a2dp_source_local_param.audio_tx_q = fixed_queue_new(QUEUE_SIZE_MAX);
if(a2dp_source_local_param.audio_tx_q == NULL) {
goto error_exit;
}
btc_a2dp_control_init();
return true;
error_exit:;
APPL_TRACE_ERROR("%s A2DP source start up failed", __func__);
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_source_local_param_ptr);
a2dp_source_local_param_ptr = NULL;
#endif
return false;
}
void btc_a2dp_source_shutdown(void)
{
if (btc_a2dp_source_state != BTC_A2DP_SOURCE_STATE_ON) {
APPL_TRACE_ERROR("%s A2DP source already shutdown", __func__);
return;
}
btc_a2dp_source_state = BTC_A2DP_SOURCE_STATE_OFF;
btc_a2dp_control_cleanup();
fixed_queue_free(a2dp_source_local_param.audio_tx_q, osi_free_func);
a2dp_source_local_param.audio_tx_q = NULL;
a2dp_source_local_param.tx_flush = FALSE;
a2dp_source_local_param.stream_started = FALSE;
#if A2D_DYNAMIC_MEMORY == TRUE
osi_free(a2dp_source_local_param_ptr);
a2dp_source_local_param_ptr = NULL;
#endif
}
#endif /* (BTC_AV_SRC_INCLUDED == TRUE) && (BTC_AV_EXT_CODEC == TRUE) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,6 +22,7 @@
#include "btc/btc_common.h"
#include "btc/btc_manage.h"
#include "btc_av.h"
#include "btc_av_co.h"
#include "btc_avrc.h"
#include "btc/btc_util.h"
#include "btc/btc_profile_queue.h"
@@ -46,6 +47,9 @@ bool g_a2dp_source_ongoing_deinit;
bool g_a2dp_sink_ongoing_deinit;
/* reserve some bytes for media payload header (currently, only SBC, 1 byte) */
#define BTC_AV_AUDIO_MTU_RESERVE 1
/*****************************************************************************
** Constants & Macros
******************************************************************************/
@@ -74,6 +78,9 @@ typedef enum {
#define BTC_AV_FLAG_PENDING_START 0x4
#define BTC_AV_FLAG_PENDING_STOP 0x8
#define BTC_AV_SBC_CIE_OFFSET 3
#define BTC_AV_SBC_CIE_LEN 4
/*****************************************************************************
** Local type definitions
******************************************************************************/
@@ -81,11 +88,13 @@ typedef enum {
typedef struct {
int service_id;
tBTA_AV_HNDL bta_handle;
UINT16 mtu;
bt_bdaddr_t peer_bda;
btc_sm_handle_t sm_handle;
UINT8 flags;
tBTA_AV_EDR edr;
UINT8 peer_sep; /* sep type of peer device */
tBTC_AV_CODEC_INFO codec_caps;
#if BTC_AV_SRC_INCLUDED
osi_alarm_t *tle_av_open_on_rc;
#endif /* BTC_AV_SRC_INCLUDED */
@@ -170,6 +179,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;
/*****************************************************************************
** Local helper functions
******************************************************************************/
@@ -222,7 +232,7 @@ UNUSED_ATTR static const char *dump_av_sm_event_name(btc_av_sm_event_t event)
CASE_RETURN_STR(BTC_AV_DISCONNECT_REQ_EVT)
CASE_RETURN_STR(BTC_AV_START_STREAM_REQ_EVT)
CASE_RETURN_STR(BTC_AV_SUSPEND_STREAM_REQ_EVT)
CASE_RETURN_STR(BTC_AV_SINK_CONFIG_REQ_EVT)
CASE_RETURN_STR(BTC_AV_CONFIG_EVT)
default: return "UNKNOWN_EVENT";
}
}
@@ -263,7 +273,7 @@ static void btc_initiate_av_open_tmr_hdlr(void *arg)
/*****************************************************************************
** Static functions
******************************************************************************/
static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, int disc_rsn)
static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, uint16_t mtu, int disc_rsn)
{
// todo: add callback for SRC
esp_a2d_cb_param_t param;
@@ -272,8 +282,15 @@ static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bda
param.conn_stat.state = state;
if (bd_addr) {
memcpy(param.conn_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
if (memcmp(bd_addr, &(btc_av_cb.peer_bda), sizeof(bt_bdaddr_t)) == 0) {
param.conn_stat.conn_hdl = btc_av_cb.bta_handle;
}
}
if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
param.conn_stat.audio_mtu = mtu;
btc_av_cb.mtu = mtu;
}
else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
param.conn_stat.disc_rsn = (disc_rsn == 0) ? ESP_A2D_DISC_RSN_NORMAL :
ESP_A2D_DISC_RSN_ABNORMAL;
}
@@ -289,6 +306,9 @@ static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_
param.audio_stat.state = state;
if (bd_addr) {
memcpy(param.audio_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
if (memcmp(bd_addr, &(btc_av_cb.peer_bda), sizeof(bt_bdaddr_t)) == 0) {
param.audio_stat.conn_hdl = btc_av_cb.bta_handle;
}
}
btc_a2d_cb_to_app(ESP_A2D_AUDIO_STATE_EVT, &param);
}
@@ -328,6 +348,12 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_REGISTER_EVT:
btc_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl;
break;
case BTA_AV_SEP_REG_EVT:
param.a2d_sep_reg_stat.seid = ((tBTA_AV *)p_data)->sep_reg.seid;
param.a2d_sep_reg_stat.reg_state = (((tBTA_AV *)p_data)->sep_reg.reg_state == BTA_AV_SUCCESS) ? ESP_A2D_SEP_REG_SUCCESS :
ESP_A2D_SEP_REG_FAIL;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
break;
case BTA_AV_PENDING_EVT:
case BTC_AV_CONNECT_REQ_EVT: {
@@ -358,7 +384,7 @@ static BOOLEAN btc_av_state_idle_handler(btc_sm_event_t event, void *p_data)
case BTC_AV_DISCONNECT_REQ_EVT:
BTC_TRACE_WARNING("No Link At All.");
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &((btc_av_disconn_req_t *)p_data)->target_bda, 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &((btc_av_disconn_req_t *)p_data)->target_bda, 0, 0);
break;
case BTA_AV_RC_OPEN_EVT:
@@ -448,7 +474,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
switch (event) {
case BTC_SM_ENTER_EVT:
/* inform the application that we are entering connecting state */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0, 0);
break;
case BTC_SM_EXIT_EVT:
@@ -456,7 +482,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_REJECT_EVT:
BTC_TRACE_WARNING(" Received BTA_AV_REJECT_EVT \n");
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0, 0);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
break;
@@ -464,13 +490,14 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
tBTA_AV *p_bta_data = (tBTA_AV *)p_data;
esp_a2d_connection_state_t conn_stat;
btc_sm_state_t av_state;
uint16_t mtu = 0;
BTC_TRACE_DEBUG("status:%d, edr 0x%x, peer sep %d\n", p_bta_data->open.status,
p_bta_data->open.edr, p_bta_data->open.sep);
if (p_bta_data->open.status == BTA_AV_SUCCESS) {
btc_av_cb.edr = p_bta_data->open.edr;
btc_av_cb.peer_sep = p_bta_data->open.sep;
mtu = p_bta_data->open.mtu - BTC_AV_AUDIO_MTU_RESERVE;
conn_stat = ESP_A2D_CONNECTION_STATE_CONNECTED;
av_state = BTC_AV_STATE_OPENED;
} else {
@@ -480,7 +507,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
av_state = BTC_AV_STATE_IDLE;
}
/* inform the application of the event */
btc_report_connection_state(conn_stat, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(conn_stat, &(btc_av_cb.peer_bda), mtu, 0);
/* change state to open/idle based on the status */
btc_sm_change_state(btc_av_cb.sm_handle, av_state);
@@ -502,13 +529,12 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
btc_queue_advance();
} break;
case BTC_AV_SINK_CONFIG_REQ_EVT: {
if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
esp_a2d_cb_param_t param;
memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, &param);
}
case BTC_AV_CONFIG_EVT: {
esp_a2d_cb_param_t param;
param.audio_cfg.conn_hdl = btc_av_cb.bta_handle;
memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, &param);
} break;
case BTC_AV_CONNECT_REQ_EVT:
@@ -520,7 +546,7 @@ static BOOLEAN btc_av_state_opening_handler(btc_sm_event_t event, void *p_data)
break;
} else {
BTC_TRACE_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0, 0);
btc_queue_advance();
break;
}
@@ -623,7 +649,7 @@ static BOOLEAN btc_av_state_closing_handler(btc_sm_event_t event, void *p_data)
case BTA_AV_CLOSE_EVT: {
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), close->disc_rsn);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0, close->disc_rsn);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
break;
}
@@ -693,7 +719,9 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
case BTC_AV_START_STREAM_REQ_EVT:
#if BTC_AV_SRC_INCLUDED
if (btc_av_cb.peer_sep != AVDT_TSEP_SRC) {
#if (BTC_AV_EXT_CODEC == FALSE)
btc_a2dp_source_setup_codec();
#endif
}
#endif /* BTC_AV_SRC_INCLUDED */
BTA_AvStart();
@@ -759,7 +787,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
}
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0, 0);
break;
case BTA_AV_CLOSE_EVT: {
@@ -767,7 +795,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
btc_a2dp_on_stopped(NULL);
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnected */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0,
close->disc_rsn);
if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) {
@@ -804,7 +832,7 @@ static BOOLEAN btc_av_state_opened_handler(btc_sm_event_t event, void *p_data)
} else {
BTC_TRACE_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED,
(bt_bdaddr_t *)p_data, ESP_A2D_DISC_RSN_NORMAL);
(bt_bdaddr_t *)p_data, 0, ESP_A2D_DISC_RSN_NORMAL);
}
btc_queue_advance();
break;
@@ -917,7 +945,7 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data)
}
/* inform the application that we are disconnecting */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0, 0);
/* wait in closing state until fully closed */
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_CLOSING);
@@ -982,7 +1010,7 @@ static BOOLEAN btc_av_state_started_handler(btc_sm_event_t event, void *p_data)
btc_a2dp_on_stopped(NULL);
tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
/* inform the application that we are disconnected */
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0,
close->disc_rsn);
btc_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
@@ -1337,7 +1365,7 @@ static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data)
}
#if BTC_AV_SINK_INCLUDED
static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
btc_sm_state_t state;
UINT8 que_len;
@@ -1351,13 +1379,16 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
que_len = btc_a2dp_sink_enque_buf((BT_HDR *)p_data);
BTC_TRACE_DEBUG(" Packets in Que %d\n", que_len);
} else {
osi_free(p_data);
return;
}
}
if (event == BTA_AV_MEDIA_SINK_CFG_EVT) {
if (event == BTA_AV_MEDIA_CFG_EVT) {
#if (BTC_AV_EXT_CODEC == FALSE)
/* send a command to BT Media Task */
btc_a2dp_sink_reset_decoder((UINT8 *)p_data);
#endif
/* currently only supports SBC */
a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
@@ -1367,11 +1398,11 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_A2DP;
msg.act = BTC_AV_SINK_CONFIG_REQ_EVT;
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, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_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);
@@ -1380,7 +1411,7 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
UNUSED(que_len);
}
#else
static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
static void bte_av_media_sink_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
UNUSED(event);
UNUSED(p_data);
@@ -1388,6 +1419,82 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
}
#endif
#if BTC_AV_SRC_INCLUDED
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;
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("A2D_ParsSbcInfo fail:%d\n", a2d_status);
}
}
}
#else
static void bte_av_media_source_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
{
UNUSED(event);
UNUSED(p_data);
BTC_TRACE_WARNING("%s : event %u\n", __func__, event);
}
#endif
#if (BTC_AV_EXT_CODEC == TRUE)
tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void)
{
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;
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) {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_INVALID_STATE;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: try to reg sep when a2dp not init or connected", __func__);
}
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);
}
else {
param.a2d_sep_reg_stat.reg_state = ESP_A2D_SEP_REG_UNSUPPORTED;
btc_a2d_cb_to_app(ESP_A2D_SEP_REG_STATE_EVT, &param);
BTC_TRACE_WARNING("%s: unsupported codec type %d", __func__, mcc->type);
}
}
#endif
/*******************************************************************************
**
** Function btc_av_execute_service
@@ -1399,7 +1506,15 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
*******************************************************************************/
bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep)
{
tBTA_AV_DATA_CBACK *p_data_cback = NULL;
if (b_enable) {
if (tsep == AVDT_TSEP_SNK) {
p_data_cback = bte_av_media_sink_callback;
}
else {
p_data_cback = bte_av_media_source_callback;
}
/* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
* handle this request in order to allow incoming connections to succeed.
* We need to put this back once support for this is added */
@@ -1413,11 +1528,11 @@ bt_status_t btc_av_execute_service(BOOLEAN b_enable, UINT8 tsep)
BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR |
BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_DELAY_RPT,
bte_av_callback);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, &bta_avrc_cos, tsep);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, p_data_cback, &bta_av_a2d_cos, &bta_avrc_cos, tsep);
} else {
BTC_TRACE_WARNING("A2DP Enable without AVRC")
BTA_AvEnable(BTA_SEC_AUTHENTICATE, BTA_AV_FEAT_NO_SCO_SSPD | BTA_AV_FEAT_DELAY_RPT, bte_av_callback);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos, NULL, tsep);
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, p_data_cback, &bta_av_a2d_cos, NULL, tsep);
}
} else {
BTA_AvDeregister(btc_av_cb.bta_handle);
@@ -1476,6 +1591,11 @@ BOOLEAN btc_av_is_connected(void)
return ((state == BTC_AV_STATE_OPENED) || (state == BTC_AV_STATE_STARTED));
}
BOOLEAN btc_av_is_started(void)
{
return ((btc_sm_get_state(btc_av_cb.sm_handle) == BTC_AV_STATE_STARTED));
}
/*******************************************************************************
*
* Function btc_av_get_service_id
@@ -1548,15 +1668,17 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_av_args_t *arg = (btc_av_args_t *)(msg->arg);
switch (msg->act) {
#if BTC_AV_SINK_INCLUDED
case BTC_AV_SINK_CONFIG_REQ_EVT: {
btc_sm_dispatch(btc_av_cb.sm_handle, msg->act, (void *)(msg->arg));
break;
}
case BTC_AV_SINK_API_INIT_EVT: {
btc_a2d_sink_init();
// todo: callback to application
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SINK_API_REG_SEP_EVT: {
btc_av_reg_sep(AVDT_TSEP_SNK, arg->reg_sep.seid, &arg->reg_sep.mcc);
break;
}
#endif
case BTC_AV_SINK_API_DEINIT_EVT: {
btc_a2d_sink_deinit();
// todo: callback to application
@@ -1574,10 +1696,17 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req);
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT: {
btc_a2dp_sink_reg_audio_data_cb(arg->audio_data_cb);
break;
}
#else
case BTC_AV_SINK_API_REG_DATA_CB_EVT: {
btc_a2dp_sink_reg_data_cb(arg->data_cb);
break;
}
#endif
case BTC_AV_SINK_API_SET_DELAY_VALUE_EVT: {
btc_a2d_sink_set_delay_value(arg->delay_value);
break;
@@ -1592,6 +1721,12 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_a2d_src_init();
break;
}
#if (BTC_AV_EXT_CODEC == TRUE)
case BTC_AV_SRC_API_REG_SEP_EVT: {
btc_av_reg_sep(AVDT_TSEP_SRC, arg->reg_sep.seid, &arg->reg_sep.mcc);
break;
}
#endif
case BTC_AV_SRC_API_DEINIT_EVT: {
btc_a2d_src_deinit();
break;
@@ -1607,10 +1742,12 @@ void btc_a2dp_call_handler(btc_msg_t *msg)
btc_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_DISCONNECT_REQ_EVT, &disconn_req);
break;
}
#if (BTC_AV_EXT_CODEC == FALSE)
case BTC_AV_SRC_API_REG_DATA_CB_EVT: {
btc_a2dp_src_reg_data_cb(arg->src_data_cb);
break;
}
#endif
#endif /* BTC_AV_SRC_INCLUDED */
case BTC_AV_API_MEDIA_CTRL_EVT: {
btc_a2dp_control_media_ctrl(arg->ctrl);
@@ -1734,6 +1871,56 @@ static bt_status_t btc_a2d_src_connect(bt_bdaddr_t *remote_bda)
return btc_queue_connect(UUID_SERVCLASS_AUDIO_SOURCE, remote_bda, connect_int);
}
BOOLEAN btc_a2d_src_audio_mtu_check(uint16_t data_len)
{
return (data_len <= btc_av_cb.mtu);
}
bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf)
{
#if (BTC_AV_EXT_CODEC == TRUE)
if (conn_hdl != btc_av_cb.bta_handle) {
return BT_STATUS_FAIL;
}
BT_HDR *p_buf = (BT_HDR *)audio_buf;
assert(audio_buf->data - (UINT8 *)(p_buf + 1) >= BTC_AUDIO_BUFF_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;
uint32_t timestamp = audio_buf->timestamp;
p_buf->offset = audio_buf->data - (UINT8 *)(p_buf + 1);
p_buf->layer_specific = number_frame;
p_buf->len = data_len;
*((UINT32 *) (p_buf + 1)) = timestamp;
if (btc_a2dp_source_enqueue_audio_frame(p_buf)) {
return BT_STATUS_SUCCESS;
}
#endif
return BT_STATUS_FAIL;
}
#endif /* BTC_AV_SRC_INCLUDED */
uint16_t btc_a2d_conn_handle_get(void)
{
return btc_av_cb.bta_handle;
}
void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data)
{
/* todo */
BT_HDR *p_buf= (BT_HDR *)osi_calloc(sizeof(BT_HDR) + BTC_AUDIO_BUFF_OFFSET + size);
if (p_buf != NULL) {
*pp_buff = (uint8_t *)p_buf;
*pp_data = (uint8_t *)(p_buf + 1) + BTC_AUDIO_BUFF_OFFSET;
}
}
void btc_av_audio_buff_free(uint8_t *p_buf)
{
osi_free(p_buf);
}
#endif /* #if BTC_AV_INCLUDED */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -64,6 +64,7 @@ typedef struct {
typedef struct {
/* Connected peer information */
tBTA_AV_CO_PEER peers[BTA_AV_NUM_STRS];
tBTC_AV_CODEC_INFO codec_caps;
/* Current codec configuration - access to this variable must be protected */
tBTC_AV_CODEC_INFO codec_cfg;
tBTC_AV_CODEC_INFO codec_cfg_setconfig; /* remote peer setconfig preference */
@@ -189,8 +190,7 @@ void bta_av_co_audio_discard_config(tBTA_AV_HNDL hndl);
** Returns Nothing
**
*******************************************************************************/
void bta_av_co_init(void);
void bta_av_co_init(tBTC_AV_CODEC_INFO *codec_caps);
/*******************************************************************************
**
@@ -211,7 +211,7 @@ BOOLEAN bta_av_co_peer_cp_supported(tBTA_AV_HNDL hndl);
** of our exported bitpool range. If set we will set the
** remote preference.
**
** Returns TRUE if config set, FALSE otherwize
** Returns TRUE if config set, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_av_co_get_remote_bitpool_pref(UINT8 *min, UINT8 *max);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -23,6 +23,7 @@
/*******************************************************************************
** Data types
*******************************************************************************/
#if (BTC_AV_EXT_CODEC == FALSE)
/* tBTC_MEDIA_INIT_AUDIO msg structure */
typedef struct {
@@ -50,6 +51,8 @@ typedef struct {
tBTC_AV_MEDIA_FEEDINGS feeding;
} tBTC_MEDIA_INIT_AUDIO_FEEDING;
#endif
/*******************************************************************************
** Public functions
*******************************************************************************/
@@ -76,29 +79,6 @@ bool btc_a2dp_source_startup(void);
*******************************************************************************/
void btc_a2dp_source_shutdown(void);
/*******************************************************************************
**
** Function btc_a2dp_source_enc_init_req
**
** Description Request to initialize the media task encoder
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_init_req(tBTC_MEDIA_INIT_AUDIO *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_enc_udpate_req
**
** Description Request to update the media task encoder
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_start_audio_req
@@ -110,28 +90,6 @@ BOOLEAN btc_a2dp_source_enc_update_req(tBTC_MEDIA_UPDATE_AUDIO *p_msg);
*******************************************************************************/
BOOLEAN btc_a2dp_source_start_audio_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_stop_audio_req
**
** Description Request to stop audio encoding task
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_stop_audio_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_tx_flush_req
**
** Description Request to flush audio encoding pipe
**
** Returns TRUE is success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_tx_flush_req(void);
/*******************************************************************************
**
** Function btc_a2dp_source_audio_readbuf
@@ -143,27 +101,6 @@ BOOLEAN btc_a2dp_source_tx_flush_req(void);
*******************************************************************************/
BT_HDR *btc_a2dp_source_audio_readbuf(void);
/*******************************************************************************
**
** Function btc_a2dp_source_audio_feeding_init_req
**
** Description Request to initialize audio feeding
**
** Returns TRUE if success
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_audio_feeding_init_req(tBTC_MEDIA_INIT_AUDIO_FEEDING *p_msg);
/*******************************************************************************
**
** Function btc_a2dp_source_is_streaming
**
** Description Check whether A2DP source is in streaming state
**
*******************************************************************************/
bool btc_a2dp_source_is_streaming(void);
/*******************************************************************************
**
** Function btc_a2dp_source_is_task_shutting_down
@@ -204,15 +141,6 @@ void btc_a2dp_source_on_stopped(tBTA_AV_SUSPEND *p_av);
*******************************************************************************/
void btc_a2dp_source_on_suspended(tBTA_AV_SUSPEND *p_av);
/*******************************************************************************
**
** Function btc_a2dp_source_setup_codec
**
** Description initialize the encoder parameters
**
*******************************************************************************/
void btc_a2dp_source_setup_codec(void);
/*******************************************************************************
**
** Function btc_a2dp_source_set_tx_flush
@@ -222,6 +150,17 @@ void btc_a2dp_source_setup_codec(void);
*******************************************************************************/
void btc_a2dp_source_set_tx_flush(BOOLEAN enable);
#if (BTC_AV_EXT_CODEC == FALSE)
/*******************************************************************************
**
** Function btc_a2dp_source_setup_codec
**
** Description initialize the encoder parameters
**
*******************************************************************************/
void btc_a2dp_source_setup_codec(void);
/*******************************************************************************
**
** Function btc_a2dp_source_encoder_update
@@ -231,6 +170,8 @@ void btc_a2dp_source_set_tx_flush(BOOLEAN enable);
*******************************************************************************/
void btc_a2dp_source_encoder_update(void);
#endif
/*****************************************************************************
**
** Function btc_source_report_delay_value
@@ -240,6 +181,19 @@ void btc_a2dp_source_encoder_update(void);
*******************************************************************************/
void btc_source_report_delay_value(UINT16 delay_value);
#if (BTC_AV_EXT_CODEC == TRUE)
/*****************************************************************************
**
** Function btc_a2dp_source_enqueue_audio_frame
**
** Description Enqueue source audio frame to tx queue
**
*******************************************************************************/
BOOLEAN btc_a2dp_source_enqueue_audio_frame(BT_HDR *p_buf);
#endif
#endif /* #if BTC_AV_SRC_INCLUDED */
#endif /* __BTC_A2DP_SOURCE_H__ */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,11 +22,21 @@
#include "btc/btc_task.h"
#include "btc/btc_common.h"
#include "btc/btc_sm.h"
#include "btc_av_api.h"
#include "bta/bta_av_api.h"
#include "bta/bta_av_sbc.h"
#if (BTC_AV_INCLUDED == TRUE)
// global variable to inidcate avrc is initialized with a2dp
#if (BTA_AV_CO_CP_SCMS_T == TRUE)
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
#else
#define BTC_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
#endif
#define BTC_AUDIO_BUFF_OFFSET BTC_MEDIA_AA_SBC_OFFSET
// global variable to indicate avrc is initialized with a2dp
extern bool g_av_with_rc;
// global variable to indicate a2dp is initialized
extern bool g_a2dp_on_init;
@@ -51,21 +61,24 @@ typedef enum {
BTC_AV_DISCONNECT_REQ_EVT,
BTC_AV_START_STREAM_REQ_EVT,
BTC_AV_SUSPEND_STREAM_REQ_EVT,
BTC_AV_SINK_CONFIG_REQ_EVT,
BTC_AV_CONFIG_EVT,
} btc_av_sm_event_t;
typedef enum {
#if BTC_AV_SINK_INCLUDED
BTC_AV_SINK_API_INIT_EVT = 0,
BTC_AV_SINK_API_REG_SEP_EVT,
BTC_AV_SINK_API_DEINIT_EVT,
BTC_AV_SINK_API_CONNECT_EVT,
BTC_AV_SINK_API_DISCONNECT_EVT,
BTC_AV_SINK_API_REG_DATA_CB_EVT,
BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT,
BTC_AV_SINK_API_SET_DELAY_VALUE_EVT,
BTC_AV_SINK_API_GET_DELAY_VALUE_EVT,
#endif /* BTC_AV_SINK_INCLUDED */
#if BTC_AV_SRC_INCLUDED
BTC_AV_SRC_API_INIT_EVT,
BTC_AV_SRC_API_REG_SEP_EVT,
BTC_AV_SRC_API_DEINIT_EVT,
BTC_AV_SRC_API_CONNECT_EVT,
BTC_AV_SRC_API_DISCONNECT_EVT,
@@ -77,14 +90,14 @@ typedef enum {
/* btc_av_args_t */
typedef union {
#if BTC_AV_SINK_INCLUDED
// BTC_AV_SINK_CONFIG_REQ_EVT -- internal event
esp_a2d_mcc_t mcc;
// BTC_AV_SINK_API_CONNECT_EVT
bt_bdaddr_t connect;
// BTC_AV_SINK_API_DISCONNECT_EVT
bt_bdaddr_t disconn;
// BTC_AV_SINK_API_REG_DATA_CB_EVT
esp_a2d_sink_data_cb_t data_cb;
// BTC_AV_SINK_API_REG_AUDIO_DATA_CB_EVT
esp_a2d_sink_audio_data_cb_t audio_data_cb;
// BTC_AV_SINK_API_SET_DELAY_VALUE_EVT
uint16_t delay_value;
#endif /* BTC_AV_SINK_INCLUDED */
@@ -96,6 +109,13 @@ typedef union {
// BTC_AV_SRC_API_DISCONNECT_EVT
bt_bdaddr_t src_disconn;
#endif /* BTC_AV_SRC_INCLUDED */
// BTC_AV_CONFIG_EVT
esp_a2d_mcc_t mcc;
// BTC_AV_SINK_API_REG_SEP_EVT or BTC_AV_SRC_API_REG_SEP_EVT
struct {
uint8_t seid;
esp_a2d_mcc_t mcc;
} reg_sep;
// BTC_AV_API_MEDIA_CTRL_EVT
esp_a2d_media_ctrl_t ctrl;
} btc_av_args_t;
@@ -110,6 +130,32 @@ void btc_a2dp_cb_handler(btc_msg_t *msg);
void btc_a2dp_sink_reg_data_cb(esp_a2d_sink_data_cb_t callback);
/*******************************************************************************
**
** Function btc_a2dp_sink_reg_audio_data_cb
**
** Description Register a2dp sink audio data callback
**
** Returns None
**
*******************************************************************************/
void btc_a2dp_sink_reg_audio_data_cb(esp_a2d_sink_audio_data_cb_t callback);
#if (BTC_AV_EXT_CODEC == TRUE)
/*******************************************************************************
**
** Function btc_av_codec_cap_get
**
** Description Get external codec capability
**
** Returns Pointer to codec capability
**
*******************************************************************************/
tBTC_AV_CODEC_INFO *btc_av_codec_cap_get(void);
#endif
void btc_a2dp_src_reg_data_cb(esp_a2d_source_data_cb_t callback);
/*******************************************************************************
**
@@ -172,6 +218,16 @@ void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len);
BOOLEAN btc_av_is_connected(void);
/*******************************************************************************
**
** Function btc_av_is_started
**
** Description Checks if av is started
**
** Returns BOOLEAN
**
*******************************************************************************/
BOOLEAN btc_av_is_started(void);
/*******************************************************************************
*
@@ -225,4 +281,60 @@ uint8_t btc_av_get_service_id(void);
#endif ///BTC_AV_INCLUDED == TRUE
/*******************************************************************************
**
** Function btc_a2d_conn_handle_get
**
** Description Get connection handle from btc_av_cb
**
** Returns Void
**
*******************************************************************************/
uint16_t btc_a2d_conn_handle_get(void);
/*******************************************************************************
**
** Function btc_av_audio_buff_alloc
**
** Description Allocate audio buffer with specific size
**
** Returns Void
**
*******************************************************************************/
void btc_av_audio_buff_alloc(uint16_t size, uint8_t **pp_buff, uint8_t **pp_data);
/*******************************************************************************
**
** Function btc_av_audio_buff_free
**
** Description Free audio buffer
**
** Returns Void
**
*******************************************************************************/
void btc_av_audio_buff_free(uint8_t *p_buf);
/*******************************************************************************
**
** Function btc_a2d_src_audio_mtu_check
**
** Description Checks if data length is valid, not bigger than mtu
**
** Returns BOOLEAN
**
*******************************************************************************/
BOOLEAN btc_a2d_src_audio_mtu_check(uint16_t data_len);
/*******************************************************************************
**
** Function btc_a2d_src_audio_data_send
**
** Description Send audio data to lower layer, audio buffer is consumed
** only when operation is success
**
** Returns BT_STATUS_SUCCESS if success, otherwise, BT_STATUS_FAIL
**
*******************************************************************************/
bt_status_t btc_a2d_src_audio_data_send(esp_a2d_conn_hdl_t conn_hdl, esp_a2d_audio_buff_t *audio_buf);
#endif /* __BTC_AV_H__ */

View File

@@ -91,6 +91,7 @@
#define BTC_AV_SRC_INCLUDED TRUE
#if (UC_BT_A2DP_USE_EXTERNAL_CODEC == TRUE)
#define BTC_AV_EXT_CODEC TRUE
#define BTA_AV_EXT_CODEC TRUE
#else
#define SBC_DEC_INCLUDED TRUE
#define SBC_ENC_INCLUDED TRUE
@@ -581,6 +582,10 @@
#define BTC_AV_SRC_INCLUDED FALSE
#endif
#ifndef BTC_AV_EXT_CODEC
#define BTC_AV_EXT_CODEC FALSE
#endif
#ifndef BTC_SPP_INCLUDED
#define BTC_SPP_INCLUDED FALSE
#endif
@@ -658,8 +663,8 @@
#define BTA_AV_CA_INCLUDED FALSE
#endif
#ifndef BTC_AV_EXT_CODEC
#define BTC_AV_EXT_CODEC FALSE
#ifndef BTA_AV_EXT_CODEC
#define BTA_AV_EXT_CODEC FALSE
#endif
#ifndef BTA_AV_SINK_INCLUDED

View File

@@ -264,6 +264,37 @@ UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs)
return result;
}
/*******************************************************************************
**
** Function AVDT_UpdateCodecInfo
**
** Description Update codec capability for a stream endpoint.
**
**
** Returns AVDT_SUCCESS if successful, otherwise error.
**
*******************************************************************************/
UINT16 AVDT_UpdateCodecInfo(UINT8 handle, UINT8 num_codec, UINT8 *codec_info, UINT16 codec_info_len)
{
UINT16 result = AVDT_SUCCESS;
tAVDT_SCB *p_scb;
/* look up scb */
if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) {
result = AVDT_BAD_HANDLE;
}
else if (num_codec != 1 || codec_info == NULL || codec_info_len != AVDT_CODEC_SIZE) {
/* currently, only allow one codec info */
result = AVDT_BAD_PARAMS;
}
else {
/* update codec info */
p_scb->cs.cfg.num_codec = num_codec;
memcpy(p_scb->cs.cfg.codec_info, codec_info, codec_info_len);
}
return result;
}
/*******************************************************************************
**
** Function AVDT_RemoveStream

View File

@@ -30,7 +30,12 @@
/* the length of the SBC Media Payload header. */
#define A2D_SBC_MPL_HDR_LEN 1
/* the LOSC of SBC media codec capabilitiy */
/* CIE offset in the info byte sequence */
#define A2D_SBC_CIE_OFF 3
/* CIE length in the info byte sequence */
#define A2D_SBC_CIE_LEN 4
/* the LOSC of SBC media codec capability */
#define A2D_SBC_INFO_LEN 6
/* for Codec Specific Information Element */

View File

@@ -517,6 +517,18 @@ extern void AVDT_AbortReq(UINT8 handle);
*******************************************************************************/
extern UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs);
/*******************************************************************************
**
** Function AVDT_UpdateCodecInfo
**
** Description Update codec capability for a stream endpoint.
**
**
** Returns AVDT_SUCCESS if successful, otherwise error.
**
*******************************************************************************/
extern UINT16 AVDT_UpdateCodecInfo(UINT8 handle, UINT8 num_codec, UINT8 *codec_info, UINT16 codec_info_len);
/*******************************************************************************
**
** Function AVDT_RemoveStream