/*---------------------------------------------------------------------------- * Copyright (c) TJD Technologies Co., Ltd. 2025. All rights reserved. * * Description: TjdUiBTHeadsetEvent.cpp * * Author: luziquan@ss-tjd.com * * Create: 2025-04-09 *--------------------------------------------------------------------------*/ #include "TjdUiAppPlayerModel.h" #include "TjdUiAppPlayerView.h" #include "audio_manager.h" #include "bt_audio.h" #include "bts_avrcp_controller.h" #include "bts_avrcp_target.h" #include "bts_br_gap.h" #include "bts_def.h" #include "graphic_service.h" #include "sys_config.h" #include "td_type.h" using namespace OHOS; using namespace ::Audio; namespace TJD { #define ENABLE_PRINT_INFO 1 #define ENABLE_DEBUG 1 #if ENABLE_PRINT_INFO #define static_print_info(...) sys_ui_log_i(__VA_ARGS__) // 一般信息打印宏控制 #define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) // 警告信息打印一般常开 #define static_print_error(...) sys_ui_log_e(__VA_ARGS__) // 错误信息打印一般常开 #if ENABLE_DEBUG #define static_print_debug(...) sys_ui_log_d(__VA_ARGS__) // 调试信息打印一般常开 #else #define static_print_debug(...) #endif #else #define static_print_info(...) #define static_print_warn(...) #define static_print_error(...) #endif static void UsingPressButtonStatusCallback(td_u32 keyOperation, td_u32 keyValue); static void MediaStatusCallback(bt_avrcp_tg_evt_status_param *eventStatusParam); static void PressButtonStatusCallback(td_u32 keyOperation, td_u32 keyValue); static void ConnCtStateChangedCallback(const bd_addr_t *bdAddr, profile_connect_state_t state); static void ConnectTgStateChangedCallback(const bd_addr_t *bdAddr, profile_connect_state_t state); enum AVRCP_SRV_STATE { AVRCP_SRV_PRESS_VALUE, AVRCP_SRV_RELEASED_VALUE, AVRCP_SRV_INIT_VALUE }; #define AVRCP_CT_AUTO_REG_TYPE 1 static uint8_t g_avrcp_tg_local_media_vol = AVRCP_ABSOLUTE_VOLUME_INVALID; #define BT_A2DP_MEDIA_VOL_LEVEL 100 static bt_avrcp_tg_bts_cbk g_avrcp_tg_audio_cb = {}; static avrcp_ct_callbacks_t g_avrcp_ct_cb = {}; static avrcp_tg_callbacks_t g_avrcp_tg_cb = {}; static BtAvrcpTgInf g_avrcp_srv_inf; BtAvrcpTgInf *GetAvrcpTgInf(void) { return &g_avrcp_srv_inf; } void BtAvrcpVolumeChangedSyncMedia(uint8_t a2dp_volume) { static_print_info("[BtAvrcpVolumeChangedSyncMedia]::a2dp_volume = 0x%x", a2dp_volume); int32_t audio_volume = a2dp_volume * BT_A2DP_MEDIA_VOL_LEVEL / AVRCP_ABSOLUTE_VOLUME_PERCENTAGE_100; AudioManager::GetInstance().SetVolume(AUDIO_STREAM_A2DP_MUSIC, audio_volume); /* 设置增益音量 */ g_avrcp_tg_local_media_vol = a2dp_volume; avrcp_tg_notify_volume_changed(a2dp_volume); TjdUiAppPlayerView *view = TjdUiAppPlayerView::GetInstance(); if (view != nullptr) { if (view->GetCurrentView() == PlayerViewIndex::VOLUME_VIEW) { view->ChangedVolume(audio_volume); } } } /* tg通知媒体状态 */ static void BtAvrcpTgNotifyMeidaStatus(bt_avrcp_tg_evt_status_param *notifyEventStatusParam) { if (notifyEventStatusParam == nullptr || notifyEventStatusParam->event_id >= AVRCP_NOTIFY_EVENT_RESERVED) { static_print_error("[BtAvrcpTgNotifyMeidaStatus] param is error"); return; } switch (notifyEventStatusParam->event_id) { case AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED: avrcp_tg_notify_playback_status_changed(*(td_u8 *)notifyEventStatusParam->event_status); break; case AVRCP_NOTIFY_EVENT_TRACK_CHANGED: avrcp_tg_notify_track_changed(*(unsigned long long *)notifyEventStatusParam->event_status); break; case AVRCP_NOTIFY_EVENT_TRACK_REACHED_END: avrcp_tg_notify_track_reached_end(); break; case AVRCP_NOTIFY_EVENT_TRACK_REACHED_START: avrcp_tg_notify_track_reached_start(); break; case AVRCP_NOTIFY_EVENT_PLAYBACK_POS_CHANGED: avrcp_tg_notify_playback_pos_changed(*(td_u32 *)notifyEventStatusParam->event_status); break; case AVRCP_NOTIFY_EVENT_BATT_STATUS_CHANGED: case AVRCP_NOTIFY_EVENT_SYSTEM_STATUS_CHANGED: case AVRCP_NOTIFY_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: break; case AVRCP_NOTIFY_EVENT_NOW_PLAYING_CONTENT_CHANGED: avrcp_tg_notify_now_playing_content_changed(); break; case AVRCP_NOTIFY_EVENT_AVAILABLE_PLAYERS_CHANGED: avrcp_tg_notify_available_players_changed(); break; case AVRCP_NOTIFY_EVENT_ADDRESSED_PLAYER_CHANGED: avrcp_tg_notify_addressed_players_changed(*(td_u16 *)notifyEventStatusParam->event_status, *(td_u16 *)(notifyEventStatusParam->event_status + sizeof(td_u16))); break; case AVRCP_NOTIFY_EVENT_UIDS_CHANGED: avrcp_tg_notify_uid_changed(*(td_u16 *)notifyEventStatusParam->event_status); break; case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED: BtAvrcpVolumeChangedSyncMedia(notifyEventStatusParam->event_status[0]); break; case AVRCP_NOTIFY_EVENT_RESERVED: break; default: break; } } void PressButtonStatusCallback(td_u32 keyOperation, td_u32 keyValue) { static_print_debug("[PressButtonStatusCallback]::keyValue = 0x%x keyOperation = 0x%x", keyValue, keyOperation); GraphicService::GetInstance()->PostGraphicEvent(std::bind(UsingPressButtonStatusCallback, keyOperation, keyValue)); } void BtAvrcpTgNotifyVolumeChangedProc(uint8_t a2dp_volume) { int32_t audio_volume = a2dp_volume * BT_A2DP_MEDIA_VOL_LEVEL / AVRCP_ABSOLUTE_VOLUME_PERCENTAGE_100; static_print_info("NotifyVolume = %d", audio_volume); AudioManager::GetInstance().SetVolume(AUDIO_STREAM_MUSIC, audio_volume); // 耳机连接手表场景调节手表音量 } void UsingPressButtonStatusCallback(td_u32 keyOperation, td_u32 keyValue) { bt_avrcp_tg_evt_status_param event; switch (keyOperation) { case AVRCP_KEY_VOLUME_UP: // 耳机控制手表加减音量不走key_operation事件 case AVRCP_KEY_VOLUME_DOWN: // 减小音量 break; case AVRCP_KEY_MUTE: // 静音 event.event_status[0] = AVRCP_ABSOLUTE_VOLUME_PERCENTAGE_0; event.event_id = AVRCP_NOTIFY_EVENT_VOLUME_CHANGED; break; case AVRCP_KEY_PLAY: // 播放 // (void)TjdUiAppPlayerModel::GetInstance()->PlayerPlay(); event.event_status[0] = AVRCP_PLAY_STATUS_PLAYING; event.event_id = AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED; break; case AVRCP_KEY_STOP: // 停止 // (void)TjdUiAppPlayerModel::GetInstance()->PlayerStop(); event.event_status[0] = AVRCP_PLAY_STATUS_STOPPED; event.event_id = AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED; break; case AVRCP_KEY_PAUSE: // 暂停 // (void)TjdUiAppPlayerModel::GetInstance()->PlayerPause(); event.event_status[0] = AVRCP_PLAY_STATUS_PAUSED; event.event_id = AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED; break; case AVRCP_KEY_FORWARD: // 下一曲 // TjdUiAppPlayerModel::GetInstance()->LoopingPlayout(); event.event_status[0] = AVRCP_PLAY_STATUS_FWD_SEEK; event.event_id = AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED; break; case AVRCP_KEY_BACKWARD: // 上一曲 // TjdUiAppPlayerModel::GetInstance()->PreviousPlayout(); event.event_status[0] = AVRCP_PLAY_STATUS_REV_SEEK; event.event_id = AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED; break; case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED: /* 手机连接手表场景,控制手表喇叭声音 */ event.event_status[0] = keyValue; event.event_id = AVRCP_NOTIFY_EVENT_VOLUME_CHANGED; BtAvrcpTgNotifyVolumeChangedProc((uint8_t)keyValue); default: break; } BtAvrcpTgNotifyMeidaStatus(&event); } uint8_t BtAvrcpTgGetLocMediaVolume(void) { if (g_avrcp_tg_local_media_vol == AVRCP_ABSOLUTE_VOLUME_INVALID) { int32_t audio_volume = AudioManager::GetInstance().GetVolume(AUDIO_STREAM_A2DP_MUSIC); uint8_t a2dp_volume = (uint8_t)(audio_volume * AVRCP_ABSOLUTE_VOLUME_PERCENTAGE_100 / BT_A2DP_MEDIA_VOL_LEVEL); static_print_info("audio_volume 0x%x, a2dp_volume 0x%x", audio_volume, a2dp_volume); g_avrcp_tg_local_media_vol = a2dp_volume; } static_print_info("get local media volume, a2dp_volume = 0x%x", g_avrcp_tg_local_media_vol); return g_avrcp_tg_local_media_vol; } void MediaStatusCallback(bt_avrcp_tg_evt_status_param *eventStatusParam) { if (eventStatusParam == nullptr) { static_print_error("eventStatusParam is nullptr"); return; } int32_t playerStatus = TjdUiAppPlayerModel::GetInstance()->GetPlayerStatus(); static_print_debug("playerStatus = %d", playerStatus); switch (eventStatusParam->event_id) { case AVRCP_NOTIFY_EVENT_PLAYBACK_STATUS_CHANGED: eventStatusParam->event_status[0] = playerStatus; 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: eventStatusParam->event_status[0] = 0xFF; break; case AVRCP_NOTIFY_EVENT_BATT_STATUS_CHANGED: case AVRCP_NOTIFY_EVENT_SYSTEM_STATUS_CHANGED: case AVRCP_NOTIFY_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: break; 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: eventStatusParam->event_status[0] = BtAvrcpTgGetLocMediaVolume(); break; case AVRCP_NOTIFY_EVENT_RESERVED: break; default: break; } } void ConnCtStateChangedCallback(const bd_addr_t *bdAddr, profile_connect_state_t state) { static_print_debug("avrcp ct update conn stat:%u", state); if (bdAddr == nullptr) { static_print_error("bdaddr nullptr"); return; } static_print_debug("ConnCtStateChangedCallback 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 */ (void)memcpy_s(g_avrcp_srv_inf.bd_addr.addr, BD_ADDR_LEN, bdAddr->addr, BD_ADDR_LEN); g_avrcp_srv_inf.avrcp_srv_conn_stat = state; } static bd_addr_t *BtAvrcpSrvGetActiveAddr(void) { return &g_avrcp_srv_inf.bd_addr; } void ConnectTgStateChangedCallback(const bd_addr_t *bdAddr, profile_connect_state_t state) { static_print_info("avrcp tg update conn stat:%u", state); if (bdAddr == nullptr) { static_print_error("bdaddr nullptr"); return; } static_print_debug("ConnCtStateChangedCallback 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 */ (void)memcpy_s(g_avrcp_srv_inf.bd_addr.addr, BD_ADDR_LEN, bdAddr->addr, BD_ADDR_LEN); g_avrcp_srv_inf.avrcp_srv_conn_stat = state; /* 手表耳机链接场景时,耳机控制音量是一个事件而非key_operation,故需要在手表侧向耳机注册音量变更事件 */ if (state == PROFILE_STATE_CONNECTED) { (void)avrcp_ct_get_supported_events(BtAvrcpSrvGetActiveAddr()); } } static void BtAvrcpCtPressButtonRspCallback(const bd_addr_t *bd_addr, int32_t key_operation) { if (bd_addr == nullptr) { static_print_error("null pointer"); return; } if (g_avrcp_srv_inf.avrcp_button_flag == AVRCP_SRV_RELEASED_VALUE) { static_print_debug("avrcp ct press button button_flag change:%u->%u", g_avrcp_srv_inf.avrcp_button_flag, AVRCP_SRV_PRESS_VALUE); g_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_avrcp_srv_inf.avrcp_button_flag); } static void BtAvrcpPressReleaseButtonLog(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 BtAvrcpCtReleaseButtonRspCallback(const bd_addr_t *bd_addr, int32_t key_operation) { UNUSED(bd_addr); static_print_debug("avrcp ct release button cbk key:%d", key_operation); if (g_avrcp_srv_inf.avrcp_button_flag == AVRCP_SRV_PRESS_VALUE) { static_print_debug("avrcp ct released button button_flag change:%u->%u", g_avrcp_srv_inf.avrcp_button_flag, AVRCP_SRV_RELEASED_VALUE); g_avrcp_srv_inf.avrcp_button_flag = AVRCP_SRV_RELEASED_VALUE; } BtAvrcpPressReleaseButtonLog((uint32_t)key_operation); } static void BtAvrcpCtSupportCompanyCallback(const bd_addr_t *bd_addr, avrcp_ct_support_company_para_t *company) { if (bd_addr == nullptr || company == nullptr || company->company_id == nullptr) { static_print_error("null pointer"); return; } static_print_debug("get company list:"); for (uint8_t i = 0; i < company->company_num; i++) { static_print_debug(" 0x%x ", *company->company_id); } } static void BtAvrcpCtSupportEventCallback(const bd_addr_t *bd_addr, avrcp_ct_support_event_para_t *event) { if (bd_addr == nullptr || event == nullptr || event->event_id == nullptr) { static_print_debug("null pointer"); return; } for (uint8_t i = 0; i < event->event_num; i++) { if (event->event_id[i] == AVRCP_NOTIFY_EVENT_VOLUME_CHANGED) { int ret = avrcp_ct_register_notification(bd_addr, event->event_id[i], 0); static_print_info("register event_id: 0x%x, ret = 0x%x", event->event_id[i], ret); } } for (uint8_t i = 0; i < event->event_num; i++) { if (event->event_id[i] != AVRCP_NOTIFY_EVENT_VOLUME_CHANGED) { int ret = avrcp_ct_register_notification(bd_addr, event->event_id[i], 0); static_print_info("register event_id: 0x%x, ret = 0x%x", event->event_id[i], ret); } } } static void BtAvrcpCtGetElementAttriCallback(const bd_addr_t *bd_addr, avrcp_ct_element_attr_para_cb_t *attr_cb_para) { if (bd_addr == nullptr || attr_cb_para == nullptr || attr_cb_para->attr_value == nullptr) { static_print_error("null pointer"); return; } static_print_debug("attr_cb_para->attr_num = %u", attr_cb_para->attr_num); 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:"); 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:"); 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; } uint8_t *value = (uint8_t *)(void *)malloc(attr_cb_para->attr_value[i].value_len + 1); if (attr_cb_para->attr_value[i].value_len > 0 && attr_cb_para->attr_value[i].value != nullptr) { 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) { free(value); static_print_error("memcpy_s fail."); return; } } value[attr_cb_para->attr_value[i].value_len] = '\0'; static_print_debug(" %s ", value); free(value); } } static void BtAvrcpCtGetPlayStatusCallback(const bd_addr_t *bd_addr, avrcp_ct_play_status_cb_t *status) { if (bd_addr == nullptr || status == nullptr) { static_print_error("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); } static void BtAvrcpCtHdlNotifyVolumeRspCbk(avrcp_ct_notification_value_cb_t *notification) { static_print_info("Set local volume: 0x%x", notification->volume); /* 注册事件后通知的音量 */ BtAvrcpTgNotifyVolumeChangedProc((uint8_t)notification->volume); } static void BtAvrcpCtNotificationCallback(const bd_addr_t *bd_addr, avrcp_notify_event_t event_id, avrcp_ct_notification_value_cb_t *notification) { if (bd_addr == nullptr || notification == nullptr) { static_print_error("null pointer"); return; } // static_print_debug("ct notification cbk event_id:0x%x", event_id); 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); // TODO:手机音乐状态改变 TjdUiAppPlayerModel::GetInstance()->AvrcpCtStatusNotification(notification->play_status); // 播放状态改变 break; case AVRCP_NOTIFY_EVENT_VOLUME_CHANGED: BtAvrcpCtHdlNotifyVolumeRspCbk(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 = false; // static_print_info("invalid event id event_id:0x%x", event_id); break; } if (notify_flag) { static_print_debug("notify reg event_id: 0x%x", event_id); avrcp_ct_register_notification(bd_addr, event_id, 0); /* TG通知变化后CT重新注册事件 */ } } void TjdUiAppPlayerModel::InitBtHeadsetEvent() { g_avrcp_tg_audio_cb.notify_pass_through_status_cbk = PressButtonStatusCallback; g_avrcp_tg_audio_cb.get_media_status_cbk = MediaStatusCallback; g_avrcp_tg_cb.conn_state_changed_cb = ConnectTgStateChangedCallback; g_avrcp_ct_cb.conn_state_changed_cb = ConnCtStateChangedCallback; g_avrcp_ct_cb.press_button_cb = BtAvrcpCtPressButtonRspCallback; g_avrcp_ct_cb.release_button_cb = BtAvrcpCtReleaseButtonRspCallback; g_avrcp_ct_cb.supported_companies_cb = BtAvrcpCtSupportCompanyCallback; g_avrcp_ct_cb.supported_event_cb = BtAvrcpCtSupportEventCallback; g_avrcp_ct_cb.element_attr_cb = BtAvrcpCtGetElementAttriCallback; g_avrcp_ct_cb.play_status_cb = BtAvrcpCtGetPlayStatusCallback; g_avrcp_ct_cb.notification_cb = BtAvrcpCtNotificationCallback; avrcp_tg_register_callbacks(&g_avrcp_tg_cb); avrcp_ct_register_callbacks(&g_avrcp_ct_cb); bt_avrcp_tg_register_audio_cbk(&g_avrcp_tg_audio_cb); } } // namespace TJD