#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(¶m, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, g_avrcp_srv_sample_inf.loc_volume); break; case AVRCP_KEY_VOLUME_DOWN: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, g_avrcp_srv_sample_inf.loc_volume); break; case AVRCP_KEY_MUTE: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, 0x0); break; case AVRCP_KEY_PLAY: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_PLAYING); break; case AVRCP_KEY_STOP: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_STOPPED); break; case AVRCP_KEY_PAUSE: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_PAUSED); break; case AVRCP_KEY_FORWARD: // sample_avrcp_set_notify(¶m, AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED, AVRCP_PLAY_STATUS_FWD_SEEK); break; case AVRCP_KEY_BACKWARD: // sample_avrcp_set_notify(¶m, 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(¶m, AVRCP_NOTIFY_EVENT_VOLUME_CHANGED, (uint8_t)key_value); break; default: sample_avrcp_set_notify(¶m, 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; }