forked from espressif/esp-idf
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:
@@ -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"
|
||||
|
@@ -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 */
|
||||
|
@@ -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 configured,only 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
|
||||
|
@@ -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
|
@@ -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);
|
||||
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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 *)®istr);
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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)
|
||||
|
@@ -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) */
|
||||
|
@@ -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
|
@@ -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) */
|
||||
|
@@ -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, ¶m);
|
||||
}
|
||||
|
||||
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) */
|
@@ -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, ¶m);
|
||||
}
|
||||
@@ -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, ¶m);
|
||||
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(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
|
||||
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, ¶m);
|
||||
}
|
||||
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(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
|
||||
btc_a2d_cb_to_app(ESP_A2D_AUDIO_CFG_EVT, ¶m);
|
||||
} 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, ¶m);
|
||||
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, ¶m);
|
||||
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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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__ */
|
||||
|
@@ -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__ */
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user