return true; // 返回true表示"成功",因为不需要切换
}
- // 如果正在切换视频,等待完成(最多等待2秒)
- if (g_isSwitchingVideo)
- {
- WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Video switch in progress, waiting for completion...");
- if (!WaitForVideoSwitchComplete(2000))
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Previous video switch did not complete in time, proceeding anyway");
- }
- }
-
// 设置切换视频标志,防止屏幕状态通知暂停播放
g_isSwitchingVideo = true;
int32_t ret = newPlayer->StartVideoPlay();
if (ret != 0)
{
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to start video play, ret=%d", ret);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to start video play, ret=%d, fallback to winking", ret);
delete newPlayer;
g_isSwitchingVideo = false;
+ // 视频播放失败,切换到 wink 表情(检查 GetInstance 是否为空)
+ ThunderBirdModel* model = ThunderBirdModel::GetInstance();
+ if (model != nullptr) {
+ model->SwitchToWinkingExpression();
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: ThunderBirdModel::GetInstance() is null, cannot switch to winking");
+ }
return false;
}
// 通过检查Surface的buffer状态来判断第一帧是否已渲染
if (lastFrameView_ != nullptr && lastFrameView_->IsVisible() && surface != nullptr)
{
- // 等待新播放器开始播放
+ // 等待新播放器开始播放(增加等待时间,因为 Prepare 可能需要更长时间)
int checkCount = 0;
- const int maxCheckCount = 50; // 50 * 20ms = 1000ms
+ const int maxCheckCount = 100; // 100 * 20ms = 2000ms(增加等待时间,因为 Prepare 可能需要更长时间)
bool playerConfirmed = false;
+ bool prepareFailed = false;
while (checkCount < maxCheckCount)
{
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player confirmed playing after %d ms", checkCount * 20);
break;
}
+
+ // 检查是否 Prepare 失败(通过检查 playError_ 标志)
+ // 如果 isEntered_ 为 true 但长时间后 isPlayed_ 仍为 false,且 playError_ 为 true,说明 Prepare 失败
+ if (newPlayer != nullptr && newPlayer->isEntered_ && !newPlayer->isPlayed_ &&
+ checkCount > 20) // 等待至少 400ms 后再检查
+ {
+ // 检查播放错误标志(Prepare 失败时会设置 playError_)
+ if (newPlayer->playError_)
+ {
+ prepareFailed = true;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Prepare failed detected (playError_=true) after %d ms", checkCount * 20);
+ break;
+ }
+ // 如果线程已退出但 isPlayed_ 仍为 false,也说明 Prepare 失败
+ if (newPlayer->isPlaybackStopped_ || newPlayer->isExited_)
+ {
+ prepareFailed = true;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Prepare failed detected (isPlaybackStopped_=%d, isExited_=%d) after %d ms",
+ newPlayer->isPlaybackStopped_, newPlayer->isExited_, checkCount * 20);
+ break;
+ }
+ }
+
usleep(20000); // 等待20ms
checkCount++;
}
+ // 如果超时后仍未播放,或检测到 Prepare 失败,说明视频文件可能有问题,切换到 wink 表情
+ if (!playerConfirmed || prepareFailed)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Video play failed (timeout=%d, prepareFailed=%d) after %d ms, video file may be incomplete (missing moov box), fallback to winking",
+ !playerConfirmed, prepareFailed, checkCount * 20);
+ // 停止并删除失败的播放器
+ if (newPlayer != nullptr)
+ {
+ newPlayer->StopVideoPlay();
+ delete newPlayer;
+ newPlayer = nullptr;
+ videoPlay_ = nullptr;
+ g_videoPlay = nullptr;
+ }
+ g_isSwitchingVideo = false;
+ // 切换到 wink 表情(检查 GetInstance 是否为空)
+ ThunderBirdModel* model = ThunderBirdModel::GetInstance();
+ if (model != nullptr) {
+ model->SwitchToWinkingExpression();
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: ThunderBirdModel::GetInstance() is null, cannot switch to winking");
+ }
+ return false;
+ }
+
// 如果新播放器已开始播放,检查Surface是否有新的buffer(表示第一帧已输出)
if (playerConfirmed)
{
// 静态图片会一直遮挡,直到这里才隐藏
if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
{
- GraphicService::GetInstance()->PostGraphicEvent([this]() {
- lastFrameView_->SetVisible(false);
- lastFrameView_->Invalidate();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, new player first frame should be rendered");
- });
+ lastFrameView_->SetVisible(false);
+ lastFrameView_->Invalidate();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, new player first frame should be rendered");
}
// 释放最后一帧截图的内存(在隐藏静态图片后释放)
if (hasLastFrame && lastFrameImage.data != nullptr)
{
+ if (lastFrameView_ != nullptr)
+ {
+ lastFrameView_->SetSrc(static_cast<const ImageInfo*>(nullptr));
+ }
ImageCacheFree(lastFrameImage);
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory");
}
return true; // 异步调用,返回 true 表示调用已提交
}
+
+ // 切换到 wink 表情(用于视频切换失败时的回退)
+ void ThunderBirdModel::SwitchToWinkingExpression()
+ {
+ // 检查 eyesShape_ 和 eyesColor_ 是否已初始化
+ if (eyesShape_.empty() || eyesColor_.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchToWinkingExpression: eyesShape_ or eyesColor_ is empty, cannot switch to winking");
+ return;
+ }
+
+ std::string winkingExpressionPath = "/user/res/video/CloseEyes/" + eyesShape_ + "/" + eyesColor_ + ".mp4";
+
+ // 检查文件是否存在
+ if (access(winkingExpressionPath.c_str(), F_OK) == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchToWinkingExpression: fallback to winking expression: %s", winkingExpressionPath.c_str());
+ SwitchExpressionVideo(winkingExpressionPath.c_str());
+ // 更新当前表情为 winking
+ m_currentExpression = "winking";
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::SwitchToWinkingExpression: winking expression file does not exist: %s", winkingExpressionPath.c_str());
+ }
+ }
+
//切换发型
bool ThunderBirdModel::SwitchHair(const char* hairShape, const char* hairColor)
{
int16_t currentView = ThunderBirdMainView::GetInstance()->GetCurrentView();
int16_t currentSubView = ThunderBirdMainView::GetInstance()->GetCurrentSubView();
int screenState = uapi_get_screen_state();
- int originalScreenState = screenState; // 保存原始屏幕状态,用于判断是否息屏
// 按键事件日志
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "OnKeyAct: keyID=%d, keyState=%d, screenState=%d", keyID, keyState, screenState);
// 息屏后,只有电源键操作有效,其他操作都直接忽略
bool isPowerKey = (keyID == static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER));
- if (originalScreenState != 0 && !isPowerKey) {
+ if (screenState == 1 && !isPowerKey) {
// 息屏状态且不是电源键,直接忽略
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "OnKeyAct: screen is off and key is not power key, ignore keyID=%d", keyID);
return true;
}
// 刷新息屏计时:任意按键按下时重置屏幕熄灭倒计时(只有电源键或已亮屏时才刷新)
- if (keyState == InputDevice::STATE_PRESS && (isPowerKey || originalScreenState == 0)) {
+ if (keyState == InputDevice::STATE_PRESS && (isPowerKey || screenState == 0)) {
uapi_set_screen_on_api();
- // 修复:在调用uapi_set_screen_on_api()后重新获取屏幕状态,确保使用最新的状态
+ // 在调用uapi_set_screen_on_api()后重新获取屏幕状态,确保使用最新的状态
screenState = uapi_get_screen_state();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OnKeyAct: After uapi_set_screen_on_api(), screenState=%d (original=%d)", screenState, originalScreenState);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OnKeyAct: After uapi_set_screen_on_api(), screenState=%d", screenState);
}
// 分发按键事件到对应的处理函数
switch (keyID) {
case static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_POWER):
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "HandlePowerKey: KEY_TYPE_POWER");
- // ä¿®å¤\8dï¼\9a对äº\8eç\94µæº\90é\94®ï¼\8cä¼ é\80\92å\8e\9få§\8bå±\8få¹\95ç\8a¶æ\80\81ï¼\8c以便æ£ç¡®å\88¤æ\96æ\98¯å\90¦ä»\8eæ\81¯å±\8fç\8a¶æ\80\81亮å±\8f
- return HandlePowerKey(keyState, currentView, currentSubView, originalScreenState);
+ // 使ç\94¨æ\9b´æ\96°å\90\8eç\9a\84å±\8få¹\95ç\8a¶æ\80\81
+ return HandlePowerKey(keyState, currentView, currentSubView, screenState);
case static_cast<uint16_t>(ZliteKeyCode::ZLITE_KEY_FUNC):
// 非电源键需要亮屏才能处理(已在上面检查过,这里确保亮屏)