From 86c15f6b71955b8d80ed5c41642a2635ed8bfde0 Mon Sep 17 00:00:00 2001 From: xue <2144439972@qq.com> Date: Fri, 26 Dec 2025 19:27:13 +0800 Subject: [PATCH] =?utf8?q?=E5=9B=9E=E9=80=80=E8=A1=A8=E6=83=85=E5=88=87?= =?utf8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../thunderbird/src/ThunderBirdMainView.cpp | 328 ++++++------------ 1 file changed, 112 insertions(+), 216 deletions(-) diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdMainView.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdMainView.cpp index 3775b5c29..49c5df61b 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdMainView.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdMainView.cpp @@ -672,24 +672,6 @@ namespace OHOS MediaVideoPlay *newPlayer = nullptr; ImageInfo lastFrameImage = {{0}}; bool hasLastFrame = false; - - // 如果上一次切换时保留了最后一帧视图(因为新播放器未就绪),需要先隐藏它 - // 这样才能截取到真正的视频播放器的最后一帧,而不是静态图片 - // 但只有在有正在播放的视频时才需要隐藏,如果没有播放器,应该保留它作为备用显示 - bool wasLastFrameVisible = (lastFrameView_ != nullptr && lastFrameView_->IsVisible()); - if (wasLastFrameVisible && oldPlayer != nullptr && oldWasPlaying) - { - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hiding previous last frame view before capturing new frame"); - lastFrameView_->SetVisible(false); - lastFrameView_->Invalidate(); - // 等待一小段时间确保视图已隐藏 - usleep(20000); // 20ms - } - else if (wasLastFrameVisible && (oldPlayer == nullptr || !oldWasPlaying)) - { - // 如果没有正在播放的视频,保留最后一帧视图作为备用显示 - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: No playing video, keeping last frame view as backup"); - } // 步骤1:暂停旧播放器,保留最后一帧在屏幕上(如果正在播放) if (oldPlayer != nullptr && oldWasPlaying) @@ -717,261 +699,175 @@ namespace OHOS 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"); - hasLastFrame = false; - } } else { - // 截图失败,直接切换视频(不显示最后一帧) - WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to capture last frame, switching video directly"); - hasLastFrame = false; + WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Failed to capture last frame"); } } - // 步骤2:创建新播放器(不销毁旧播放器,保留作为备用) + // 步骤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:创建新播放器 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; } - // 步骤3:启动新播放器 + // 步骤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); delete newPlayer; - newPlayer = nullptr; - - // 启动失败,恢复旧播放器 - 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 - { - // 如果没有视图显示,释放内存 - 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; return false; } - // 步骤4:等待并检查新播放器是否正常播放 - int checkCount = 0; - const int maxCheckCount = 50; // 50 * 20ms = 1000ms - bool newPlayerPlaying = false; - bool newPlayerError = false; + // 步骤5:立即切换指针到新播放器 + videoPlay_ = newPlayer; + g_videoPlay = videoPlay_; - while (checkCount < maxCheckCount) - { - // 检查新播放器是否真正开始播放 - if (newPlayer != nullptr && newPlayer->isEntered_ && newPlayer->isPlayed_ && !newPlayer->isPause_) - { - newPlayerPlaying = true; - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player confirmed playing after %d ms", checkCount * 20); - break; - } - // 检查新播放器是否发生错误 - if (newPlayer != nullptr && newPlayer->playError_) + // 步骤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) { - newPlayerError = true; - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player error detected after %d ms", checkCount * 20); - break; + // 检查新播放器是否真正开始播放 + 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++; } - usleep(20000); // 等待20ms - checkCount++; - } - - // 步骤5:根据新播放器状态决定后续操作 - if (newPlayerPlaying && !newPlayerError) - { - // 新播放器正常播放,检查第一帧是否已输出 - bool firstFrameReady = false; - LiteSurface* liteSurface = dynamic_cast(surface); - if (liteSurface != nullptr) + + // 如果新播放器已开始播放,检查Surface是否有新的buffer(表示第一帧已输出) + if (playerConfirmed) { - int bufferCheckCount = 0; - const int maxBufferCheckCount = 30; // 30 * 50ms = 1500ms - - while (bufferCheckCount < maxBufferCheckCount) + // 检查Surface是否有新的buffer被flush(表示第一帧已经输出到Surface) + // 通过检查GetBackBuf()来判断是否有新的buffer + LiteSurface* liteSurface = dynamic_cast(surface); + if (liteSurface != nullptr) { - SurfaceBuffer* backBuf = liteSurface->GetBackBuf(); - if (backBuf != 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) { - firstFrameReady = true; - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer ready after %d ms", bufferCheckCount * 50); - usleep(50000); // 50ms,确保第一帧已渲染到屏幕 - break; + WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: First frame buffer not ready after %d ms, proceeding anyway", maxBufferCheckCount * 50); } - usleep(50000); // 等待50ms - bufferCheckCount++; } - } - else - { - // 如果不是LiteSurface,使用固定等待时间 - usleep(300000); // 300ms - firstFrameReady = true; - } - - // 新播放器正常播放,停止并销毁旧播放器 - if (oldPlayer != nullptr) - { - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: New player playing successfully, destroying old player"); - if (oldWasEntered || oldWasPlaying) + else { - oldPlayer->StopVideoPlay(); + // 如果不是LiteSurface,使用固定等待时间 + usleep(300000); // 300ms } - // 等待旧播放器完全停止 - usleep(50000); // 50ms,确保旧播放器完全停止 - delete oldPlayer; - oldPlayer = nullptr; } - // 更新指针到新播放器 - videoPlay_ = newPlayer; - g_videoPlay = videoPlay_; - - // 更新当前播放的视频路径 - currentVideoPath_ = targetPath; - // 清除切换视频标志 - g_isSwitchingVideo = false; - - // 隐藏最后一帧视图(放到最后,确保新播放器已经完全接管显示) - if (firstFrameReady && lastFrameView_ != nullptr && lastFrameView_->IsVisible()) + // 隐藏最后一帧视图(此时新播放器的第一帧应该已经渲染到屏幕) + // 静态图片会一直遮挡,直到这里才隐藏 + if (lastFrameView_ != nullptr && lastFrameView_->IsVisible()) { - - //异步隐藏最后一帧视图 GraphicService::GetInstance()->PostGraphicEvent([this]() { - usleep(200000); // 100ms lastFrameView_->SetVisible(false); lastFrameView_->Invalidate(); - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Hidden last frame view, new player first frame rendered"); + 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"); } - - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Successfully switched to video: %s", uri); - return true; } - else + else if (hasLastFrame && lastFrameImage.data != nullptr) { - // 新播放器未正常播放,销毁新播放器并恢复旧播放器 - 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; + // 如果没有静态图片视图,直接释放截图内存 + ImageCacheFree(lastFrameImage); } + + // 更新当前播放的视频路径 + currentVideoPath_ = targetPath; + + // 清除切换视频标志 + g_isSwitchingVideo = false; + + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ThunderBirdMainView::SwitchVideo: Successfully switched to video: %s", uri); + return true; } void ThunderBirdMainView::CloseVideo() // 关闭表情接口 -- 2.17.1