From 8d47b426da8b72a696e969d1d681ca1c8b479608 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=83=A1=E4=BC=A0=E5=86=9B?= <1224667075@qq.com> Date: Wed, 3 Dec 2025 07:28:23 +0000 Subject: [PATCH] Merge #2844 into sdk650pathc0423 from refs/changes/2844/head MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Code review from 胡传军 * refs/changes/2844/head: (15 commits squashed) - 屏蔽VAD认证 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - 增加文件保存测试开关 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - 修改ai宏开关不生效的问题 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - cat1升级启动换位置 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - vad参数调整 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 - 修改ln 蓝牙耳机相关 - Merge branch 'sdk650pathc0423' of https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230 into sdk650pathc0423 Signed-off-by: aliyun8003852482 <1224667075@qq.com> Reviewed-by: snickersun@163.com Merged-by: snickersun@163.com CR-link: https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230/change/2844 --- .../nativeui/bluetooth/src/BluetoothModel.cpp | 16 ++ code/sdk/zhihui/ai/include/ai_media.h | 25 +++ .../zhihui/ai/media/ai_media_service_ext.cpp | 158 +++++++++++++++++- .../zhihui/leiniao/imsl_ln/ln_api_websocket.c | 48 +++--- code/sdk/zhihui/utils/custom_cmd_service.c | 45 +++++ code/sdk/zhihui/utils/custom_utils.h | 7 + 6 files changed, 274 insertions(+), 25 deletions(-) diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/bluetooth/src/BluetoothModel.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/bluetooth/src/BluetoothModel.cpp index 514c98b7f..c34c3e35e 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/bluetooth/src/BluetoothModel.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/bluetooth/src/BluetoothModel.cpp @@ -46,6 +46,8 @@ #include "thunderbird/include/ThunderBirdMainView.h" #include "thunderbird/include/ThunderBirdBlueView.h" #include +#include "ai_media.h" + namespace OHOS { #define BT_SPP_TASK_STACK_SIZE 2048 @@ -1163,6 +1165,12 @@ static void A2dpSrcConnectstateChanged(bd_addr_t *bdAddr, int connState) if (connState == PROFILE_STATE_CONNECTED) { uint32_t ret = avrcp_ct_connect(bdAddr); WEARABLE_LOGI(WEARABLE_LOG_MODULE_APP, "avrcp_ct_connect ret = 0x%x", ret); + + if(AI_IsVoiceServiceRunning()){ + ln_ws_audio_deinit(); + } + + set_a2dp_is_connect(true); PhoneAudioManagerSetDeviceConnState(OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_CONNECTED); GraphicService::GetInstance()->PostGraphicEvent(std::bind(ACLConnectionSuss, *bdAddr)); int earphoneConnectStatus = 1; @@ -1170,7 +1178,15 @@ static void A2dpSrcConnectstateChanged(bd_addr_t *bdAddr, int connState) } if (connState == PROFILE_STATE_DISCONNECTED) { + if(AI_IsVoiceServiceRunning()){ + ln_ws_audio_deinit(); + } + + PhoneAudioManagerSetDeviceConnState(OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_DISCONNECTED); + set_a2dp_is_connect(false); + + GraphicService::GetInstance()->PostGraphicEvent(std::bind(ACLConnectionFail, *bdAddr)); int earphoneConnectStatus = 0; SendMsgToUIService(HAND_BLUETOOTH_EARPHONE_CONNECT_STATUS_NOTIFY, 0, &earphoneConnectStatus, sizeof(earphoneConnectStatus)); diff --git a/code/sdk/zhihui/ai/include/ai_media.h b/code/sdk/zhihui/ai/include/ai_media.h index cb4f7b50a..f1d069e6c 100644 --- a/code/sdk/zhihui/ai/include/ai_media.h +++ b/code/sdk/zhihui/ai/include/ai_media.h @@ -102,6 +102,7 @@ typedef struct { } AIVoiceAudioParams; + int32_t old_Start_AIVoice(AIVoiceDataCallback dataCallback, void* userData, const AIVoiceAudioParams* inputAudioParams, const AIVoiceAudioParams* outputAudioParams, @@ -196,6 +197,24 @@ int32_t AI_GetSecondStreamInParams(AIVoiceAudioParams* audioParams); */ bool AI_IsSecondStreamInEnabled(void); +/* + * @brief 开启第二个音频上行流(初始化并启动) + * @return 0 成功,-1 失败 + */ +int32_t AI_StartSecondStreamIn(void); + +/* + * @brief 关闭第二个音频上行流(停止并反初始化) + * @return 0 成功,-1 失败 + */ +int32_t AI_StopSecondStreamIn(void); + +/* + * @brief 获取第二个音频上行流状态 + * @return true 正在运行,false 未运行或未启用 + */ +bool AI_GetSecondStreamInStatus(void); + /* * @brief 从第二个音频上行流获取音频数据缓冲区 * @param data 音频数据缓冲区指针 @@ -204,6 +223,12 @@ bool AI_IsSecondStreamInEnabled(void); */ int32_t AI_SecondStreamInObtainBuffer(uint8_t *data, uint32_t *size); +/* + * @brief 设置连接状态 + * @param state 设置状态 + */ +void set_a2dp_is_connect(bool state); + #ifdef __cplusplus }; #endif diff --git a/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp b/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp index 14de8a835..cec5bd91e 100644 --- a/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp +++ b/code/sdk/zhihui/ai/media/ai_media_service_ext.cpp @@ -882,6 +882,68 @@ static int32_t DeinitSecondAudioStreamInSafe(void) return ret; } +/** + * @brief 初始化第二个音频上行流(线程安全版本,使用全局上下文) + * @return 0 成功,-1 失败 + */ +static int32_t InitSecondAudioStreamInSafe(void) +{ + int32_t ret = 0; + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr) { + MEDIA_ERR_LOG("AI voice context is null, please start AI voice first"); + MediaMutexUnLock(mutex); + return -1; + } + + if (!g_aiVoiceContext->useSecondStreamIn) { + MEDIA_ERR_LOG("Second audio stream in is not enabled"); + MediaMutexUnLock(mutex); + return -1; + } + + ret = InitSecondAudioStreamIn(g_aiVoiceContext); + if (ret != 0) { + MEDIA_ERR_LOG("second audio stream in init failed"); + } + + MediaMutexUnLock(mutex); + return ret; +} + +/** + * @brief 启动第二个音频上行流(线程安全版本,使用全局上下文) + * @return 0 成功,-1 失败 + */ +static int32_t StartSecondAudioStreamInSafe(void) +{ + int32_t ret = 0; + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + return -1; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext == nullptr) { + MEDIA_ERR_LOG("AI voice context is null, please start AI voice first"); + MediaMutexUnLock(mutex); + return -1; + } + + ret = StartSecondAudioStreamIn(g_aiVoiceContext); + if (ret != 0) { + MEDIA_ERR_LOG("second audio stream in start failed"); + } + + MediaMutexUnLock(mutex); + return ret; +} + // 注意:g_aiVoiceContext 已在上方定义,这里不需要重复定义 @@ -999,6 +1061,12 @@ static bool InitAIVoiceResources(AIVoiceContext *context) context->bufferSize = AI_VOICE_BUFFER_SIZE; return true; } +bool a2dp_is_connect=false; + +void set_a2dp_is_connect(bool state) +{ + a2dp_is_connect=state; +} static int32_t LocalAIVoiceStart(AIVoiceContext *context) { @@ -1047,7 +1115,7 @@ static int32_t LocalAIVoiceStart(AIVoiceContext *context) } // 初始化第二个上行流(如果启用) - if (context->useSecondStreamIn) { + if (context->useSecondStreamIn && a2dp_is_connect==false) { ret = InitSecondAudioStreamIn(context); if (ret != 0) { MEDIA_ERR_LOG("Failed to init second audio stream in"); @@ -2296,6 +2364,90 @@ bool AI_IsSecondStreamInEnabled(void) return enabled; } +/** + * @brief 开启第二个音频上行流(初始化并启动) + * @return 0 成功,-1 失败 + */ +int32_t AI_StartSecondStreamIn(void) +{ + // 检查音频主服务是否已开启 + MEDIA_ERR_LOG("AI_StartSecondStreamIn"); + + if (!AI_IsVoiceServiceRunning()) { + MEDIA_ERR_LOG("Audio main service is not running, please start AI voice service first"); + return -1; + } + + // 先初始化 + int32_t ret = InitSecondAudioStreamInSafe(); + if (ret != 0) { + MEDIA_ERR_LOG("Failed to init second audio stream in"); + return -1; + } + + // 再启动 + ret = StartSecondAudioStreamInSafe(); + if (ret != 0) { + MEDIA_ERR_LOG("Failed to start second audio stream in"); + // 如果启动失败,尝试清理已初始化的资源 + DeinitSecondAudioStreamInSafe(); + return -1; + } + + MEDIA_INFO_LOG("Second audio stream in started successfully"); + return 0; +} + +/** + * @brief 关闭第二个音频上行流(停止并反初始化) + * @return 0 成功,-1 失败 + */ +int32_t AI_StopSecondStreamIn(void) +{ + int32_t ret = 0; + + // 先停止 + ret = StopSecondAudioStreamInSafe(); + if (ret != 0) { + MEDIA_ERR_LOG("Failed to stop second audio stream in"); + // 继续执行反初始化,即使停止失败 + } + + // 再反初始化 + ret = DeinitSecondAudioStreamInSafe(); + if (ret != 0) { + MEDIA_ERR_LOG("Failed to deinit second audio stream in"); + return -1; + } + + MEDIA_INFO_LOG("Second audio stream in stopped successfully"); + return 0; +} + +/** + * @brief 获取第二个音频上行流状态 + * @return true 正在运行,false 未运行或未启用 + */ +bool AI_GetSecondStreamInStatus(void) +{ + bool isRunning = false; + + MediaMutexHandle mutex = GetGlobalMutex(); + if (mutex == nullptr) { + return false; + } + + MediaMutexLock(mutex); + if (g_aiVoiceContext != nullptr && + g_aiVoiceContext->useSecondStreamIn && + g_aiVoiceContext->secondStreamIn != nullptr) { + isRunning = true; + } + MediaMutexUnLock(mutex); + + return isRunning; +} + /** * @brief 从第二个音频上行流获取音频数据缓冲区 * @param data 音频数据缓冲区指针 @@ -2346,6 +2498,10 @@ int32_t AI_SecondStreamInObtainBuffer(uint8_t *data, uint32_t *size) return 0; } + + + + #ifdef __cplusplus }; #endif diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c index 83fbc2ca5..7aa5620e9 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -794,25 +794,11 @@ static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* use #if ENABLE_VAD_SERVER if (g_vad_server_enabled && g_vad_server_initialized && vad_server_is_running()) { - #ifdef VAD_LOCAL_PM - vad_audio_format_t format = VAD_AUDIO_FORMAT_MP3; // 默认 MP3 + + if(AI_GetSecondStreamInStatus()==false){ + vad_audio_format_t format = VAD_AUDIO_FORMAT_MP3; // 默认 MP3 -errcode_t ret = vad_server_send_audio(format, data, size, false); - - #else - vad_audio_format_t format = VAD_AUDIO_FORMAT_PCM; // 默认 MP3 - - if (data && size > 0) { - - uint8_t *pcm_data=(uint8_t *) malloc(1024*3); - uint32_t len=1024*3; - - errcode_t ret= AI_SecondStreamInObtainBuffer(pcm_data,&len); - - if(ret==0 && len>0) - { - ret = vad_server_send_audio(format, pcm_data, len, false); -// AI_SendAudioDataToQueue(pcm_data, len); + errcode_t ret = vad_server_send_audio(format, data, size, false); if (ret != ERRCODE_SUCC) { /* 静默失败,避免过多日志 */ @@ -820,13 +806,27 @@ errcode_t ret = vad_server_send_audio(format, data, size, false); // LN_WS_DEBUG("VAD: Sent %u bytes (format=%d, is_last=%d)\n", // size, format, false); } - } - free(pcm_data); - + else{ + vad_audio_format_t format = VAD_AUDIO_FORMAT_PCM; // 默认 MP3 + + uint8_t *pcm_data=(uint8_t *) malloc(1024*3); + uint32_t len=1024*3; + + errcode_t ret= AI_SecondStreamInObtainBuffer(pcm_data,&len); + + if(ret==0 && len>0) + { + ret = vad_server_send_audio(format, pcm_data, len, false); + if (ret != ERRCODE_SUCC) { + /* 静默失败,避免过多日志 */ + } else { + // LN_WS_DEBUG("VAD: Sent %u bytes (format=%d, is_last=%d)\n", + // size, format, false); + } + } + free(pcm_data); } - #endif - // 如果是结束标志,打印日志 if (false) { LN_WS_INFO("VAD: Audio recording ended\n"); @@ -1122,7 +1122,7 @@ int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks) void vad_status_callback_ln_ws(int status) { LN_WS_INFO("Audio vad_status_callback_ln_ws %d\n",status); - + // 检查VAD服务器是否被禁用(例如在哄睡模式下) if (!ln_ws_is_vad_server_enabled()) { LN_WS_INFO("VAD server is disabled, ignoring callback (status=%d)\n", status); diff --git a/code/sdk/zhihui/utils/custom_cmd_service.c b/code/sdk/zhihui/utils/custom_cmd_service.c index 7d80b233b..ae10dfc0e 100644 --- a/code/sdk/zhihui/utils/custom_cmd_service.c +++ b/code/sdk/zhihui/utils/custom_cmd_service.c @@ -37,6 +37,7 @@ #ifdef SUPPORT_AI_SERVICE #include "websocket_server_manager.h" +#include "ai_media.h" #endif #ifdef FD230_650 @@ -484,6 +485,34 @@ case CUSTOM_CMD_MSG_START_AI_ASR: PRINT("VAD auth via custom cmd\n"); ln_ws_vad_auth(); break; + case CUSTOM_CMD_MSG_START_SECOND_STREAM_IN: +#ifdef SUPPORT_AI_SERVICE + { + int32_t ret = AI_StartSecondStreamIn(); + if (ret == 0) { + PRINT("Second audio stream in started via custom cmd\n"); + } else { + PRINT("Failed to start second audio stream in via custom cmd, ret=%d\n", ret); + } + } +#else + PRINT("AI service not supported\n"); +#endif + break; + case CUSTOM_CMD_MSG_STOP_SECOND_STREAM_IN: +#ifdef SUPPORT_AI_SERVICE + { + int32_t ret = AI_StopSecondStreamIn(); + if (ret == 0) { + PRINT("Second audio stream in stopped via custom cmd\n"); + } else { + PRINT("Failed to stop second audio stream in via custom cmd, ret=%d\n", ret); + } + } +#else + PRINT("AI service not supported\n"); +#endif + break; #endif break; case CUSTOM_CMD_MSG_ROTARY_OPEN: @@ -616,5 +645,21 @@ void uapi_set_chat_ai_type(uint8_t ai_type) { set_chat_ai_type(ai_type); } + +/** + * @brief 启动第二个音频上行流 + */ +void uapi_start_second_stream_in(void) +{ + do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_START_SECOND_STREAM_IN, NULL, 0); +} + +/** + * @brief 停止第二个音频上行流 + */ +void uapi_stop_second_stream_in(void) +{ + do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_STOP_SECOND_STREAM_IN, NULL, 0); +} #endif #endif \ No newline at end of file diff --git a/code/sdk/zhihui/utils/custom_utils.h b/code/sdk/zhihui/utils/custom_utils.h index d204fc147..a868243c2 100644 --- a/code/sdk/zhihui/utils/custom_utils.h +++ b/code/sdk/zhihui/utils/custom_utils.h @@ -265,6 +265,11 @@ uint32_t uapi_calendar_store_date(void); #define CUSTOM_CMD_MSG_ROTARY_OPEN 0x29 +#define CUSTOM_CMD_MSG_START_SECOND_STREAM_IN 0x30 + +#define CUSTOM_CMD_MSG_STOP_SECOND_STREAM_IN 0x31 + + typedef enum { BR_HFP_CONNECT=1, BR_HFP_DISCONNECT, @@ -282,6 +287,8 @@ errcode_t do_custom_cmd_service_check_msg_head(int cmd, void *params, int paraml #ifdef SUPPORT_AI_SERVICE void uapi_start_AI_asr(void); void uapi_stop_AI_asr(void); +void uapi_start_second_stream_in(void); +void uapi_stop_second_stream_in(void); #endif #ifdef SUPPORT_AI_SERVICE -- 2.17.1