delete chargeBatteryicon;
chargeBatteryicon = nullptr;
}
- if (listenIcon != nullptr)
- {
- delete listenIcon;
- listenIcon = nullptr;
+ if (listenIcon != nullptr)
+ {
+ delete listenIcon;
+ listenIcon = nullptr;
+ }
+
+ // 最后删除group1
+ delete group1;
+ group1 = nullptr;
+
+ // 清理最后一帧视图
+ if (lastFrameView_ != nullptr)
+ {
+ // 先从父组件中移除
+ if (ScrollView_ != nullptr) {
+ ScrollView_->Remove(lastFrameView_);
}
-
- // 最后删除group1
- delete group1;
- group1 = nullptr;
+ delete lastFrameView_;
+ lastFrameView_ = nullptr;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::DeInit: Destroyed lastFrameView_");
}
+ }
// 清理其他成员变量
video_ = nullptr;
backGroundView->SetPosition(0, 0, 360, 360);
backGroundView->SetStyle(STYLE_BACKGROUND_COLOR, 0xff000000);
backGroundView->SetStyle(STYLE_BACKGROUND_OPA, 0xD0);
+
+ // 创建最后一帧视图(只创建一次,不销毁,避免频繁创建销毁)
+ lastFrameView_ = new UIImageView();
+ if (lastFrameView_ != nullptr)
+ {
+ lastFrameView_->SetAutoEnable(false);
+ lastFrameView_->SetResizeMode(UIImageView::COVER);
+ lastFrameView_->SetVisible(false); // 初始状态为隐藏
+ ScrollView_->Add(lastFrameView_);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::OnStart: Created lastFrameView_");
+ }
//backGroundView->SetVisible(false);
ScrollView_->Add(backGroundView);
}
if (g_isSwitchingVideo)
{
- WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::WaitForVideoSwitchComplete: Timeout after %d ms, video switch still in progress", maxWaitMs);
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::WaitForVideoSwitchComplete: Timeout after %d ms, video switch still in progress, force reset flag", maxWaitMs);
+ // 超时后强制重置标志位,避免死锁
+ g_isSwitchingVideo = false;
return false;
}
return true;
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Captured last frame, size: %dx%d",
lastFrameImage.header.width, lastFrameImage.header.height);
- // 创建或更新临时视图显示最后一帧
- if (lastFrameView_ == nullptr)
- {
- lastFrameView_ = new UIImageView();
- if (lastFrameView_ != nullptr)
- {
- lastFrameView_->SetPosition(surfaceRect.GetX(), surfaceRect.GetY(),
- surfaceRect.GetWidth(), surfaceRect.GetHeight());
- lastFrameView_->SetAutoEnable(false);
- lastFrameView_->SetResizeMode(UIImageView::COVER);
- // 将临时视图添加到surfaceView_的父容器中,并放在surfaceView_上方
- if (ScrollView_ != nullptr)
- {
- ScrollView_->Add(lastFrameView_);
- }
- }
- }
-
+ // 更新最后一帧视图的位置和内容(视图已在初始化时创建,只需刷新)
if (lastFrameView_ != nullptr)
{
+ // 更新位置和大小
+ lastFrameView_->SetPosition(surfaceRect.GetX(), surfaceRect.GetY(),
+ surfaceRect.GetWidth(), surfaceRect.GetHeight());
+ // 更新图片内容
lastFrameView_->SetSrc(&lastFrameImage);
+ // 显示视图
lastFrameView_->SetVisible(true);
lastFrameView_->Invalidate();
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Last frame view displayed");
}
+ else
+ {
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: lastFrameView_ is null, cannot display last frame");
+ }
}
else
{
}
}
- // 步骤2:在创建新播放器前,停止并销毁旧播放器
- // 静态图片会遮挡整个过程,所以可以安全地销毁旧播放器
- if (oldPlayer != nullptr)
- {
- // 先停止播放
- if (oldWasEntered || oldWasPlaying)
- {
- oldPlayer->StopVideoPlay();
- }
- // 等待旧播放器完全停止
- usleep(50000); // 50ms,确保旧播放器完全停止
- delete oldPlayer;
- oldPlayer = nullptr;
- videoPlay_ = nullptr; // 清除指针
- g_videoPlay = nullptr;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Old player stopped and destroyed before creating new player");
- }
-
- // 步骤3:创建新播放器
+ // 步骤2:创建新播放器(不销毁旧播放器,保留作为备用)
newPlayer = new MediaVideoPlay(surface, nullptr, targetPath, true);
if (newPlayer == nullptr)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to create MediaVideoPlay");
+ // 创建失败,恢复旧播放器,
+ if (oldPlayer != nullptr && oldWasPlaying)
+ {
+ oldPlayer->ResumeVideoPlay();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player creation failed, resumed old player");
+ if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ lastFrameView_->SetVisible(false);
+ lastFrameView_->Invalidate();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, old player resumed");
+ }
+ }
g_isSwitchingVideo = false;
return false;
}
- // 步骤4:立即启动新播放器
+ // 步骤3:启动新播放器
int32_t ret = newPlayer->StartVideoPlay();
if (ret != 0)
{
delete newPlayer;
newPlayer = nullptr;
- // 如果启动失败,保持lastFrameView可见,避免黑屏
- if (lastFrameView_ != nullptr && lastFrameView_->IsVisible()) {
+ // 启动失败,恢复旧播放器
+ if (oldPlayer != nullptr && oldWasPlaying)
+ {
+ oldPlayer->ResumeVideoPlay();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player start failed, resumed old player");
+ // 隐藏最后一帧视图,因为旧播放器会继续播放
+ if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ lastFrameView_->SetVisible(false);
+ lastFrameView_->Invalidate();
+ }
+ // 释放截图内存(视图已隐藏,不再需要)
+ if (hasLastFrame && lastFrameImage.data != nullptr)
+ {
+ ImageCacheFree(lastFrameImage);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory (start failed)");
+ }
+ }
+ else if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ // 如果没有旧播放器,保持lastFrameView可见,避免黑屏
+ // 此时不释放内存,因为视图还在使用
WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player failed to start, keeping last frame view visible to avoid black screen");
- } else {
- // 如果没有lastFrameView,尝试切换到默认表情,避免黑屏
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player failed to start and no last frame view, attempting to switch to default winking");
- if (ThunderBirdModel::GetInstance()) {
- std::string defaultPath = "/user/res/video/CloseEyes/round-eyes/amber.mp4";
- if (access(defaultPath.c_str(), F_OK) == 0) {
- // 延迟切换,避免递归调用
- GraphicService::GetInstance()->PostGraphicEvent(
- [defaultPath]() {
- if (ThunderBirdMainView::GetInstance()) {
- ThunderBirdMainView::GetInstance()->SwitchVideo(defaultPath.c_str());
- }
- });
- }
+ }
+ else
+ {
+ // 如果没有视图显示,释放内存
+ if (hasLastFrame && lastFrameImage.data != nullptr)
+ {
+ ImageCacheFree(lastFrameImage);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory (no view visible)");
}
}
return false;
}
- // 步骤5:立即切换指针到新播放器
- videoPlay_ = newPlayer;
- g_videoPlay = videoPlay_;
+ // 步骤4:等待并检查新播放器是否正常播放
+ int checkCount = 0;
+ const int maxCheckCount = 50; // 50 * 20ms = 1000ms
+ bool newPlayerPlaying = false;
+ bool newPlayerError = false;
- // 步骤5.5:同步等待新播放器渲染第一帧后再隐藏最后一帧视图
- // 静态图片会一直遮挡,直到新播放器的第一帧真正渲染到屏幕
- // 通过检查Surface的buffer状态来判断第一帧是否已渲染
- if (lastFrameView_ != nullptr && lastFrameView_->IsVisible() && surface != nullptr)
- {
- // 等待新播放器开始播放
- int checkCount = 0;
- const int maxCheckCount = 50; // 50 * 20ms = 1000ms
- bool playerConfirmed = false;
-
- while (checkCount < maxCheckCount)
+ while (checkCount < maxCheckCount)
+ {
+ // 检查新播放器是否真正开始播放
+ if (newPlayer != nullptr && newPlayer->isEntered_ && newPlayer->isPlayed_ && !newPlayer->isPause_)
{
- // 检查新播放器是否真正开始播放
- if (newPlayer != nullptr && newPlayer->isEntered_ && newPlayer->isPlayed_ && !newPlayer->isPause_)
- {
- playerConfirmed = true;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player confirmed playing after %d ms", checkCount * 20);
- break;
- }
- usleep(20000); // 等待20ms
- checkCount++;
+ newPlayerPlaying = true;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player confirmed playing after %d ms", checkCount * 20);
+ break;
}
-
- // 如果新播放器已开始播放,检查Surface是否有新的buffer(表示第一帧已输出)
- bool canHideLastFrame = false; // 是否可以安全隐藏最后一帧视图
- if (playerConfirmed)
- {
- // 检查Surface是否有新的buffer被flush(表示第一帧已经输出到Surface)
- // 通过检查GetBackBuf()来判断是否有新的buffer
- LiteSurface* liteSurface = dynamic_cast<LiteSurface*>(surface);
- if (liteSurface != nullptr)
+ // 检查新播放器是否发生错误
+ if (newPlayer != nullptr && newPlayer->playError_)
+ {
+ newPlayerError = true;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player error detected after %d ms", checkCount * 20);
+ break;
+ }
+ usleep(20000); // 等待20ms
+ checkCount++;
+ }
+
+ // 步骤5:根据新播放器状态决定后续操作
+ if (newPlayerPlaying && !newPlayerError)
+ {
+ // 新播放器正常播放,检查第一帧是否已输出
+ bool firstFrameReady = false;
+ LiteSurface* liteSurface = dynamic_cast<LiteSurface*>(surface);
+ if (liteSurface != nullptr)
+ {
+ int bufferCheckCount = 0;
+ const int maxBufferCheckCount = 30; // 30 * 50ms = 1500ms
+
+ while (bufferCheckCount < maxBufferCheckCount)
{
- int bufferCheckCount = 0;
- const int maxBufferCheckCount = 30; // 30 * 50ms = 1500ms
- bool firstFrameReady = false;
-
- while (bufferCheckCount < maxBufferCheckCount)
- {
- // 检查Surface是否有新的buffer(GetBackBuf返回非null表示有新的buffer)
- SurfaceBuffer* backBuf = liteSurface->GetBackBuf();
- if (backBuf != nullptr)
- {
- // 有新的buffer,说明第一帧已经输出到Surface
- firstFrameReady = true;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer ready after %d ms", bufferCheckCount * 50);
- // 再等待一小段时间确保第一帧已经渲染到屏幕
- usleep(50000); // 50ms,确保第一帧已渲染到屏幕
- break;
- }
- usleep(50000); // 等待50ms
- bufferCheckCount++;
- }
-
- if (firstFrameReady)
+ SurfaceBuffer* backBuf = liteSurface->GetBackBuf();
+ if (backBuf != nullptr)
{
- // 第一帧已就绪,可以安全隐藏最后一帧视图
- canHideLastFrame = true;
+ firstFrameReady = true;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer ready after %d ms", bufferCheckCount * 50);
+ usleep(50000); // 50ms,确保第一帧已渲染到屏幕
+ break;
}
- else
- {
- // 第一帧未就绪,保留最后一帧视图,避免黑屏
- WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer not ready after %d ms, keeping last frame view to avoid black screen", maxBufferCheckCount * 50);
- }
- }
- else
- {
- // 如果不是LiteSurface,使用固定等待时间
- usleep(300000); // 300ms
- // 等待后认为可以安全隐藏最后一帧视图
- canHideLastFrame = true;
+ usleep(50000); // 等待50ms
+ bufferCheckCount++;
}
}
else
{
- // 新播放器未确认播放,保留最后一帧视图,避免黑屏
- WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player not confirmed playing after %d ms, keeping last frame view to avoid black screen", maxCheckCount * 20);
+ // 如果不是LiteSurface,使用固定等待时间
+ usleep(300000); // 300ms
+ firstFrameReady = true;
}
- // 只有在确认新播放器已开始播放且第一帧已就绪时,才隐藏最后一帧视图
- // 这样可以避免黑屏问题
- if (canHideLastFrame && lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ // 新播放器正常播放,停止并销毁旧播放器
+ if (oldPlayer != nullptr)
{
- lastFrameView_->SetVisible(false);
- lastFrameView_->Invalidate();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, new player first frame should be rendered");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player playing successfully, destroying old player");
+ if (oldWasEntered || oldWasPlaying)
+ {
+ oldPlayer->StopVideoPlay();
+ }
+ // 等待旧播放器完全停止
+ usleep(50000); // 50ms,确保旧播放器完全停止
+ delete oldPlayer;
+ oldPlayer = nullptr;
}
- else if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+
+ // 更新指针到新播放器
+ videoPlay_ = newPlayer;
+ g_videoPlay = videoPlay_;
+
+ // 隐藏最后一帧视图
+ if (firstFrameReady && lastFrameView_ != nullptr && lastFrameView_->IsVisible())
{
- // 不能安全隐藏最后一帧视图,保留显示,避免黑屏
- WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Keeping last frame view visible to avoid black screen");
+ usleep(200000); // 200ms
+ lastFrameView_->SetVisible(false);
+ lastFrameView_->Invalidate();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, new player first frame rendered");
}
- // 释放最后一帧截图的内存(在隐藏静态图片后释放)
+ // 更新当前播放的视频路径
+ currentVideoPath_ = targetPath;
+
+ // 释放最后一帧截图的内存
if (hasLastFrame && lastFrameImage.data != nullptr)
{
ImageCacheFree(lastFrameImage);
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory");
}
+
+ // 清除切换视频标志
+ g_isSwitchingVideo = false;
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Successfully switched to video: %s", uri);
+ return true;
}
- else if (hasLastFrame && lastFrameImage.data != nullptr)
+ else
{
- // 如果没有静态图片视图,直接释放截图内存
- ImageCacheFree(lastFrameImage);
+ // 新播放器未正常播放,销毁新播放器并恢复旧播放器
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player not playing correctly, destroying new player and resuming old player");
+
+ // 停止并销毁新播放器
+ if (newPlayer != nullptr)
+ {
+ if (newPlayer->isEntered_)
+ {
+ newPlayer->StopVideoPlay();
+ }
+ usleep(50000); // 50ms,确保新播放器完全停止
+ delete newPlayer;
+ newPlayer = nullptr;
+ }
+
+ // 恢复旧播放器
+ if (oldPlayer != nullptr && oldWasPlaying)
+ {
+ oldPlayer->ResumeVideoPlay();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Resumed old player");
+ // 隐藏最后一帧视图,因为旧播放器会继续播放
+ if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ lastFrameView_->SetVisible(false);
+ lastFrameView_->Invalidate();
+ }
+ // 释放截图内存(视图已隐藏,不再需要)
+ if (hasLastFrame && lastFrameImage.data != nullptr)
+ {
+ ImageCacheFree(lastFrameImage);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory (resumed old player)");
+ }
+ }
+ else if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ // 如果没有旧播放器,保持lastFrameView可见,避免黑屏
+ // 此时不释放内存,因为视图还在使用
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: No old player to resume, keeping last frame view visible");
+ }
+ else
+ {
+ // 如果没有视图显示,释放内存
+ if (hasLastFrame && lastFrameImage.data != nullptr)
+ {
+ ImageCacheFree(lastFrameImage);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory (no view visible)");
+ }
+ }
+
+ // 清除切换视频标志
+ g_isSwitchingVideo = false;
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to switch video, old player resumed: %s", uri);
+ return false;
}
-
- // 更新当前播放的视频路径
- currentVideoPath_ = targetPath;
-
- // 清除切换视频标志
- g_isSwitchingVideo = false;
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Successfully switched to video: %s", uri);
- return true;
}
void ThunderBirdMainView::CloseVideo() // 关闭表情接口
return;
}
}
+
#include "thunderbird/include/ThunderBirdBlueView.h"
#include "thunderbird/include/ThunderBirdPresenter.h"
#include "thunderbird/include/ThunderBirdAdjustView.h"
- #include "bluetooth/BluetoothModel.h"
+ #include "bluetooth/BluetoothModel.h"
#include "wearable_log.h"
#include <sys/time.h>
#include <service/graphic_service.h>
#include "bluetooth/Ln_Bluetooth_apiC.h"
#include <algorithm>
#include <ctime>
+#include <memory>
#include "custom_runtime.h"
#ifdef SUPPORT_BATTERY_SERVICE
#include "battery_hal.h"
view->ChangeView(currentView, THUNDERBIRD_GUIDEVIEW, ThunderBirdGuideView::THUNDERBIRD_GUIDEVIEW_5);
}
ThunderBirdDialogueView::GetInstance()->DialogueFirst = true;
+ // 先设置退出标志,防止录音结束回调误判为正常录音结束
+ ThunderBirdPresenter::GetInstance()->SetDialogueing(false);
ln_ws_end_audio_chat();//结束对话
AI_ForceInterrupt();
ln_ws_send_voice_chat_interrupt("退出对话");
do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_AUDIO_DEINIT, NULL, 0);
ThunderBirdModel::GetInstance()->OnExitDialogueMode();
- ThunderBirdPresenter::GetInstance()->SetDialogueing(false);
}
// 检查是否是网络连接中界面,6秒后自动跳转到状态界面
if (!networkSuccess) {
// 网络仍未连接,检查是否超过60秒
m_networkConnectingTimeoutCount++;
- if (m_networkConnectingTimeoutCount < 10) { // 最多10次,共60秒
+ if (m_networkConnectingTimeoutCount < 5) { // 最多10次,共60秒
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "网络仍未连接,继续等待(%d/10)", m_networkConnectingTimeoutCount);
shouldContinue = true;
// 重新启动6秒定时器
if (!vadAuthSuccess) {
// VAD认证仍未成功,检查是否超过60秒
m_networkConnectingTimeoutCount++;
- if (m_networkConnectingTimeoutCount < 10) { // 最多10次,共60秒
+ if (m_networkConnectingTimeoutCount < 5) { // 最多10次,共60秒
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "VAD认证仍未成功,继续等待(%d/10)", m_networkConnectingTimeoutCount);
shouldContinue = true;
// 重新启动6秒定时器
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpression: switching to %s", expressionPath.c_str());
// 通过图形服务调用 ThunderBirdMainView 的 SwitchVideo 函数
// 注意:PostGraphicEvent 是异步的,这里直接返回 true 表示调用已提交
+ // 使用 shared_ptr 确保字符串生命周期,避免 lambda 执行时对象已销毁
+ auto pathPtr = std::make_shared<std::string>(expressionPath);
GraphicService::GetInstance()->PostGraphicEvent(
- [expressionPath]() {
- if (ThunderBirdMainView::GetInstance()) {
- ThunderBirdMainView::GetInstance()->SwitchVideo(expressionPath.c_str());
+ [pathPtr]() {
+ ThunderBirdMainView* mainView = ThunderBirdMainView::GetInstance();
+ if (mainView != nullptr && pathPtr != nullptr) {
+ mainView->SwitchVideo(pathPtr->c_str());
} else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpression: ThunderBirdMainView instance is null");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpression: ThunderBirdMainView instance is null or path invalid");
}
});
{
WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpressionVideo: Video switch in progress, waiting for completion...");
// 在图形线程中等待,避免阻塞当前线程
+ // 使用 shared_ptr 确保字符串生命周期,避免 lambda 执行时对象已销毁
+ auto pathPtr = std::make_shared<std::string>(normalizedPath);
GraphicService::GetInstance()->PostGraphicEvent(
- [normalizedPath]() {
+ [pathPtr]() {
// 等待之前的切换完成
- if (ThunderBirdMainView::GetInstance()) {
+ ThunderBirdMainView* mainView = ThunderBirdMainView::GetInstance();
+ if (mainView != nullptr) {
ThunderBirdMainView::WaitForVideoSwitchComplete(2000);
// 再次检查是否还在切换(防止超时后仍在切换)
if (!ThunderBirdMainView::IsSwitchingVideo()) {
- ThunderBirdMainView::GetInstance()->SwitchVideo(normalizedPath.c_str());
+ // 再次检查对象有效性,防止在执行过程中被销毁
+ ThunderBirdMainView* view = ThunderBirdMainView::GetInstance();
+ if (view != nullptr && pathPtr != nullptr) {
+ view->SwitchVideo(pathPtr->c_str());
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpressionVideo: Object invalidated during execution");
+ }
} else {
WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpressionVideo: Video switch still in progress after wait, skipping this switch");
}
} else {
// 通过图形服务调用 ThunderBirdMainView 的 SwitchVideo 函数
// 注意:PostGraphicEvent 是异步的,这里直接返回 true 表示调用已提交
+ // 使用 shared_ptr 确保字符串生命周期,避免 lambda 执行时对象已销毁
+ auto pathPtr = std::make_shared<std::string>(normalizedPath);
GraphicService::GetInstance()->PostGraphicEvent(
- [normalizedPath]() {
- if (ThunderBirdMainView::GetInstance()) {
- ThunderBirdMainView::GetInstance()->SwitchVideo(normalizedPath.c_str());
+ [pathPtr]() {
+ ThunderBirdMainView* mainView = ThunderBirdMainView::GetInstance();
+ if (mainView != nullptr && pathPtr != nullptr) {
+ mainView->SwitchVideo(pathPtr->c_str());
} else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpressionVideo: ThunderBirdMainView instance is null");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchExpressionVideo: ThunderBirdMainView instance is null or path invalid");
}
});
}
// return ;//需要vad的时候在处理
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleAudioRecordingEnd: 录音结束");
+ // 检查是否正在退出对话模式,如果是则不应该跳转到播放界面
+ ThunderBirdPresenter* presenter = ThunderBirdPresenter::GetInstance();
+ if (presenter != nullptr && !presenter->isDialogueing) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleAudioRecordingEnd: 正在退出对话模式,忽略录音结束事件");
+ return;
+ }
+
// 取消退出对话的定时器(用户结束录音,说明要继续对话)
StopCountUpTimer();
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleAudioRecordingEnd: 取消退出对话定时器");
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnEnterDialogueMode: 记录时间线表情: %s", m_currentExpression.c_str());
// 重置对话模式finished标志
m_dialogueModeFinished = false;
+ // 设置标志位,表示在对话模式中
+ m_needGetTimelineExpression = true;
// 等待视频切换完成,避免在视频切换过程中切换表情导致冲突
if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::IsSwitchingVideo()) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 开始退出对话模式");
// 隐藏小绿点
- if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::GetInstance()->listenIcon) {
- ThunderBirdMainView::GetInstance()->listenIcon->SetVisible(false);
+ ThunderBirdMainView* mainView = ThunderBirdMainView::GetInstance();
+ if (mainView != nullptr && mainView->listenIcon != nullptr) {
+ mainView->listenIcon->SetVisible(false);
}
// 重置对话模式finished标志
m_dialogueModeFinished = false;
- // 等待视频切换完成,避免在视频切换过程中恢复表情导致冲突
- if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::IsSwitchingVideo()) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 等待视频切换完成");
- ThunderBirdMainView::WaitForVideoSwitchComplete(2000);
- }
-
- // 恢复到进入对话模式前记录的时间线表情
- if (!m_currentExpression.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 恢复时间线表情: %s", m_dialogueExpression.c_str());
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 当前表情: %s", m_currentExpression.c_str());
- //如果相同直接返回
- if(m_dialogueExpression == m_currentExpression)
- {
- return;
- }
- // 恢复表情状态
- // 构建表情文件路径并切换表情
- std::string expressionPath;
- if (screenImageAsEmoji_ == false) {
- // 不为屏保模式,根据角色配置构建表情路径
- if (eyesColor_.empty() || eyesShape_.empty() ||
- (eyesShape_ == "calm-eyes" && eyesColor_ == "amber")) {
- // 默认表情
- std::string emotionFileName = MapEmotionToFileName(m_currentExpression.c_str());
- expressionPath = "/user/res/video/Default/" + emotionFileName + ".mp4";
- } else {
- // 非默认表情
- if (m_currentExpression == "winking") {
- // 眨眼表情
- expressionPath = "/user/res/video/CloseEyes/" + eyesShape_ + "/" + eyesColor_ + ".mp4";
- } else if (m_currentExpression == "sleeping") {
- // 睡眠表情
- expressionPath = "/user/res/video/Default/sleeping.mp4";
- } else {
- // 其他表情
- expressionPath = "/user/userdata/leiniao/emoji/" + eyesShape_ + "/" + eyesColor_ + "/" + m_currentExpression + ".mp4";
- }
- }
-
- // 检查文件是否存在并切换表情
- if (access(expressionPath.c_str(), F_OK) == 0) {
- SwitchExpressionVideo(expressionPath.c_str());
- HandleHairIconChange(m_currentExpression);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 恢复表情成功: %s", expressionPath.c_str());
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 表情文件不存在: %s", expressionPath.c_str());
- }
- }
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 没有记录的表情,无法恢复");
- }
+ // 不在这里切换表情,标志位保持为1,在发呆中界面时获取时间线表情
+ // m_needGetTimelineExpression 保持为 true,表示需要获取时间线表情
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 退出对话模式,标志位保持为true,将在发呆中界面获取时间线表情");
}
// 进入哄睡模式时记录当前表情(进入THUNDERBIRD_LULLABYVIEW_4时调用)
// 记录进入哄睡模式前的时间线表情(用于退出时恢复)
m_lullabyExpression = "winking";
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnEnterLullabyMode: 记录时间线表情: %s", m_currentExpression.c_str());
+ // 设置标志位,表示在哄睡模式中
+ m_needGetTimelineExpression = true;
//如果本来就是眨眼的表情直接返回
if (m_currentExpression == "winking" || m_currentExpression.empty() == true) {
return;
if (ThunderBirdMainView::GetInstance() && ThunderBirdMainView::GetInstance()->listenIcon) {
ThunderBirdMainView::GetInstance()->listenIcon->SetVisible(false);
}
- // 恢复到进入哄睡模式前记录的时间线表情
- if (!m_lullabyExpression.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitLullabyMode: 恢复时间线表情: %s", m_lullabyExpression.c_str());
- //如果相同直接返回
- if(m_currentExpression.empty() == true ||m_currentExpression == "winking")
- {
- return;
- }
- // 恢复表情状态
- // 构建表情文件路径并切换表情
- std::string expressionPath;
- if (screenImageAsEmoji_ == false) {
- // 不为屏保模式,根据角色配置构建表情路径
- if (eyesColor_.empty() || eyesShape_.empty() ||
- (eyesShape_ == "calm-eyes" && eyesColor_ == "amber")) {
- // 默认表情
- std::string emotionFileName = MapEmotionToFileName(m_currentExpression.c_str());
- expressionPath = "/user/res/video/Default/" + emotionFileName + ".mp4";
- } else {
- // 非默认表情
- if (m_currentExpression == "winking") {
- // 眨眼表情
- expressionPath = "/user/res/video/CloseEyes/" + eyesShape_ + "/" + eyesColor_ + ".mp4";
- } else if (m_currentExpression == "sleeping") {
- // 睡眠表情
- expressionPath = "/user/res/video/Default/sleeping.mp4";
- } else {
- // 其他表情
- expressionPath = "/user/userdata/leiniao/emoji/" + eyesShape_ + "/" + eyesColor_ + "/" + m_currentExpression + ".mp4";
- }
- }
-
- // 检查文件是否存在并切换表情
- if (access(expressionPath.c_str(), F_OK) == 0) {
- SwitchExpressionVideo(expressionPath.c_str());
- HandleHairIconChange(m_currentExpression);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitLullabyMode: 恢复表情成功: %s", expressionPath.c_str());
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitLullabyMode: 表情文件不存在: %s", expressionPath.c_str());
- }
- }
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitLullabyMode: 没有记录的表情,无法恢复");
- }
+
+ // 不在这里切换表情,标志位保持为1,在发呆中界面时获取时间线表情
+ // m_needGetTimelineExpression 保持为 true,表示需要获取时间线表情
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitLullabyMode: 退出哄睡模式,标志位保持为true,将在发呆中界面获取时间线表情");
}
// 设置当前表情状态