回退表情切换 2981/1 2981/head 2983/target/1
authorxue <2144439972@qq.com>
Fri, 26 Dec 2025 11:27:13 +0000 (19:27 +0800)
committerxue <2144439972@qq.com>
Fri, 26 Dec 2025 11:27:13 +0000 (19:27 +0800)
code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdMainView.cpp

index 3775b5c..49c5df6 100644 (file)
@@ -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<LiteSurface*>(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<LiteSurface*>(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() // 关闭表情接口