mcu_hi3321_watch/tjd/ble/ble_port_a2dp_service.c
2025-05-26 20:15:20 +08:00

877 lines
36 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ble_port_a2dp_service.h"
#include "audio_manager_c_wrapper.h"
#include "ble_api.h"
#include "bt_audio.h"
#include "bt_audio_hal_interface.h"
#include "bts_a2dp_sink.h"
#include "bts_a2dp_source.h"
#include "bts_avrcp_controller.h"
#include "bts_avrcp_target.h"
#include "osal_addr.h"
#include "osal_timer.h"
#include "soc_errno.h"
#include "sys_config.h"
#define ENABLE_PRINT_INFO 1
#if ENABLE_PRINT_INFO
#define static_print_debug(...) sys_bt_log_d(__VA_ARGS__) // 一般信息打印宏控制
#define static_print_warn(...) sys_bt_log_w(__VA_ARGS__) // 警告信息打印一般常开
#define static_print_error(...) sys_bt_log_e(__VA_ARGS__) // 错误信息打印一般常开
#else
#define static_print_debug(...)
#define static_print_warn(...)
#define static_print_error(...)
#endif
#define AVRCP_CT_AUTO_REG_TYPE 1
#define AVRCP_CT_AUTO_REG_NON 0
#define BTS_ATTR_ID_LEN 4
#define BTS_ATTR_LIST_LEN 12
#define BTS_UID_LEN 8
#define MAX_SAMPLE_AVRCP_CT_REG_CNT 5 // 支持一次shell命令最多能注册5个事件
#define AVRCP_SHIFT_BITS_32 32
#define LONG_MIN (-LONG_MAX - 1)
#define LONG_MAX 2147483647L
#define A2DP_STREAM_ACCEPT_TIMER_PERIOD 500
#define BLUETOOTH_MAX_NUM 20
#define MEDIA_A2DP_SNK_INTERFACE 1
#define MEMORY_MINI 1
enum
{
AVRCP_SRV_PRESS_VALUE,
AVRCP_SRV_RELEASED_VALUE,
AVRCP_SRV_INIT_VALUE
};
enum
{
SAMPLE_A2DP_STREAM_UNKNOWN, /* no a2dp */
SAMPLE_A2DP_STREAM_CREATE,
SAMPLE_A2DP_STREAM_OPENED,
SAMPLE_A2DP_STREAM_START,
SAMPLE_A2DP_STREAM_SUSPENDED,
SAMPLE_A2DP_STREAM_CLOSED
};
// typedef void *td_pvoid;
typedef struct
{
td_pvoid stream_hdl;
uint8_t loc_volume;
uint8_t play_stat;
uint8_t dir_forward;
uint8_t avrcp_button_flag;
bd_addr_t bd_addr;
uint8_t reg_cap_auto;
uint8_t resrv;
profile_connect_state_t avrcp_srv_conn_stat;
} sample_avrcp_tg_sample_inf;
sample_avrcp_tg_sample_inf g_tjd_avrcp_srv_inf;
static src_connect_state_callback_t g_src_connect_state_callback = NULL;
static snk_connect_state_callback_t g_snk_connect_state_callback = NULL;
static playing_state_callback_t g_playing_state_callback = NULL;
// static uint32_t g_tjd_hid_service_handle = 0;
static sync_lrc_callback_t g_sync_lrc_callback = NULL;
osal_timer g_tjd_a2dp_stream_accept_timer = {0};
static bool isFirst = false;
extern AudioFormatConfig g_audio_format_config;
#pragma region a2dp src
/****************************
* A2DP SRC
*/
// static void A2dpSrcConnectstateChanged(bd_addr_t *bdAddr, int connState)
// {
// static_print_debug("a2dp_src_connectStateChangedCallback addr: %02x%02x%02x%02x%02x%02x connState: %x",
// bdAddr->addr[5], bdAddr->addr[4], bdAddr->addr[3], /* 5 4 3 idx */
// bdAddr->addr[2], bdAddr->addr[1], bdAddr->addr[0], connState); /* 2 1 0 idx */
// if ((gap_get_device_class(bdAddr) & 0x200) != 0) {
// static_print_debug("a2dp_src_connectstateChangedCb: Mobile Phone Device");
// return;
// }
// if (connState == PROFILE_STATE_CONNECTED) {
// avrcp_ct_connect(bdAddr);
// }
// else if (connState == PROFILE_STATE_DISCONNECTED) {
// static_print_debug("a2dp_src_connectstateChangedCb: Disconnected");
// }
// }
// static void A2dpSrcConfigureChanged(bd_addr_t *bdAddr, a2dp_codec_info_t *info)
// {
// static_print_debug("A2dpSrcConfigurationChangedCallback addr: %02x%02x%02x%02x%02x%02x ",
// bdAddr->addr[5], bdAddr->addr[4], bdAddr->addr[3], /* 5 4 3 idx */
// bdAddr->addr[2], bdAddr->addr[1], bdAddr->addr[0]); /* 2 1 0 idx */
// static_print_debug("codec_priority: %04x codec_type: %04x sample_rate: %02x bits_per_sample: %02x channel_mode:
// %02x ",
// info->codec_priority, info->codec_type, info->sample_rate, info->bits_per_sample, info->channel_mode);
// static_print_debug("codec_specific1: %04x codec_specific2: %04x codec_specific3: %04x codec_specific4: %04x",
// info->codec_specific1, info->codec_specific2, info->codec_specific3, info->codec_specific4);
// }
// static void A2dpSrcPlayingStateChanged(bd_addr_t *bdAddr, int playState)
// {
// static_print_debug("A2dpSrcPlayingStateChangedCallback addr: %02x%02x%02x%02x%02x%02x playState: %x",
// bdAddr->addr[5], bdAddr->addr[4], bdAddr->addr[3], /* 5 4 3 idx */
// bdAddr->addr[2], bdAddr->addr[1], bdAddr->addr[0], playState); /* 2 1 0 idx */
// }
static void sample_a2dp_src_connect_state_changed_callback(const bd_addr_t *bd_addr, int conn_state)
{
static_print_debug("addr: ****%02x%02x%02x%02x conn_state: %x", bd_addr->addr[3], bd_addr->addr[2],
bd_addr->addr[1], bd_addr->addr[0], conn_state); /* 3 2 1 0 idx */
if (g_src_connect_state_callback != NULL) {
static_print_debug("g_src_connect_state_callback");
g_src_connect_state_callback(conn_state);
}
if (conn_state == PROFILE_STATE_CONNECTED) {
avrcp_ct_connect(bd_addr);
audio_manager_set_device_connection_state(OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_CONNECTED);
} else if (conn_state == PROFILE_STATE_DISCONNECTING) {
} else if (conn_state == PROFILE_STATE_DISCONNECTED) {
audio_manager_set_device_connection_state(OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_DISCONNECTED);
}
// static_print_debug("addr: ****%02x%02x%02x%02x conn_state: %x",
// bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0], conn_state); /* 3 2 1 0 idx */
// if (conn_state == PROFILE_STATE_CONNECTED) {
// /* A2DP 连接建立成功后主动建立AVRCP连接 */
// int conn_ret = avrcp_ct_connect(bd_addr);
// if (conn_ret != ERRCODE_BT_SUCCESS) {
// static_print_debug("avrcp_ct_connect failed, ret: %d", conn_ret);
// }
// }
// else if (conn_state == PROFILE_STATE_DISCONNECTED) {
// /* A2DP 连接断开后主动断开AVRCP连接 */
// int disconn_ret = avrcp_ct_disconnect(bd_addr);
// if (disconn_ret != ERRCODE_BT_SUCCESS) {
// static_print_debug("avrcp_ct_disconnect failed, ret: %d", disconn_ret);
// }
// }
}
static void sample_a2dp_src_configuration_changed_callback(const bd_addr_t *bd_addr, const a2dp_codec_info_t *info)
{
static_print_debug("addr: ****%02x%02x%02x%02x ", bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1],
bd_addr->addr[0]); /* 3 2 1 0 idx */
static_print_debug(
"codec_priority: %04x codec_type: %04x sample_rate: %02x bits_per_sample: %02x channel_mode: %02x",
info->codec_priority, info->codec_type, info->sample_rate, info->bits_per_sample, info->channel_mode);
static_print_debug("codec_specific1: %04x codec_specific2: %04x codec_specific3: %04x codec_specific4: %04x",
(unsigned int)info->codec_specific1, (unsigned int)info->codec_specific2,
(unsigned int)info->codec_specific3, (unsigned int)info->codec_specific4);
}
static void sample_a2dp_src_playing_state_changed_callback(const bd_addr_t *bd_addr, int playing_state)
{
static_print_debug("addr: ****%02x%02x%02x%02x playing_state: %x", bd_addr->addr[3], bd_addr->addr[2],
bd_addr->addr[1], bd_addr->addr[0], playing_state); /* 3 2 1 0 idx */
/* 播放状态变化通知CT */
avrcp_tg_notify_playback_status_changed((unsigned char)playing_state);
}
void register_a2dpsrc_servers_callbacks(void)
{
static a2dp_src_callbacks_t g_sample_a2dp_src_callbacks = {
.connectstate_changed_cb = sample_a2dp_src_connect_state_changed_callback,
.configuration_changed_cb = sample_a2dp_src_configuration_changed_callback,
.playing_state_changed_cb = sample_a2dp_src_playing_state_changed_callback};
int ret = a2dp_src_register_callbacks(&g_sample_a2dp_src_callbacks);
if (ret != ERRCODE_BT_SUCCESS) {
static_print_debug("a2dp_src_register_callbacks failed, ret: %d", ret);
}
audio_manager_init(AUDIO_STREAM_MUSIC);
}
#pragma endregion
#pragma region a2dp snk
/************************************************************************
* A2DP SINK Server
*/
static void sample_a2dp_snk_connect_state_changed_callback(const bd_addr_t *bd_addr, int conn_state)
{
// static_print_debug("sample_a2dp_snk_connect_state_changed_callback ba_addr: %p ",bd_addr);
if (bd_addr == NULL) {
static_print_debug("sample_a2dp_snk_connect_state_changed_callback: bd_addr is NULL");
return;
}
static_print_debug("a2dp_snk_connect_state_changed_callback addr: ****%02x%02x%02x%02x conn_state: %x",
bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0],
conn_state); /* 3 2 1 0 idx */
if (g_snk_connect_state_callback != NULL) {
static_print_debug("g_snk_connect_state_callback");
g_snk_connect_state_callback(conn_state);
}
if (conn_state == PROFILE_STATE_CONNECTED) {
/* A2DP 连接建立成功后主动建立AVRCP连接 */
int conn_ret = avrcp_tg_connect(bd_addr);
if (conn_ret != ERRCODE_BT_SUCCESS) {
static_print_debug("avrcp_tg_connect failed, ret: %x", conn_ret);
}
static_print_debug("test mode auto reg");
g_tjd_avrcp_srv_inf.avrcp_button_flag = AVRCP_SRV_RELEASED_VALUE;
g_tjd_avrcp_srv_inf.reg_cap_auto = AVRCP_CT_AUTO_REG_TYPE;
conn_ret = avrcp_ct_connect(bd_addr);
if (conn_ret != ERRCODE_BT_SUCCESS) {
static_print_debug("avrcp_ct_connect failed, ret: %x", conn_ret);
}
}
if (conn_state == PROFILE_STATE_DISCONNECTED) {
/* A2DP 连接断开后主动断开AVRCP连接 */
int disconn_ret = avrcp_tg_disconnect(bd_addr);
if (disconn_ret != ERRCODE_BT_SUCCESS) {
static_print_debug("avrcp_tg_disconnect failed, ret: %x", disconn_ret);
}
}
}
static void sample_a2dp_snk_accept_timer_stop(void)
{
static_print_debug("stop timer");
if (g_tjd_a2dp_stream_accept_timer.timer != NULL) {
(void)osal_timer_stop(&g_tjd_a2dp_stream_accept_timer);
(void)osal_timer_destroy(&g_tjd_a2dp_stream_accept_timer);
g_tjd_a2dp_stream_accept_timer.timer = NULL;
}
static_print_debug("stop timer success!");
}
bool g_isA2dpSnkConnected = false;
static void sample_a2dp_snk_start_accept_handler(unsigned long data)
{
static_print_debug("sample_a2dp_snk_start_accept_handler");
// bts_unused(data);
// if (g_isA2dpSnkConnected) {
bd_addr_t active_bd_addr = a2dp_snk_get_active_device();
a2dp_snk_start_accept(&active_bd_addr);
sample_a2dp_snk_accept_timer_stop();
// }
}
static int32_t sample_a2dp_snk_start_accept_timer(void)
{
static_print_debug("start timer");
if (g_tjd_a2dp_stream_accept_timer.timer != NULL) {
static_print_debug("timer is running");
return EXT_ERR_SUCCESS;
}
g_tjd_a2dp_stream_accept_timer.handler = sample_a2dp_snk_start_accept_handler;
g_tjd_a2dp_stream_accept_timer.interval = A2DP_STREAM_ACCEPT_TIMER_PERIOD;
int32_t timer_ret = osal_timer_init(&g_tjd_a2dp_stream_accept_timer);
if (timer_ret != EOK) {
static_print_debug("create timer error! ret: %x", timer_ret);
sample_a2dp_snk_accept_timer_stop();
return EXT_ERR_FAILURE;
}
timer_ret = osal_timer_start(&g_tjd_a2dp_stream_accept_timer);
if (timer_ret != EOK) {
static_print_debug("start timer error! ret: %x", timer_ret);
sample_a2dp_snk_accept_timer_stop();
return EXT_ERR_FAILURE;
}
return EXT_ERR_SUCCESS;
}
static int32_t sample_a2dp_snk_audio_create(void)
{
#if MEDIA_A2DP_SNK_INTERFACE
static_print_debug("audioFormat %d channelCount %d sampleRate %u ", g_audio_format_config.audioFormat,
g_audio_format_config.channelCount, g_audio_format_config.sampleRate);
// g_audio_format_config.audioFormat =
int32_t ret = AudioManagerA2dpSinkSetParam(g_audio_format_config);
static_print_debug("AudioManagerA2dpSinkSetParam ret %x", ret);
ret = AudioManagerA2dpSinkCreate();
static_print_debug("AudioManagerA2dpSinkCreate ret %x", ret);
#endif
/* 起定时器接受音乐开始请求 */
sample_a2dp_snk_start_accept_timer();
return 0;
}
static void sample_a2dp_snk_start_stream_request_callback(const bd_addr_t *bd_addr)
{
// static_print_debug("sample_a2dp_snk_start_stream_request_callback ba_addr: %p ",bd_addr);
if (bd_addr == NULL) {
static_print_debug("sample_a2dp_snk_start_stream_request_callback: bd_addr is NULL");
return;
}
static_print_debug("a2dp_snk_start_stream_request_callback addr: ****%02x%02x%02x%02x ", bd_addr->addr[3],
bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0]); /* 3 2 1 0 idx */
sample_a2dp_snk_audio_create();
}
static int32_t sample_a2dp_snk_audio_destroy(void)
{
#if MEDIA_A2DP_SNK_INTERFACE
int32_t ret = AudioManagerA2dpSinkDestroy();
static_print_debug("AudioManagerA2dpSinkDestroy ret %x", ret);
#endif
return 0;
}
static void sample_a2dp_snk_playing_state_changed_callback(const bd_addr_t *bd_addr, int playing_state)
{
// static_print_debug("sample_a2dp_snk_playing_state_changed_callback ba_addr: %p ",bd_addr);
if (bd_addr == NULL) {
static_print_debug("sample_a2dp_snk_playing_state_changed_callback: bd_addr is NULL");
return;
}
static_print_debug("a2dp_snk_playing_state_changed_callback addr: ****%02x%02x%02x%02x playing_state: %x",
bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0],
playing_state); /* 3 2 1 0 idx */
if (playing_state == A2DP_NOT_PLAYING) {
sample_a2dp_snk_audio_destroy();
}
// }else if (playing_state == A2DP_IS_PLAYING){
// static_print_debug("a2dp is playing...");
// // avrcp_ct_get_element_attr_para_t element_attr_param;
// // element_attr_param.attr_num = AVRCP_CT_ELEMENT_ATTR_TRACK_PLAYING_TIME;
// // element_attr_param.attr_id_list =
// // (unsigned int *)(void *)osal_kmalloc(sizeof(unsigned int) *
// element_attr_param.attr_num,OSAL_GFP_KERNEL);
// // for (int i = 0; i < element_attr_param.attr_num; i++) {
// // element_attr_param.attr_id_list[i] = (unsigned int)i + 1;
// // }
// // avrcp_ct_get_element_attributes(bd_addr,&element_attr_param);
// // if (element_attr_param.attr_id_list != NULL) {
// // osal_kfree(element_attr_param.attr_id_list);
// // element_attr_param.attr_id_list = NULL;
// // }
// }
}
static void sample_a2dp_snk_configuration_changed_callback(const bd_addr_t *bd_addr, const a2dp_snk_codec_info_t *info)
{
// static_print_debug("sample_a2dp_snk_configuration_changed_callback ba_addr: %p info: %p",bd_addr,info);
if (bd_addr == NULL || info == NULL) {
static_print_debug("sample_a2dp_snk_configuration_changed_callback: bd_addr or infois NULL");
return;
}
static_print_debug("a2dp_snk_configuration_changed_callback addr: ****%02x%02x%02x%02x ", bd_addr->addr[3],
bd_addr->addr[2], bd_addr->addr[1], bd_addr->addr[0]); /* 3 2 1 0 idx */
static_print_debug(
"codec_priority: %04x codec_type: %04x sample_rate: %02x bits_per_sample: %02x channel_mode: %02x",
info->codec_priority, info->codec_type, info->sample_rate, info->bits_per_sample, info->channel_mode);
static_print_debug("codec_specific1: %04x codec_specific2: %04x codec_specific3: %04x codec_specific4: %04x",
(unsigned int)info->codec_specific1, (unsigned int)info->codec_specific2,
(unsigned int)info->codec_specific3, (unsigned int)info->codec_specific4);
#if MEDIA_A2DP_SNK_INTERFACE
if (info->codec_type == A2DP_SNK_CODEC_TYPE_AAC) {
g_audio_format_config.audioFormat = AAC_LC;
} else {
g_audio_format_config.audioFormat = SBC;
}
/* channel count 1 or 2 */
g_audio_format_config.channelCount = (info->channel_mode == A2DP_SNK_CODEC_CHANNEL_MODE_NONE ? 1 : 2);
switch (info->sample_rate) {
case A2DP_SNK_CODEC_SAMPLE_RATE_48000:
g_audio_format_config.sampleRate = BT_AUDIO_A2DP_SMAPLE_RATE48000;
break;
case A2DP_SNK_CODEC_SAMPLE_RATE_88200:
g_audio_format_config.sampleRate = BT_AUDIO_A2DP_SMAPLE_RATE88200;
break;
case A2DP_SNK_CODEC_SAMPLE_RATE_96000:
g_audio_format_config.sampleRate = BT_AUDIO_A2DP_SMAPLE_RATE96000;
break;
default:
g_audio_format_config.sampleRate = BT_AUDIO_A2DP_SMAPLE_RATE44100;
break;
}
static_print_debug("audioFormat %d channelCount %d sampleRate %u ", g_audio_format_config.audioFormat,
g_audio_format_config.channelCount, g_audio_format_config.sampleRate);
#endif
}
void register_a2dpsnk_servers_callback(void)
{
static a2dp_snk_callbacks_t g_sample_a2dp_snk_callbacks = {
.connectstate_changed_cb = sample_a2dp_snk_connect_state_changed_callback,
.start_stream_request_cb = sample_a2dp_snk_start_stream_request_callback,
.playing_state_changed_cb = sample_a2dp_snk_playing_state_changed_callback,
.configuration_changed_cb = sample_a2dp_snk_configuration_changed_callback};
int ret = a2dp_snk_register_callbacks(&g_sample_a2dp_snk_callbacks);
if (ret != ERRCODE_BT_SUCCESS) {
static_print_debug("a2dp_snk_register_callbacks ret(int): %x", ret);
}
}
#pragma endregion
#pragma region avrcp ct server
/*************************************************************
* AVRCP CT SERVER
*/
static bd_addr_t *sample_avrcp_srv_get_active_addr(void) { return &g_tjd_avrcp_srv_inf.bd_addr; }
static void sample_avrcp_ct_connect_state_changed_callback(const bd_addr_t *bd_addr, profile_connect_state_t state)
{
if (bd_addr == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("avrcp ct update conn stat:%u", state);
(void)memcpy_s(g_tjd_avrcp_srv_inf.bd_addr.addr, BD_ADDR_LEN, bd_addr->addr, BD_ADDR_LEN);
static_print_debug("addr: ****%02x%02x%02x%02x ", bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1],
bd_addr->addr[0]); /* 3 2 1 0 idx */
g_tjd_avrcp_srv_inf.avrcp_srv_conn_stat = state;
avrcp_ct_get_supported_events(g_tjd_avrcp_srv_inf.bd_addr.addr);
}
static void sample_avrcp_ct_press_button_rsp_cbk(const bd_addr_t *bd_addr, int32_t key_operation)
{
if (bd_addr == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("avrcp ct press button rsp_cbk key:%d", key_operation);
if (g_tjd_avrcp_srv_inf.avrcp_button_flag == AVRCP_SRV_RELEASED_VALUE) {
static_print_debug("avrcp ct press button button_flag change:%u->%u", g_tjd_avrcp_srv_inf.avrcp_button_flag,
AVRCP_SRV_PRESS_VALUE);
g_tjd_avrcp_srv_inf.avrcp_button_flag = AVRCP_SRV_PRESS_VALUE;
}
static_print_debug("avrcp ct press button cbk key:%d button_flag:%u", key_operation,
g_tjd_avrcp_srv_inf.avrcp_button_flag);
}
static void sample_avrcp_press_release_button_log(uint32_t key_operation)
{
switch (key_operation) {
case AVRCP_KEY_VOLUME_UP:
static_print_debug("AVRCP_KEY_VOLUME_UP");
break;
case AVRCP_KEY_VOLUME_DOWN:
static_print_debug("AVRCP_KEY_VOLUME_DOWN");
break;
case AVRCP_KEY_MUTE:
static_print_debug("AVRCP_KEY_VOLUME_MUTE");
break;
case AVRCP_KEY_PLAY:
static_print_debug("AVRCP_KEY_PLAY");
break;
case AVRCP_KEY_STOP:
static_print_debug("AVRCP_KEY_STOP");
break;
case AVRCP_KEY_PAUSE:
static_print_debug("AVRCP_KEY_PAUSE");
break;
case AVRCP_KEY_FORWARD:
static_print_debug("AVRCP_KEY_FORWARD");
break;
case AVRCP_KEY_BACKWARD:
static_print_debug("AVRCP_KEY_BACKWARD");
break;
case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED:
static_print_debug("AVRCP_EVENT_VOLUME_CHANGED");
break;
default:
static_print_debug("invalid");
break;
}
}
static void sample_avrcp_ct_release_button_rsp_cbk(const bd_addr_t *bd_addr, int32_t key_operation)
{
if (bd_addr == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("avrcp ct release button cbk key:%d", key_operation);
if (g_tjd_avrcp_srv_inf.avrcp_button_flag == AVRCP_SRV_PRESS_VALUE) {
static_print_debug("avrcp ct released button button_flag change:%u->%u", g_tjd_avrcp_srv_inf.avrcp_button_flag,
AVRCP_SRV_RELEASED_VALUE);
g_tjd_avrcp_srv_inf.avrcp_button_flag = AVRCP_SRV_RELEASED_VALUE;
}
sample_avrcp_press_release_button_log((uint32_t)key_operation);
}
static void sample_avrcp_ct_support_company_callback(const bd_addr_t *bd_addr,
const avrcp_ct_support_company_para_t *company)
{
if (bd_addr == NULL || company == NULL || company->company_id == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("sample_avrcp_ct_get_support_company company list:");
for (uint8_t i = 0; i < company->company_num; i++) {
static_print_debug(" 0x%x ", *company->company_id);
}
}
static void sample_avrcp_ct_support_event_callback(const bd_addr_t *bd_addr, avrcp_ct_support_event_para_t *event)
{
if (bd_addr == NULL || event == NULL || event->event_id == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("sample_avrcp_ct_support_event_callback event list:");
for (uint8_t i = 0; i < event->event_num; i++) {
static_print_debug(" 0x%x ", event->event_id[i]);
}
if (g_tjd_avrcp_srv_inf.reg_cap_auto != AVRCP_CT_AUTO_REG_TYPE) {
return;
}
uint8_t avrcp_ct_cap[] = {
AVRCP_NOTIFY_EVENT_RESERVED, // 0
AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, // 1
AVRCP_NOTIFY_EVENT_TRACK_CHANGED,
AVRCP_NOTIFY_EVENT_TRACK_REACHED_END,
AVRCP_NOTIFY_EVENT_TRACK_REACHED_START,
AVRCP_NOTIFY_EVENT_RESERVED, // 5
AVRCP_NOTIFY_EVENT_RESERVED,
AVRCP_NOTIFY_EVENT_RESERVED,
AVRCP_NOTIFY_EVENT_RESERVED,
AVRCP_NOTIFY_EVENT_NOW_PLAYING_CONTENT_CHANGED, // 9
AVRCP_NOTIFY_EVENT_AVAILABLE_PLAYERS_CHANGED, // A
AVRCP_NOTIFY_EVENT_ADDRESSED_PLAYER_CHANGED,
AVRCP_NOTIFY_EVENT_RESERVED, // 0xC
AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, // 0xD
};
for (uint8_t i = 0; i < event->event_num; i++) {
if (event->event_id[i] < AVRCP_NOTIFY_EVENT_RESERVED &&
avrcp_ct_cap[event->event_id[i]] != AVRCP_NOTIFY_EVENT_RESERVED) {
avrcp_ct_register_notification(bd_addr, event->event_id[i], 0);
static_print_debug("avrcp_ct_register_notification auto reg event_id:0x%x", event->event_id[i]);
}
}
}
static void sample_avrcp_ct_get_element_attri_callback(const bd_addr_t *bd_addr,
avrcp_ct_element_attr_para_cb_t *attr_cb_para)
{
if (bd_addr == NULL || attr_cb_para == NULL || attr_cb_para->attr_value == NULL) {
static_print_debug("null pointer");
return;
}
// static_print_debug("sample_avrcp_ct_get_element_attri_callback");
for (uint8_t i = 0; i < attr_cb_para->attr_num; i++) {
// static_print_debug("attrId-charaId-valLen: 0x%x-0x%x-%u ", attr_cb_para->attr_value[i].attr_id,
// attr_cb_para->attr_value[i].character_set_id, attr_cb_para->attr_value[i].value_len);
switch (attr_cb_para->attr_value[i].attr_id) {
case AVRCP_CT_ELEMENT_ATTR_TITLE:
// static_print_debug("Attr Title:");
if (g_sync_lrc_callback != NULL) {
// static_print_debug("g_sync_lrc_callback((const char *)value);");
g_sync_lrc_callback((const char *)attr_cb_para->attr_value[i].value, isFirst);
} else {
// static_print_debug("g_sync_lrc_callback is NULL");
}
if (isFirst) {
isFirst = false;
}
break;
case AVRCP_CT_ELEMENT_ATTR_ARTIST_NAME:
// static_print_debug("Attr Name:");
break;
case AVRCP_CT_ELEMENT_ATTR_ALBUM_NAME:
// static_print_debug("Attr Album:");
break;
case AVRCP_CT_ELEMENT_ATTR_TRACK_NUMBER:
// static_print_debug("Attr Track Number:");
// uint8_t *value = (uint8_t *)(void *)osal_kmalloc(attr_cb_para->attr_value[i].value_len + 1,
// OSAL_GFP_KERNEL); if (attr_cb_para->attr_value[i].value_len > 0 && attr_cb_para->attr_value[i].value !=
// NULL) {
// if (memcpy_s(value, attr_cb_para->attr_value[i].value_len, attr_cb_para->attr_value[i].value,
// attr_cb_para->attr_value[i].value_len) != EOK) {
// osal_kfree(value);
// static_print_debug("memcpy_s fail.");
// return;
// }
// }
// value[attr_cb_para->attr_value[i].value_len] = '\0';
// static_print_debug(" %s ", value);
// osal_kfree(value);
break;
case AVRCP_CT_ELEMENT_ATTR_TOTAL_NUMBER_OF_TRACKS:
// static_print_debug("Attr Total Number Of Track:");
break;
case AVRCP_CT_ELEMENT_ATTR_TRACK_GENRE:
// static_print_debug("Attr Track GENRE:");
break;
case AVRCP_CT_ELEMENT_ATTR_TRACK_PLAYING_TIME:
// static_print_debug("Attr Track Playing Time_ms:");
break;
default:
// static_print_debug("Other:");
break;
}
}
}
static void sample_avrcp_ct_hdl_notify_volume_rsp_cbk(avrcp_ct_notification_value_cb_t *notification)
{
static_print_debug("Set local volume:0x%x", notification->volume); /* 注册事件后通知的音量 */
}
static void sample_avrcp_ct_notification_callback(const bd_addr_t *bd_addr, avrcp_notify_event_t event_id,
avrcp_ct_notification_value_cb_t *notification)
{
if (bd_addr == NULL || notification == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("ct notification cbk event_id:0x%x playback_position %d", event_id,
notification->playback_position);
bool notify_flag = true;
switch (event_id) {
case AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED:
static_print_debug("tg notify ct playback status changed :%u", notification->play_status);
if (g_playing_state_callback != NULL) {
static_print_debug("g_playing_state_callback");
g_playing_state_callback(notification->play_status);
}
break;
case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED:
sample_avrcp_ct_hdl_notify_volume_rsp_cbk(notification);
break;
case AVRCP_NOTIFY_EVENT_NOW_PLAYING_CONTENT_CHANGED:
static_print_debug("play conent changed event_id:0x%x", event_id);
break;
case AVRCP_NOTIFY_EVENT_TRACK_CHANGED:
static_print_debug("track changed event_id:0x%x", event_id);
break;
case AVRCP_NOTIFY_EVENT_TRACK_REACHED_END:
case AVRCP_NOTIFY_EVENT_TRACK_REACHED_START:
case AVRCP_NOTIFY_EVENT_PLAYBACK_POS_CHANGED:
case AVRCP_NOTIFY_EVENT_BATT_STATUS_CHANGED:
case AVRCP_NOTIFY_EVENT_SYSTEM_STATUS_CHANGED:
case AVRCP_NOTIFY_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
case AVRCP_NOTIFY_EVENT_AVAILABLE_PLAYERS_CHANGED:
case AVRCP_NOTIFY_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_NOTIFY_EVENT_UIDS_CHANGED:
case AVRCP_NOTIFY_EVENT_RESERVED:
default:
notify_flag = true;
break;
}
if (notify_flag) {
static_print_debug("notify reg event_id:%u", event_id);
avrcp_ct_register_notification(bd_addr, event_id, 0); /* TG通知变化后CT重新注册事件 */
avrcp_ct_get_element_attr_para_t element_attr_param;
element_attr_param.attr_num = AVRCP_CT_ELEMENT_ATTR_TRACK_PLAYING_TIME;
element_attr_param.attr_id_list =
(unsigned int *)(void *)malloc(sizeof(unsigned int) * element_attr_param.attr_num);
for (int i = 0; i < element_attr_param.attr_num; i++) {
element_attr_param.attr_id_list[i] = (unsigned int)i + 1;
}
avrcp_ct_get_element_attributes(bd_addr, &element_attr_param);
if (element_attr_param.attr_id_list != NULL) {
free(element_attr_param.attr_id_list);
element_attr_param.attr_id_list = NULL;
}
}
}
static void sample_avrcp_ct_get_play_status_callback(const bd_addr_t *bd_addr, const avrcp_ct_play_status_cb_t *status)
{
if (bd_addr == NULL || status == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("test get play status songlen:%u, songpos:%u, playstatus:%u:", status->song_length,
status->song_position, status->play_status);
// if() {
// isFirst = true;
// }
}
void register_avrcp_ct_servers_callbacks(void)
{
static avrcp_ct_callbacks_t g_sample_avrcp_ct_bts_cbk = {
sample_avrcp_ct_connect_state_changed_callback, sample_avrcp_ct_press_button_rsp_cbk,
sample_avrcp_ct_release_button_rsp_cbk, sample_avrcp_ct_support_company_callback,
sample_avrcp_ct_support_event_callback, sample_avrcp_ct_get_element_attri_callback,
sample_avrcp_ct_get_play_status_callback, sample_avrcp_ct_notification_callback,
};
int ret = avrcp_ct_register_callbacks(&g_sample_avrcp_ct_bts_cbk);
if (ret != ERRCODE_SUCC) {
static_print_debug("avrcp_ct_register_callbacks fail:%d", ret);
}
}
#pragma endregion
#pragma region AVRCP TG SERVERS
/***************************************************************
* AVRCP TG SERVERS
*/
static void sample_avrcp_tg_connect_state_changed_callback(const bd_addr_t *bd_addr, profile_connect_state_t state)
{
if (bd_addr == NULL) {
static_print_debug("null pointer");
return;
}
static_print_debug("avrcp tg update conn stat:%u", state);
if (memcpy_s(&g_tjd_avrcp_srv_inf.bd_addr, BD_ADDR_LEN, bd_addr->addr, BD_ADDR_LEN) != EOK) {
static_print_debug("memcpy_s fail.");
return;
}
g_tjd_avrcp_srv_inf.avrcp_srv_conn_stat = state;
static_print_debug("addr: ****%02x%02x%02x%02x ", bd_addr->addr[3], bd_addr->addr[2], bd_addr->addr[1],
bd_addr->addr[0]); /* 3 2 1 0 idx */
}
void register_avrcp_tg_servers_callbacks(void)
{
static avrcp_tg_callbacks_t g_sample_avrcp_tg_bts_cbk = {
sample_avrcp_tg_connect_state_changed_callback,
};
int ret = avrcp_tg_register_callbacks(&g_sample_avrcp_tg_bts_cbk);
if (ret != ERRCODE_SUCC) {
static_print_debug("avrcp_tg_register_callbacks fail:%d", ret);
}
}
static void sample_avrcp_set_notify(bt_avrcp_tg_evt_status_param *notify_event_status_param,
avrcp_notify_event_t event_id, uint8_t event_status)
{
notify_event_status_param->event_id = event_id;
notify_event_status_param->event_status[0] = event_status;
}
static void avrcp_tg_notify_press_button_callback(uint32_t key_operation, uint32_t key_value)
{
bt_avrcp_tg_evt_status_param param;
static_print_debug("sample_avcrp_tg_notify:key_type:%u", key_operation);
// sample_avrcp_press_release_button_log(key_operation);
switch (key_operation) {
case AVRCP_KEY_VOLUME_UP:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, g_avrcp_srv_sample_inf.loc_volume);
break;
case AVRCP_KEY_VOLUME_DOWN:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, g_avrcp_srv_sample_inf.loc_volume);
break;
case AVRCP_KEY_MUTE:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, 0x0);
break;
case AVRCP_KEY_PLAY:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_PLAYING);
break;
case AVRCP_KEY_STOP:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_STOPPED);
break;
case AVRCP_KEY_PAUSE:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_PAUSED);
break;
case AVRCP_KEY_FORWARD:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_FWD_SEEK);
break;
case AVRCP_KEY_BACKWARD:
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_REV_SEEK);
break;
case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED:
static_print_debug("set absolute volume:%u", key_value);
// sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, (uint8_t)key_value);
break;
default:
sample_avrcp_set_notify(&param, AVRCP_NOTIFY_EVENT_RESERVED, AVRCP_PLAY_STATUS_ERROR);
break;
}
}
static void avrcp_tg_get_loc_media_status_callback(bt_avrcp_tg_evt_status_param *event_status_param)
{
if (event_status_param == NULL) {
return;
}
event_status_param->event_status[0] = 0xff;
uint8_t stream_stat;
switch (event_status_param->event_id) {
case AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED:
stream_stat = a2dp_src_get_stream_status();
if (stream_stat == SAMPLE_A2DP_STREAM_START) {
event_status_param->event_status[0] = AVRCP_PLAY_STATUS_PLAYING;
} else if (stream_stat == SAMPLE_A2DP_STREAM_SUSPENDED) {
event_status_param->event_status[0] = AVRCP_PLAY_STATUS_PAUSED;
} else {
event_status_param->event_status[0] = AVRCP_PLAY_STATUS_STOPPED;
}
break;
case AVRCP_NOTIFY_EVENT_TRACK_CHANGED:
case AVRCP_NOTIFY_EVENT_TRACK_REACHED_END:
break;
case AVRCP_NOTIFY_EVENT_TRACK_REACHED_START:
break;
case AVRCP_NOTIFY_EVENT_PLAYBACK_POS_CHANGED:
break;
case AVRCP_NOTIFY_EVENT_BATT_STATUS_CHANGED:
case AVRCP_NOTIFY_EVENT_SYSTEM_STATUS_CHANGED:
case AVRCP_NOTIFY_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
case AVRCP_NOTIFY_EVENT_NOW_PLAYING_CONTENT_CHANGED:
break;
case AVRCP_NOTIFY_EVENT_AVAILABLE_PLAYERS_CHANGED:
break;
case AVRCP_NOTIFY_EVENT_ADDRESSED_PLAYER_CHANGED:
break;
case AVRCP_NOTIFY_EVENT_UIDS_CHANGED:
break;
case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED:
static_print_debug("avrcp_tg_get_loc_media_status_callback: volume:%u", event_status_param->event_status[0]);
// event_status_param->event_status[0] = avrcp_tg_get_loc_media_volume();
break;
case AVRCP_NOTIFY_EVENT_RESERVED:
default:
break;
}
static_print_debug("sample_avrcp_tg_get_loc_media_status_callback event_id:0x%x event_status:%u",
event_status_param->event_id, event_status_param->event_status[0]);
}
void register_bt_avrcp_tg_bts_callback(void)
{
static bt_avrcp_tg_bts_cbk g_sample_avrcp_tg_callbacks = {
avrcp_tg_notify_press_button_callback,
avrcp_tg_get_loc_media_status_callback,
};
bt_avrcp_tg_register_audio_cbk(&g_sample_avrcp_tg_callbacks);
}
#pragma endregion
void register_playing_state_callback(playing_state_callback_t callback)
{
static_print_debug("register_playing_state_callback");
g_playing_state_callback = callback;
}
void unregister_playing_state_callback(void) { g_playing_state_callback = NULL; }
void register_snk_connect_state_callback(snk_connect_state_callback_t callback)
{
static_print_debug("g_snk_connect_state_callback");
g_snk_connect_state_callback = callback;
}
void unregister_snk_connect_state_callback(void) { g_snk_connect_state_callback = NULL; }
void register_src_connect_state_callback(src_connect_state_callback_t callback)
{
g_src_connect_state_callback = callback;
}
void unregister_src_connect_state_callback(void) { g_src_connect_state_callback = NULL; }
void register_sync_lrc_callback(sync_lrc_callback_t callback)
{
static_print_debug("g_sync_lrc_callback");
g_sync_lrc_callback = callback;
}
void unregister_sync_lrc_callback(void) { g_sync_lrc_callback = NULL; }