#include "custom_runtime.h"
#include <service/graphic_service.h>
#include "ln_api.h"
+#include "common/screen.h"
+#include "common/lite_surface.h"
namespace OHOS
{
static constexpr int16_t VIDEO_WIDTH = 360;
return false;
}
+ std::string targetPath = std::string(uri);
+
// 检查是否尝试切换到同一个文件
- if (currentVideoPath_ == std::string(uri))
+ if (currentVideoPath_ == targetPath)
{
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Already playing the same video, skipping: %s", uri);
return true; // 返回true表示"成功",因为不需要切换
}
}
- // 停止并销毁旧播放器
- if (videoPlay_ != nullptr)
+ Surface *surface = surfaceView_->GetSurface();
+ if (surface == nullptr)
{
- if (videoPlay_->isPlayed_)
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to get surface from surfaceView_");
+ g_isSwitchingVideo = false;
+ return false;
+ }
+
+ MediaVideoPlay *oldPlayer = videoPlay_;
+ bool oldWasPlaying = (oldPlayer != nullptr && oldPlayer->isPlayed_);
+ bool oldWasEntered = (oldPlayer != nullptr && oldPlayer->isEntered_);
+ MediaVideoPlay *newPlayer = nullptr;
+ ImageInfo lastFrameImage = {{0}};
+ bool hasLastFrame = false;
+
+ // 步骤1:暂停旧播放器,保留最后一帧在屏幕上(如果正在播放)
+ if (oldPlayer != nullptr && oldWasPlaying)
+ {
+ oldPlayer->PauseVideoPlay(); // 暂停保留最后一帧,减少黑屏
+ }
+
+ // 步骤1.5:截取最后一帧并显示,避免黑屏
+ if (oldPlayer != nullptr && oldWasPlaying && surfaceView_ != nullptr)
+ {
+ // 等待一小段时间确保暂停后的最后一帧已经显示
+ usleep(10000); // 10ms
+
+ // 获取surfaceView_的位置和大小
+ Rect surfaceRect;
+ surfaceRect.SetX(surfaceView_->GetX());
+ surfaceRect.SetY(surfaceView_->GetY());
+ surfaceRect.SetWidth(surfaceView_->GetWidth());
+ surfaceRect.SetHeight(surfaceView_->GetHeight());
+
+ // 截取surfaceView_区域的屏幕
+ if (Screen::GetInstance().GetScreenBitmapByRect(lastFrameImage, &surfaceRect))
{
- videoPlay_->PauseVideoPlay();
+ hasLastFrame = 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_->SetSrc(&lastFrameImage);
+ lastFrameView_->SetVisible(true);
+ lastFrameView_->Invalidate();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Last frame view displayed");
+ }
}
- if (videoPlay_->isEntered_)
+ else
{
- videoPlay_->StopVideoPlay();
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to capture last frame");
}
- delete videoPlay_;
- videoPlay_ = nullptr;
}
- Surface *surface = surfaceView_->GetSurface();
- if (surface == nullptr)
+ // 步骤2:在创建新播放器前,停止并销毁旧播放器
+ // 静态图片会遮挡整个过程,所以可以安全地销毁旧播放器
+ if (oldPlayer != nullptr)
{
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to get surface from surfaceView_");
- g_isSwitchingVideo = false; // 清除标志
- return false;
+ // 先停止播放
+ 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");
}
- // å\88\9b建æ\96°æ\92æ\94¾å\99¨å¹¶å\90¯å\8a¨
- videoPlay_ = new MediaVideoPlay(surface, nullptr, std::string(uri), true);
- if (videoPlay_ == nullptr)
+ // æ¥éª¤3ï¼\9aå\88\9b建æ\96°æ\92æ\94¾å\99¨
+ newPlayer = new MediaVideoPlay(surface, nullptr, targetPath, true);
+ if (newPlayer == nullptr)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to create MediaVideoPlay");
- g_isSwitchingVideo = false; // 清除标志
+ g_isSwitchingVideo = false;
return false;
}
- g_videoPlay = videoPlay_;
- int32_t ret = videoPlay_->StartVideoPlay();
+
+ // 步骤4:立即启动新播放器
+ int32_t ret = newPlayer->StartVideoPlay();
if (ret != 0)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to start video play, ret=%d", ret);
- g_isSwitchingVideo = false; // 清除标志
+ delete newPlayer;
+ g_isSwitchingVideo = false;
return false;
}
+
+ // 步骤5:立即切换指针到新播放器
+ videoPlay_ = newPlayer;
+ g_videoPlay = videoPlay_;
+
+ // 步骤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)
+ {
+ // 检查新播放器是否真正开始播放
+ 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++;
+ }
+
+ // 如果新播放器已开始播放,检查Surface是否有新的buffer(表示第一帧已输出)
+ if (playerConfirmed)
+ {
+ // 检查Surface是否有新的buffer被flush(表示第一帧已经输出到Surface)
+ // 通过检查GetBackBuf()来判断是否有新的buffer
+ LiteSurface* liteSurface = dynamic_cast<LiteSurface*>(surface);
+ if (liteSurface != nullptr)
+ {
+ 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)
+ {
+ WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer not ready after %d ms, proceeding anyway", maxBufferCheckCount * 50);
+ }
+ }
+ else
+ {
+ // 如果不是LiteSurface,使用固定等待时间
+ usleep(300000); // 300ms
+ }
+ }
+
+ // 隐藏最后一帧视图(此时新播放器的第一帧应该已经渲染到屏幕)
+ // 静态图片会一直遮挡,直到这里才隐藏
+ if (lastFrameView_ != nullptr && lastFrameView_->IsVisible())
+ {
+ 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)
+ {
+ ImageCacheFree(lastFrameImage);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Released last frame image memory");
+ }
+ }
+ else if (hasLastFrame && lastFrameImage.data != nullptr)
+ {
+ // 如果没有静态图片视图,直接释放截图内存
+ ImageCacheFree(lastFrameImage);
+ }
// 更新当前播放的视频路径
- currentVideoPath_ = std::string(uri);
+ currentVideoPath_ = targetPath;
// 清除切换视频标志
g_isSwitchingVideo = false;
delete videoPlay_;
videoPlay_ = nullptr;
}
+
+ // 清理最后一帧视图
+ if (lastFrameView_ != nullptr)
+ {
+ if (ScrollView_ != nullptr)
+ {
+ ScrollView_->Remove(lastFrameView_);
+ }
+ delete lastFrameView_;
+ lastFrameView_ = nullptr;
+ }
+
g_videoPlay = nullptr;
g_lastPlayed = false;
// 注销屏幕状态通知
curFace = nullptr;
}
}
-
-
+
// OTA界面已独立为slice,不再需要此函数
bool ThunderBirdMainView::ChangeThunderBirdGuideView(int16_t thisView) // 切换引导界面接口