From 906b50dc64d7bda58c15d658dddbc1ae381608ab Mon Sep 17 00:00:00 2001 From: "snickersun@163.com" Date: Sat, 17 Jan 2026 04:29:17 +0000 Subject: [PATCH] Merge #3102 into ln from refs/changes/3102/head MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Code review from snickersun@163.com * refs/changes/3102/head: (2 commits squashed) - 避免暂停时video申请buffer,造成黑脸 - 退出对话时保证资源销毁后重连websocket。减少死机风险。 Signed-off-by: snickersun@163.com Reviewed-by: snickersun@163.com Merged-by: snickersun@163.com CR-link: https://codeup.aliyun.com/64191456b9581d62e8f81ac0/FD230/change/3102 --- .../player_control/player/liteplayer.cpp | 11 +++ .../zhihui/leiniao/imsl_ln/ln_api_websocket.c | 83 +++++++++++++++---- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/code/sdk/middleware/services/media/foundation/player/frameworks/player_control/player/liteplayer.cpp b/code/sdk/middleware/services/media/foundation/player/frameworks/player_control/player/liteplayer.cpp index a914912e7..e0dcdba72 100644 --- a/code/sdk/middleware/services/media/foundation/player/frameworks/player_control/player/liteplayer.cpp +++ b/code/sdk/middleware/services/media/foundation/player/frameworks/player_control/player/liteplayer.cpp @@ -1316,6 +1316,12 @@ int32_t PlayerControl::QueueOutputBufferToVideoDecoder(void) CHECK_NULL_RETURN(sinkManager_, HI_FAILURE, "sink manager is null"); CHECK_NULL_RETURN(videoDecoder_, HI_FAILURE, "video decoder is null"); + // 0. 如果播放器已暂停,跳过请求缓冲区,避免屏幕关闭时连续失败 + if (paused_) { + MEDIA_DEBUG_LOG("player is paused, skip requesting surface buffer"); + return HI_SUCCESS; + } + // 1. if video sink have cache, means have remain buffer not send to surface, don't need request buffer from surface bool haveCache = sinkManager_->HaveCacheFrame(false); if (haveCache) { @@ -1330,6 +1336,11 @@ int32_t PlayerControl::QueueOutputBufferToVideoDecoder(void) surfaceBuf_ = LvRequestBuffer(reinterpret_cast(surface_), 0); #endif if (surfaceBuf_ == nullptr) { + // 如果播放器已暂停,不增加失败计数,直接返回成功 + if (paused_) { + MEDIA_DEBUG_LOG("surface buffer request failed but player is paused, skip error count"); + return HI_SUCCESS; + } if (continousRequestSurfaceBufferFailCount_ == MAX_CONTINOUS_REQUEST_SURFACE_BUFFER_FAIL_COUNT) { PalayControlEventItem item = { EVNET_VIDEO_RUNNING_ERR }; eventQueue_.push_back(item); 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 1a98f6f38..e9e9071bd 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -1851,6 +1851,9 @@ int ln_ws_send_voice_chat_interrupt(const char *reason) return -1; } + // 检查是否是退出对话场景 + bool is_exit_dialogue = (strcmp(reason, "退出对话") == 0); + // 检查是否需要在1秒内有数据时触发重连 bool need_reconnect = ln_ws_check_binary_data_interrupt(); @@ -1899,24 +1902,64 @@ int ln_ws_send_voice_chat_interrupt(const char *reason) LN_WS_DEBUG("Sending VoiceChatInterrupt: %s\n", json_string); // 发送消息 + int ret = uapi_ws_send_text(json_string); + if (ret == 0) { + LN_WS_DEBUG("VoiceChatInterrupt sent successfully\n"); + } // 清理资源 - int ret=0; - if (need_reconnect || (g_audio_chat_ctx.finish_state == LN_CHAT_SENT_END_NO_REPLY)) - { - ret = uapi_ws_send_text(json_string); - if (ret == 0) { - LN_WS_DEBUG("VoiceChatInterrupt sent successfully\n"); - } - // 如果检测到频繁数据接收,直接重连 - LN_WS_INFO("Triggering reconnection due to frequent binary data reception\n"); - if (ln_ws_reconnect() != 0) { - LN_WS_ERROR("Failed to reconnect after frequent data reception\n"); + // 如果需要重连 + if (need_reconnect || (g_audio_chat_ctx.finish_state == LN_CHAT_SENT_END_NO_REPLY)) { + if (is_exit_dialogue) { + // 只有退出对话场景,才等待资源清理完成后再重连 + // 资源清理(AUDIO_DEINIT)通过消息队列异步执行,需要等待清理完成 + LN_WS_INFO("Exit dialogue mode, waiting for resource cleanup before reconnection\n"); + + // 轮询检查资源清理完成标志,最多等待500ms + const uint32_t max_wait_ms = 500; + const uint32_t check_interval_ms = 20; // 每20ms检查一次 + uint32_t waited_ms = 0; + bool cleanup_complete = false; + + while (waited_ms < max_wait_ms) { + // 检查音频是否已反初始化(资源清理完成的标志) + if (!ln_ws_is_audio_initialized()) { + cleanup_complete = true; + LN_WS_INFO("Resource cleanup completed after %u ms\n", waited_ms); + break; + } + LOS_Msleep(check_interval_ms); + waited_ms += check_interval_ms; + } + + if (!cleanup_complete) { + LN_WS_WARN("Resource cleanup timeout after %u ms, proceeding with reconnection anyway\n", max_wait_ms); + } + + // 再次检查连接状态,确保在等待期间连接状态未改变 + if (g_ws_connected == WS_STATE_CONNECTED) { + LN_WS_INFO("Triggering reconnection after exit dialogue (resource cleanup %s)\n", + cleanup_complete ? "completed" : "timeout"); + if (ln_ws_reconnect() != 0) { + LN_WS_ERROR("Failed to reconnect after exit dialogue\n"); + } + } else { + LN_WS_WARN("Connection state changed during wait, skip reconnection: %d\n", g_ws_connected); + } + } else { + // 非退出对话场景(如二进制数据间隔检查触发的重连),直接重连 + // WebSocket重连是网络层操作,与音频资源无关 + if (g_ws_connected == WS_STATE_CONNECTED) { + LN_WS_INFO("Triggering reconnection %s\n", + need_reconnect ? "due to binary data interrupt" : "due to chat finish state"); + if (ln_ws_reconnect() != 0) { + LN_WS_ERROR("Failed to reconnect\n"); + } + } else { + LN_WS_WARN("Connection state changed, skip reconnection: %d\n", g_ws_connected); + } } } - else { - LN_WS_INFO("no to send VoiceChatInterrupt\n"); - } cJSON_free(json_string); cJSON_Delete(json); @@ -2409,6 +2452,16 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms if (data_msg->command == WS_CMD_RANDOM_STORY_RESPONSE) { cJSON *data_obj = cJSON_GetObjectItem(root, "data"); if (data_obj && cJSON_IsObject(data_obj)) { + // 根据协议,先检查errorCode字段(在data对象中,不在data.data中) + cJSON *error_code = cJSON_GetObjectItem(data_obj, "errorCode"); + if (error_code && !cJSON_IsNull(error_code)) { + // 如果errorCode不为null,表示发生了错误 + // 根据协议,错误响应中会包含data字段,其中包含错误信息和音频URL + LN_WS_WARN("RandomStoryResponse error: errorCode=%s\n", + cJSON_IsString(error_code) ? error_code->valuestring : "unknown"); + // 错误响应中仍然会包含data字段,继续解析以便播放错误音频 + } + // 解析finished字段(先解析到data_msg,但不更新全局上下文) cJSON *finished = cJSON_GetObjectItem(data_obj, "finished"); if (finished && cJSON_IsBool(finished)) { @@ -2437,7 +2490,7 @@ static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_ms } } - // 解析data.data字段 + // 解析data.data字段(根据协议,即使有错误,data字段也可能存在,包含错误信息和音频URL) cJSON *data_data = cJSON_GetObjectItem(data_obj, "data"); if (data_data && cJSON_IsObject(data_data)) { // 解析storyTheme字段,存储到text_content中 -- 2.17.1