增加播放超时时间,上报速度可通过参数调整
authorhcj <1224667075@qq.com>
Mon, 29 Sep 2025 10:26:45 +0000 (18:26 +0800)
committerhcj <1224667075@qq.com>
Mon, 29 Sep 2025 10:26:45 +0000 (18:26 +0800)
code/sdk/zhihui/ai/include/ai_media.h
code/sdk/zhihui/ai/media/ai_media_service_ext.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c

index d070e43..89eb326 100644 (file)
@@ -110,6 +110,25 @@ int32_t old_Start_AIVoice(AIVoiceDataCallback dataCallback, void* userData,
 int32_t AI_StartOggEncapsulation(void);
 int32_t AI_EndOggEncapsulation(void);
 
+/*
+ * @brief 音频数据写入回调函数类型
+ * @param size 音频数据大小
+ */
+typedef void (*AI_WriteAudioDataCallback)(uint32_t size);
+
+/*
+ * @brief 注册音频数据写入回调
+ * @param callback 回调函数指针
+ * @return 0 成功,-1 失败
+ */
+int32_t AI_RegisterWriteAudioDataCallback(AI_WriteAudioDataCallback callback);
+
+/*
+ * @brief 取消注册音频数据写入回调
+ * @return 0 成功,-1 失败
+ */
+int32_t AI_UnregisterWriteAudioDataCallback(void);
+
 /*
  * @brief 写入音频数据到输出流
  * @param data 音频数据指针
index 03fa4e3..9a80ffe 100644 (file)
@@ -136,6 +136,9 @@ static const size_t AI_AUDIO_CHUNK_SIZE = 1024;  // 固定音频块大小:1KB
 static MediaMutexHandle g_fileCacheMutex = nullptr;
 static long g_fileCacheOffset = 0;  // 文件读取偏移量
 
+// 音频数据写入回调相关变量
+static AI_WriteAudioDataCallback g_writeAudioDataCallback = nullptr;
+
 // OGG相关内存管理函数
 static void MediaMemFree(void **src)
 {
@@ -178,6 +181,7 @@ static void AI_InitFileCacheMutex()
     }
 }
 
+
 /**
  * @brief 将音频数据写入文件缓存
  * @param msg 音频消息
@@ -1057,7 +1061,7 @@ static int32_t RunAIVoice(AIVoiceContext *context)
                 if (msg.data != nullptr && msg.size > 0) {
                     // 写入音频数据到输出流
                     AI_WriteAudioData(msg.data,msg.size);
-                    // struct timespec ts;
+                                        // struct timespec ts;
                     // AudioStreamGetTimestamp(&ts);
                     // printf("AudioStreamGetTimestamp %d\r\n",ts.tv_sec);
                     // 释放消息数据内存
@@ -1563,6 +1567,36 @@ static void ExampleAIVoiceDataCallback(const uint8_t* data, uint32_t size, void*
 // uint32_t queueSize, queueCapacity;
 // AI_GetQueueStatus(&queueSize, &queueCapacity);
 
+/**
+ * @brief 注册音频数据写入回调
+ * @param callback 回调函数指针
+ * @return 0 成功,-1 失败
+ */
+int32_t AI_RegisterWriteAudioDataCallback(AI_WriteAudioDataCallback callback)
+{
+    if (callback == nullptr) {
+        MEDIA_ERR_LOG("Invalid callback parameter");
+        return -1;
+    }
+    
+    g_writeAudioDataCallback = callback;
+    
+    MEDIA_INFO_LOG("Audio data write callback registered successfully");
+    return 0;
+}
+
+/**
+ * @brief 取消注册音频数据写入回调
+ * @return 0 成功,-1 失败
+ */
+int32_t AI_UnregisterWriteAudioDataCallback(void)
+{
+    g_writeAudioDataCallback = nullptr;
+    
+    MEDIA_INFO_LOG("Audio data write callback unregistered successfully");
+    return 0;
+}
+
 /**
  * @brief 写入音频数据到输出流
  * @param data 音频数据指针
@@ -1618,6 +1652,11 @@ int32_t AI_WriteAudioData(const uint8_t* data, uint32_t size)
         goto AI_WRITE;
     }
     
+    // 调用注册的回调函数
+    if (g_writeAudioDataCallback != nullptr) {
+        g_writeAudioDataCallback(size);
+    }
+    
     // MEDIA_INFO_LOG("Successfully wrote %u bytes of audio data to output stream", size);
     return 0;
 }
index 66aade4..f02f436 100644 (file)
@@ -704,7 +704,7 @@ static uint32_t ln_ws_start_at(uint8_t *para, uint32_t paraLen, char *strCommonC
     VAD_UNUSED(paraLen); 
     VAD_UNUSED(strCommonCmd);
     printf("[LN][WS_START]\n");
-    int ret = ln_ws_start(NULL);
+    int ret = ln_ws_start(NULL,2000);
     if (ret != 0)
     {
         printf("[LN][WS_START] failed: %d\n", ret);
index f6df7e5..7ba7c2a 100644 (file)
@@ -1329,7 +1329,7 @@ errcode_t uapi_ln_http_server_init(void)
         printf("create ln_send_service failed %d!\r\n", ret);
         return ERRCODE_FAIL;
     }
-    ln_ws_start(ln_ws_process_message);
+    ln_ws_start(ln_ws_process_message,2000);
 
     ln_init_required_dirs();
     ln_load_init_config();
index 0036dfb..7f352c1 100644 (file)
@@ -314,6 +314,7 @@ typedef enum {
     WS_CMD_RANDOM_STORY_RESPONSE,    /* 随机故事响应 (RandomStoryResponse) */
     WS_CMD_CONNECTED,                /* WebSocket连接成功 */
     WS_CMD_DISCONNECTED,             /* WebSocket断连 */
+    WS_CMD_AUDIO_PLAYBACK_END,       /* 音频播放结束 */
     WS_CMD_MAX                       /* 最大命令 */
 } ws_command_t;
 
@@ -347,7 +348,7 @@ typedef enum {
 } ln_audio_chat_command_t;
 
 /* WebSocket接口函数 */
-int ln_ws_start(ws_message_callback_t_ln callback);
+int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks);
 int ln_ws_send_text(const char *text);
 int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool finished);
 int ln_ws_send_random_story_request(const char *story_theme);
index dab6592..cd14dba 100644 (file)
@@ -9,6 +9,7 @@
 #include "soc_osal.h"
 #include "cmsis_os2.h"
 #include "mbedtls/base64.h"
+#include "ai_media.h" // Added for AI_RegisterWriteAudioDataCallback
 
 /*
  * - g_ws_client_fd    当前 WebSocket 客户端fd
@@ -69,6 +70,42 @@ static void ln_ws_process_base64_data(const char *base64_data, size_t length);
 static void ln_ws_process_incremental_base64_data(const char *new_data, size_t new_length);
 static void ln_ws_incomplete_callback(const char *raw_data, size_t length, bool isBinary);
 
+
+uint16_t send_audio=0;
+
+// 音频聊天相关结构体
+typedef struct {
+    char chat_id[64];           // 聊天ID
+    uint32_t message_counter;   // 消息计数器
+    bool is_active;             // 是否活跃
+} audio_chat_context_t;
+
+// 全局音频聊天上下文
+static audio_chat_context_t g_audio_chat_ctx = {0};
+
+// 音频聊天结束标志
+static bool g_audio_chat_ending = false;
+
+
+// 当前对话匹配标志
+static bool g_chat_id_matched = false;    // 当前对话ID是否匹配
+
+// 音频播放计时相关变量
+static uint32_t g_last_audio_write_tick = 0;  // 最后一次音频写入的tick
+static bool g_audio_playback_active = false;  // 音频播放是否活跃
+static uint32_t g_audio_timeout_ticks = 1500; // 音频超时tick数(默认1.5秒)
+
+// 函数声明
+static void generate_chat_id(char *chat_id, size_t size);
+static void generate_message_id(char *message_id, size_t size, const char *chat_id, uint32_t counter);
+static void reset_chat_id_match(void);
+static void set_chat_id_matched(void);
+static bool is_chat_id_matched(void);
+static bool is_active_chat_response(const char *chat_id, const char *response_id);
+static void ln_ws_audio_write_callback(uint32_t size);
+
+
+
 /* 握手成功回调:标记连接就绪 */
 static void ln_ws_on_open(int client)
 {
@@ -180,6 +217,26 @@ static int ln_ws_heartbeat_callback(void)
     // 每30秒发送一次心跳
     static int heartbeat_counter = 0;
     heartbeat_counter++;
+    
+    // 检查音频播放结束
+    if (g_audio_playback_active) {
+        uint32_t current_tick = osKernelGetTickCount();
+        uint32_t elapsed_ticks = current_tick - g_last_audio_write_tick;
+        
+        printf("[LN][WS] Heartbeat callback: audio playback active, elapsed ticks: %u\n", elapsed_ticks);
+        
+        // 如果超过超时时间没有音频写入,认为播放结束
+        if (elapsed_ticks >= g_audio_timeout_ticks) {
+            printf("[LN][WS] Audio playback timeout detected, triggering end event\n");
+            g_audio_playback_active = false;
+            
+            // 触发播放结束事件
+            if (g_ws_message_callback != NULL) {
+                g_ws_message_callback(NULL, WS_CMD_AUDIO_PLAYBACK_END);
+            }
+        }
+    }
+    
     if (heartbeat_counter >= 15) { // 30秒 = 30次 * 1秒
         heartbeat_counter = 0;
         ln_ws_send_ping();
@@ -235,34 +292,6 @@ int ln_ws_send_ping(void)
 }
 
 
-
-uint16_t send_audio=0;
-
-// 音频聊天相关结构体
-typedef struct {
-    char chat_id[64];           // 聊天ID
-    uint32_t message_counter;   // 消息计数器
-    bool is_active;             // 是否活跃
-} audio_chat_context_t;
-
-// 全局音频聊天上下文
-static audio_chat_context_t g_audio_chat_ctx = {0};
-
-// 音频聊天结束标志
-static bool g_audio_chat_ending = false;
-
-
-// 当前对话匹配标志
-static bool g_chat_id_matched = false;    // 当前对话ID是否匹配
-
-// 函数声明
-static void generate_chat_id(char *chat_id, size_t size);
-static void generate_message_id(char *message_id, size_t size, const char *chat_id, uint32_t counter);
-static void reset_chat_id_match(void);
-static void set_chat_id_matched(void);
-static bool is_chat_id_matched(void);
-static bool is_active_chat_response(const char *chat_id, const char *response_id);
-
 // 生成聊天ID
 static void generate_chat_id(char *chat_id, size_t size)
 {
@@ -317,6 +346,16 @@ static bool is_active_chat_response(const char *chat_id, const char *response_id
     return is_chat_id_matched();
 }
 
+// 音频数据写入回调函数
+static void ln_ws_audio_write_callback(uint32_t size)
+{
+    printf("[LN][WS] Audio data written successfully: %u bytes\n", size);
+    
+    // 更新最后一次音频写入tick
+    g_last_audio_write_tick = osKernelGetTickCount();
+    g_audio_playback_active = true;
+}
+
 // 发送音频数据到WebSocket
 int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool finished)
 {
@@ -662,8 +701,9 @@ static void ln_ws_incomplete_callback(const char *raw_data, size_t length, bool
  * 启动:开启监督线程与心跳机制(心跳线程首次连接成功后会自然启动)
  * - 若已在运行则直接返回成功
  * - callback: 消息处理回调函数,可以为NULL使用默认处理
+ * - audio_timeout_ticks: 音频播放超时tick数,0表示使用默认值
  */
-int ln_ws_start(ws_message_callback_t_ln callback)
+int ln_ws_start(ws_message_callback_t_ln callback, uint32_t audio_timeout_ticks)
 {
     if (g_ws_running) {
         return 0;
@@ -672,6 +712,14 @@ int ln_ws_start(ws_message_callback_t_ln callback)
     g_ws_reconnect_count = 0; /* 启动时从0开始 */
     g_ws_connected = WS_STATE_DISCONNECTED; /* 启动时重置连接状态 */
     
+    // 设置音频超时tick数
+    if (audio_timeout_ticks > 0) {
+        g_audio_timeout_ticks = audio_timeout_ticks;
+        printf("[LN][WS] Audio timeout ticks set to: %u\n", g_audio_timeout_ticks);
+    } else {
+        printf("[LN][WS] Using default audio timeout ticks: %u\n", g_audio_timeout_ticks);
+    }
+    
     // 注册回调函数
     g_ws_message_callback = callback;
     if (callback != NULL) {
@@ -697,6 +745,9 @@ int ln_ws_start(ws_message_callback_t_ln callback)
     
     old_Start_AIVoice(AIVoiceDataCallback_ln,NULL,NULL,NULL,false);
 
+    // 注册音频数据写入回调
+    AI_RegisterWriteAudioDataCallback(ln_ws_audio_write_callback);
+
     cat1_api_keep_wakeup(1);
     cat1_api_wakeup_cat1(1);
 
@@ -858,6 +909,10 @@ int ln_ws_send_voice_chat_interrupt(const char *reason)
 int ln_ws_stop(void)
 {
     g_ws_running = 0;
+    
+    // 取消注册音频数据写入回调
+    AI_UnregisterWriteAudioDataCallback();
+    
     int ret = uapi_ws_stop();
     g_ws_connected = WS_STATE_DISCONNECTED;
     
@@ -1274,6 +1329,9 @@ static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_comm
         case WS_CMD_DISCONNECTED:
             printf("[LN][WS] Default: Processing WebSocket Disconnected event\n");
             break;
+        case WS_CMD_AUDIO_PLAYBACK_END:
+            printf("[LN][WS] Default: Processing Audio Playback End event\n");
+            break;
         case WS_CMD_PONG:
             if (data_msg) {
             printf("[LN][WS] Default: Processing Pong message\n");