diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 2af22e8e4..5e7d6a604 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -1196,7 +1196,7 @@ void FileData::launchGame() // This blocks the video player, stops the scrolling of game names and descriptions and // keeps the screensaver from getting activated. if (runInBackground) - window->setLaunchedGame(); + window->setLaunchedGame(true); // Normalize deltaTime so that the screensaver does not start immediately // when returning from the game. window->normalizeNextUpdate(); diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index ca56f5b66..08592341d 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -43,9 +43,6 @@ bool MediaViewer::startMediaViewer(FileData* game) initiateViewer(); - if (mHasVideo) - ViewController::getInstance()->onPauseVideo(); - if (mHasVideo || mHasImages) return true; else @@ -55,7 +52,7 @@ bool MediaViewer::startMediaViewer(FileData* game) void MediaViewer::stopMediaViewer() { NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - ViewController::getInstance()->onStopVideo(); + ViewController::getInstance()->stopViewVideos(); if (mVideo) { delete mVideo; @@ -257,10 +254,9 @@ void MediaViewer::playVideo() return; mDisplayingImage = false; - ViewController::getInstance()->onStopVideo(); + ViewController::getInstance()->pauseViewVideos(); mVideo = new VideoFFmpegComponent; - mVideo->topWindow(true); mVideo->setOrigin(0.5f, 0.5f); mVideo->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); @@ -271,7 +267,7 @@ void MediaViewer::playVideo() mVideo->setVideo(mVideoFile); mVideo->setMediaViewerMode(true); - mVideo->onShow(); + mVideo->startVideoPlayer(); } void MediaViewer::showImage(int index) diff --git a/es-app/src/Screensaver.cpp b/es-app/src/Screensaver.cpp index 6dca52057..439f89fae 100644 --- a/es-app/src/Screensaver.cpp +++ b/es-app/src/Screensaver.cpp @@ -59,6 +59,8 @@ Screensaver::~Screensaver() void Screensaver::startScreensaver(bool generateMediaList) { + ViewController::getInstance()->pauseViewVideos(); + std::string path = ""; std::string screensaverType = Settings::getInstance()->getString("ScreensaverType"); mHasMediaFiles = false; @@ -157,7 +159,6 @@ void Screensaver::startScreensaver(bool generateMediaList) generateOverlayInfo(); mVideoScreensaver = new VideoFFmpegComponent; - mVideoScreensaver->topWindow(true); mVideoScreensaver->setOrigin(0.5f, 0.5f); mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); @@ -171,7 +172,7 @@ void Screensaver::startScreensaver(bool generateMediaList) mVideoScreensaver->setVideo(path); mVideoScreensaver->setScreensaverMode(true); - mVideoScreensaver->onShow(); + mVideoScreensaver->startVideoPlayer(); mTimer = 0; return; } @@ -197,6 +198,8 @@ void Screensaver::stopScreensaver() if (mGameOverlay) mGameOverlay.reset(); + + ViewController::getInstance()->startViewVideos(); } void Screensaver::nextGame() @@ -215,6 +218,7 @@ void Screensaver::launchGame() ViewController::getInstance()->getGamelistView(mCurrentGame->getSystem()).get(); view->setCursor(mCurrentGame); ViewController::getInstance()->cancelViewTransitions(); + ViewController::getInstance()->pauseViewVideos(); } } diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 5761cc20a..06469887a 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -254,6 +254,8 @@ GuiGamelistOptions::~GuiGamelistOptions() // the menu has been closed. ViewController::getInstance()->stopScrolling(); + ViewController::getInstance()->startViewVideos(); + if (mFiltersChanged) { if (!mCustomCollectionSystem) { ViewController::getInstance()->reloadGamelistView(mSystem); diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 584ceb441..5839a6032 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -87,6 +87,8 @@ GuiMenu::~GuiMenu() // was openened. Without this, the scrolling would run until manually stopped after // the menu has been closed. ViewController::getInstance()->stopScrolling(); + + ViewController::getInstance()->startViewVideos(); } void GuiMenu::openScraperOptions() diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 950a0a3a0..94b3e93ec 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -514,7 +514,6 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, buttons.push_back(std::make_shared<ButtonComponent>("SAVE", "save metadata", [&] { save(); - ViewController::getInstance()->onPauseVideo(); delete this; })); buttons.push_back( @@ -841,7 +840,6 @@ void GuiMetaDataEd::close() CollectionSystemsManager::getInstance()->refreshCollectionSystems(mScraperParams.game); mWindow->invalidateCachedBackground(); } - ViewController::getInstance()->onPauseVideo(); delete this; }; diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 10713d660..4b9f50d7c 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -175,7 +175,6 @@ GuiScraperMulti::~GuiScraperMulti() (*it)->sortSystem(); } } - ViewController::getInstance()->onPauseVideo(); } void GuiScraperMulti::onSizeChanged() diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index 6a42da6d3..b50fa9b82 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -261,7 +261,7 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, scrapeFiles.push_back(mediaFileInfo); #if defined(_WIN64) // Required due to the idiotic file locking that exists on this operating system. - ViewController::getInstance()->onStopVideo(); + ViewController::getInstance()->stopVideo(); #endif } diff --git a/es-app/src/views/GamelistBase.cpp b/es-app/src/views/GamelistBase.cpp index bb759992a..6d53aba9e 100644 --- a/es-app/src/views/GamelistBase.cpp +++ b/es-app/src/views/GamelistBase.cpp @@ -74,7 +74,7 @@ bool GamelistBase::input(InputConfig* config, Input input) if (config->isMappedTo("a", input)) { FileData* cursor = getCursor(); if (cursor->getType() == GAME) { - onPauseVideo(); + pauseViewVideos(); ViewController::getInstance()->cancelViewTransitions(); stopListScrolling(); launch(cursor); @@ -131,7 +131,7 @@ bool GamelistBase::input(InputConfig* config, Input input) } else { NavigationSounds::getInstance().playThemeNavigationSound(BACKSOUND); - onPauseVideo(); + muteViewVideos(); onFocusLost(); stopListScrolling(); SystemData* systemToView = getCursor()->getSystem(); @@ -175,7 +175,7 @@ bool GamelistBase::input(InputConfig* config, Input input) else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) { if (Settings::getInstance()->getBool("QuickSystemSelect") && SystemData::sSystemVector.size() > 1) { - onPauseVideo(); + muteViewVideos(); onFocusLost(); stopListScrolling(); ViewController::getInstance()->goToNextGamelist(); @@ -185,7 +185,7 @@ bool GamelistBase::input(InputConfig* config, Input input) else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { if (Settings::getInstance()->getBool("QuickSystemSelect") && SystemData::sSystemVector.size() > 1) { - onPauseVideo(); + muteViewVideos(); onFocusLost(); stopListScrolling(); ViewController::getInstance()->goToPrevGamelist(); @@ -457,6 +457,7 @@ bool GamelistBase::input(InputConfig* config, Input input) config->isMappedTo("back", input) && input.value) { ViewController::getInstance()->cancelViewTransitions(); stopListScrolling(); + pauseViewVideos(); mWindow->pushGui(new GuiGamelistOptions(this->mRoot->getSystem())); return true; } @@ -621,7 +622,7 @@ void GamelistBase::generateFirstLetterIndex(const std::vector<FileData*>& files) void GamelistBase::generateGamelistInfo(FileData* cursor, FileData* firstEntry) { // Generate data needed for the gamelistInfo field, which is displayed from the - // gamelist interfaces (Detailed/Video/Grid). + // gamelist interfaces. mIsFiltered = false; mIsFolder = false; FileData* rootFolder {firstEntry->getSystem()->getRootFolder()}; @@ -704,7 +705,7 @@ void GamelistBase::removeMedia(FileData* game) std::string path; // Stop the video player, especially important on Windows as the file would otherwise be locked. - onStopVideo(); + stopViewVideos(); // If there are no media files left in the directory after the deletion, then remove // the directory too. Remove any empty parent directories as well. diff --git a/es-app/src/views/GamelistLegacy.h b/es-app/src/views/GamelistLegacy.h index bc68bdd7d..b77206d3e 100644 --- a/es-app/src/views/GamelistLegacy.h +++ b/es-app/src/views/GamelistLegacy.h @@ -366,6 +366,12 @@ void GamelistView::legacyUpdateInfoPanel() bool fadingOut = false; if (file == nullptr) { + if (mViewStyle == ViewController::VIDEO) { + mVideoComponents.front()->stopVideoPlayer(); + mVideoComponents.front()->setVideo(""); + if (!mVideoComponents.front()->hasStartDelay()) + mVideoComponents.front()->setImage(""); + } mVideoPlaying = false; fadingOut = true; } @@ -383,15 +389,12 @@ void GamelistView::legacyUpdateInfoPanel() mImageComponents[LegacyImage::MD_MARQUEE]->setImage(mRandomGame->getMarqueePath()); if (mViewStyle == ViewController::VIDEO) { mVideoComponents.front()->setImage(mRandomGame->getImagePath()); - // Always stop the video before setting a new video as it will otherwise - // continue to play if it has the same path (i.e. it is the same physical - // video file) as the previously set video. That may happen when entering a - // folder with the same name as the first game file inside, or as in this - // case, when entering a custom collection. - mVideoComponents.front()->onHide(); + mVideoComponents.front()->stopVideoPlayer(); if (!mVideoComponents.front()->setVideo(mRandomGame->getVideoPath())) mVideoComponents.front()->setDefaultVideo(); + + mVideoComponents.front()->startVideoPlayer(); } else { mImageComponents[LegacyImage::MD_IMAGE]->setImage(mRandomGame->getImagePath()); @@ -413,10 +416,12 @@ void GamelistView::legacyUpdateInfoPanel() mImageComponents[LegacyImage::MD_MARQUEE]->setImage(file->getMarqueePath()); if (mViewStyle == ViewController::VIDEO) { mVideoComponents.front()->setImage(file->getImagePath()); - mVideoComponents.front()->onHide(); + mVideoComponents.front()->stopVideoPlayer(); if (!mVideoComponents.front()->setVideo(file->getVideoPath())) mVideoComponents.front()->setDefaultVideo(); + + mVideoComponents.front()->startVideoPlayer(); } else { mImageComponents[LegacyImage::MD_IMAGE]->setImage(file->getImagePath()); @@ -550,6 +555,8 @@ void GamelistView::legacyUpdateInfoPanel() comps.emplace_back(mImageComponents[LegacyImage::MD_THUMBNAIL].get()); comps.emplace_back(mImageComponents[LegacyImage::MD_MARQUEE].get()); comps.emplace_back(mImageComponents[LegacyImage::MD_IMAGE].get()); + if (mVideoComponents.size() > 0) + comps.emplace_back(mVideoComponents.front().get()); comps.push_back(mBadgeComponents.front().get()); comps.push_back(mRatingComponents.front().get()); @@ -576,12 +583,6 @@ void GamelistView::legacyUpdate(int deltaTime) mImageComponents[LegacyImage::MD_IMAGE]->finishAnimation(0); if (mViewStyle == ViewController::VIDEO) { - if (!mVideoPlaying) - mVideoComponents.front()->onHide(); - else if (mVideoPlaying && !mVideoComponents.front()->isVideoPaused() && - !mWindow->isScreensaverActive()) - mVideoComponents.front()->onShow(); - if (ViewController::getInstance()->getGameLaunchTriggered() && mVideoComponents.front()->isAnimationPlaying(0)) mVideoComponents.front()->finishAnimation(0); diff --git a/es-app/src/views/GamelistView.cpp b/es-app/src/views/GamelistView.cpp index 8dba275ca..c168dab01 100644 --- a/es-app/src/views/GamelistView.cpp +++ b/es-app/src/views/GamelistView.cpp @@ -204,23 +204,6 @@ void GamelistView::update(int deltaTime) } } - for (auto& video : mVideoComponents) { - if (!mVideoPlaying) { - if (!video->getScrollHide()) - video->onHide(); - else if (!video->hasStaticImage()) - video->onHide(); - else if (video->getOpacity() == 0.0f) - video->onHide(); - } - else if (mVideoPlaying && !video->isVideoPaused() && !mWindow->isScreensaverActive()) { - video->onShow(); - } - - if (ViewController::getInstance()->getGameLaunchTriggered() && video->isAnimationPlaying(0)) - video->finishAnimation(0); - } - updateChildren(deltaTime); } @@ -375,6 +358,14 @@ void GamelistView::updateInfoPanel() bool fadingOut = false; if (file == nullptr) { + if (mVideoPlaying) { + for (auto& video : mVideoComponents) { + video->stopVideoPlayer(); + video->setVideo(""); + if (!video->hasStartDelay()) + video->setImage(""); + } + } mVideoPlaying = false; fadingOut = true; } @@ -393,17 +384,14 @@ void GamelistView::updateInfoPanel() for (auto& video : mVideoComponents) { setGameImage(mRandomGame, video.get()); - // Always stop the video before setting a new video as it will otherwise - // continue to play if it has the same path (i.e. it is the same physical - // video file) as the previously set video. That may happen when entering a - // folder with the same name as the first game file inside, or as in this - // case, when entering a custom collection. - video->onHide(); + video->stopVideoPlayer(); if (video->hasStaticVideo()) video->setStaticVideo(); else if (!video->setVideo(mRandomGame->getVideoPath())) video->setDefaultVideo(); + + video->startVideoPlayer(); } } else { @@ -413,10 +401,10 @@ void GamelistView::updateInfoPanel() } for (auto& video : mVideoComponents) { + video->stopVideoPlayer(); video->setImage(""); video->setVideo(""); if (video->hasStaticVideo()) { - video->onStopVideo(); video->setStaticVideo(); } else { @@ -426,17 +414,20 @@ void GamelistView::updateInfoPanel() } } else { - for (auto& image : mImageComponents) + for (auto& image : mImageComponents) { setGameImage(file, image.get()); + } for (auto& video : mVideoComponents) { setGameImage(file, video.get()); - video->onHide(); + video->stopVideoPlayer(); if (video->hasStaticVideo()) video->setStaticVideo(); else if (!video->setVideo(file->getVideoPath())) video->setDefaultVideo(); + + video->startVideoPlayer(); } } diff --git a/es-app/src/views/GamelistView.h b/es-app/src/views/GamelistView.h index fb9304070..4f9ae35f0 100644 --- a/es-app/src/views/GamelistView.h +++ b/es-app/src/views/GamelistView.h @@ -41,6 +41,28 @@ public: } } + void startViewVideos() override + { + for (auto& video : mVideoComponents) + video->startVideoPlayer(); + } + void stopViewVideos() override + { + for (auto& video : mVideoComponents) + video->stopVideoPlayer(); + } + void pauseViewVideos() override + { + for (auto& video : mVideoComponents) { + video->pauseVideoPlayer(); + } + } + void muteViewVideos() override + { + for (auto& video : mVideoComponents) + video->muteVideoPlayer(); + } + const std::shared_ptr<ThemeData> getTheme() const { return mTheme; } void setTheme(const std::shared_ptr<ThemeData>& theme) { diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 846027d04..64ad24b9c 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -648,11 +648,6 @@ void ViewController::launch(FileData* game) return; } - // If the video view style is used, pause the video currently playing or block the - // video from starting to play if the static image is still shown. - if (mCurrentView) - mCurrentView->onPauseVideo(); - // Disable text scrolling and stop any Lottie animations. These will be enabled again in // FileData upon returning from the game. mWindow->setAllowTextScrolling(false); @@ -806,7 +801,7 @@ bool ViewController::input(InputConfig* config, Input input) if (mWindow->getGameLaunchedState()) { mWindow->setAllowTextScrolling(true); mWindow->setAllowFileAnimation(true); - mWindow->unsetLaunchedGame(); + mWindow->setLaunchedGame(false); // Filter out the "a" button so the game is not restarted if there was such a button press // queued when leaving the game. if (config->isMappedTo("a", input) && input.value != 0) @@ -825,9 +820,12 @@ bool ViewController::input(InputConfig* config, Input input) // to play when we've closed the menu. if (mSystemListView->isSystemAnimationPlaying(0)) mSystemListView->finishSystemAnimation(0); - // Stop the gamelist scrolling as well as it would otherwise - // also continue to run after closing the menu. + // Stop the gamelist scrolling as well as it would otherwise continue to run after + // closing the menu. mCurrentView->stopListScrolling(); + // Pause all videos as they would otherwise continue to play beneath the menu. + mCurrentView->pauseViewVideos(); + // Finally, if the camera is currently moving, reset its position. cancelViewTransitions(); @@ -985,7 +983,7 @@ void ViewController::reloadGamelistView(GamelistView* view, bool reloadTheme) // video player, prevent scrolling of game names and game descriptions and prevent the // screensaver from starting on schedule. if (mWindow->getGameLaunchedState()) - mWindow->setLaunchedGame(); + mWindow->setLaunchedGame(true); // Redisplay the current view. if (mCurrentView) diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index 1ce4dd95b..eb589ab80 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -66,6 +66,12 @@ public: void cancelViewTransitions(); void stopScrolling(); + // Basic video controls. + void startViewVideos() override { mCurrentView->startViewVideos(); } + void stopViewVideos() override { mCurrentView->stopViewVideos(); } + void pauseViewVideos() override { mCurrentView->pauseViewVideos(); } + void muteViewVideos() override { mCurrentView->muteViewVideos(); } + void onFileChanged(FileData* file, bool reloadGamelist); void triggerGameLaunch(FileData* game) { diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index f0051ea7f..21e6dda22 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -393,51 +393,3 @@ void GuiComponent::onHide() for (unsigned int i = 0; i < getChildCount(); ++i) getChild(i)->onHide(); } - -void GuiComponent::onStopVideo() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onStopVideo(); -} - -void GuiComponent::onPauseVideo() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onPauseVideo(); -} - -void GuiComponent::onUnpauseVideo() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onUnpauseVideo(); -} - -void GuiComponent::onScreensaverActivate() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onScreensaverActivate(); -} - -void GuiComponent::onScreensaverDeactivate() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onScreensaverDeactivate(); -} - -void GuiComponent::onGameLaunchedActivate() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onGameLaunchedActivate(); -} - -void GuiComponent::onGameLaunchedDeactivate() -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->onGameLaunchedDeactivate(); -} - -void GuiComponent::topWindow(bool isTop) -{ - for (unsigned int i = 0; i < getChildCount(); ++i) - getChild(i)->topWindow(isTop); -} diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 8cb03502e..9e2052a70 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -238,19 +238,16 @@ public: virtual void onShow(); virtual void onHide(); - virtual void onStopVideo(); - virtual void onPauseVideo(); - virtual void onUnpauseVideo(); - virtual bool isVideoPaused() { return false; } + + // System view and gamelist view video controls. + virtual void startViewVideos() {} + virtual void stopViewVideos() {} + virtual void pauseViewVideos() {} + virtual void muteViewVideos() {} + // For Lottie animations. virtual void resetFileAnimation() {}; - virtual void onScreensaverActivate(); - virtual void onScreensaverDeactivate(); - virtual void onGameLaunchedActivate(); - virtual void onGameLaunchedDeactivate(); - virtual void topWindow(bool isTop); - // Default implementation just handles <pos> and <size> tags as normalized float pairs. // You probably want to keep this behavior for any derived classes as well as add your own. virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index b2e91f680..2bf71f6df 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -75,10 +75,6 @@ Window* Window::getInstance() void Window::pushGui(GuiComponent* gui) { - if (mGuiStack.size() > 0) { - auto& top = mGuiStack.back(); - top->topWindow(false); - } mGuiStack.push_back(gui); gui->updateHelpPrompts(); } @@ -90,10 +86,8 @@ void Window::removeGui(GuiComponent* gui) it = mGuiStack.erase(it); // We just popped the stack and the stack is not empty. - if (it == mGuiStack.cend() && mGuiStack.size()) { + if (it == mGuiStack.cend() && mGuiStack.size()) mGuiStack.back()->updateHelpPrompts(); - mGuiStack.back()->topWindow(true); - } return; } @@ -752,10 +746,6 @@ void Window::stopInfoPopup() void Window::startScreensaver() { if (mScreensaver && !mRenderScreensaver) { - // Tell the GUI components the screensaver is starting. - for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); ++it) - (*it)->onScreensaverActivate(); - setAllowTextScrolling(false); setAllowFileAnimation(false); mScreensaver->startScreensaver(true); @@ -771,14 +761,6 @@ bool Window::stopScreensaver() setAllowTextScrolling(true); setAllowFileAnimation(true); - // Tell the GUI components the screensaver has stopped. - for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); ++it) { - (*it)->onScreensaverDeactivate(); - // If the menu is open, pause the video so it won't start playing beneath the menu. - if (mGuiStack.front() != mGuiStack.back()) - (*it)->onPauseVideo(); - } - return true; } @@ -830,10 +812,6 @@ void Window::closeLaunchScreen() mRenderLaunchScreen = false; } -void Window::increaseVideoPlayerCount() { ++mVideoPlayerCount; } - -void Window::decreaseVideoPlayerCount() { --mVideoPlayerCount; } - int Window::getVideoPlayerCount() { int videoPlayerCount; @@ -841,24 +819,6 @@ int Window::getVideoPlayerCount() return videoPlayerCount; } -void Window::setLaunchedGame() -{ - // Tell the GUI components that a game has been launched. - for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); ++it) - (*it)->onGameLaunchedActivate(); - - mGameLaunchedState = true; -} - -void Window::unsetLaunchedGame() -{ - // Tell the GUI components that the user is back in ES-DE again. - for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); ++it) - (*it)->onGameLaunchedDeactivate(); - - mGameLaunchedState = false; -} - void Window::invalidateCachedBackground() { mCachedBackground = false; diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 8f9d12c83..82ec2263c 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -130,12 +130,11 @@ public: void setLaunchScreen(GuiLaunchScreen* launchScreen) { mLaunchScreen = launchScreen; } bool isLaunchScreenDisplayed() { return mRenderLaunchScreen; } - void increaseVideoPlayerCount(); - void decreaseVideoPlayerCount(); + void increaseVideoPlayerCount() { ++mVideoPlayerCount; } + void decreaseVideoPlayerCount() { --mVideoPlayerCount; } int getVideoPlayerCount(); - void setLaunchedGame(); - void unsetLaunchedGame(); + void setLaunchedGame(bool state) { mGameLaunchedState = state; } void invalidateCachedBackground(); bool isInvalidatingCachedBackground() { return mInvalidateCacheTimer > 0; } diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 13fa9e0b8..39f9cdf54 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -25,43 +25,36 @@ VideoComponent::VideoComponent() , mTargetSize {0.0f, 0.0f} , mVideoAreaPos {0.0f, 0.0f} , mVideoAreaSize {0.0f, 0.0f} - , mStartDelayed {false} + , mStartTime {0} , mIsPlaying {false} , mIsActuallyPlaying {false} - , mPause {false} - , mShowing {false} - , mDisable {false} + , mPaused {false} , mMediaViewerMode {false} - , mScreensaverActive {false} , mScreensaverMode {false} - , mGameLaunched {false} - , mBlockPlayer {false} , mTargetIsMax {false} , mDrawPillarboxes {true} , mRenderScanlines {false} , mLegacyTheme {false} + , mHasVideo {false} , mFadeIn {1.0f} , mFadeInTime {1000.0f} { - // Setup the default configuration. + // Setup default configuration. mConfig.showSnapshotDelay = false; mConfig.showSnapshotNoVideo = false; mConfig.startDelay = 1500; - - if (mWindow->getGuiStackSize() > 1) - topWindow(false); } VideoComponent::~VideoComponent() { // Stop any currently running video. - stopVideo(); + stopVideoPlayer(); } bool VideoComponent::setVideo(std::string path) { // Convert the path into a generic format. - std::string fullPath = Utils::FileSystem::getCanonicalPath(path); + std::string fullPath {Utils::FileSystem::getCanonicalPath(path)}; // Check that it's changed. if (fullPath == mVideoPath) @@ -72,10 +65,17 @@ bool VideoComponent::setVideo(std::string path) // If the file exists then set the new video. if (!fullPath.empty() && ResourceManager::getInstance().fileExists(fullPath)) { + mHasVideo = true; // Return true to show that we are going to attempt to play a video. return true; } + if (!mVideoPath.empty() || !mConfig.defaultVideoPath.empty() || + !mConfig.staticVideoPath.empty()) + mHasVideo = true; + else + mHasVideo = false; + // Return false to show that no video will be displayed. return false; } @@ -87,7 +87,7 @@ void VideoComponent::setImage(const std::string& path, bool tile) if (imagePath == "") imagePath = mDefaultImagePath; - // Check that the image has changed. + // Check if the image has changed. if (imagePath == mStaticImagePath) return; @@ -95,127 +95,6 @@ void VideoComponent::setImage(const std::string& path, bool tile) mStaticImagePath = imagePath; } -void VideoComponent::onShow() -{ - mBlockPlayer = false; - mPause = false; - mShowing = true; - manageState(); -} - -void VideoComponent::onHide() -{ - mShowing = false; - manageState(); -} - -void VideoComponent::onStopVideo() -{ - stopVideo(); - manageState(); -} - -void VideoComponent::onPauseVideo() -{ - mBlockPlayer = true; - mPause = true; - manageState(); -} - -void VideoComponent::onUnpauseVideo() -{ - mBlockPlayer = false; - mPause = false; - manageState(); -} - -void VideoComponent::onScreensaverActivate() -{ - mBlockPlayer = true; - mPause = true; - - if (Settings::getInstance()->getString("ScreensaverType") == "dim") - stopVideo(); - else - pauseVideo(); - manageState(); -} - -void VideoComponent::onScreensaverDeactivate() -{ - mBlockPlayer = false; - // Stop video when deactivating the screensaver to force a reload of the - // static image (if the theme is configured as such). - stopVideo(); - manageState(); -} - -void VideoComponent::onGameLaunchedActivate() -{ - mGameLaunched = true; - manageState(); -} - -void VideoComponent::onGameLaunchedDeactivate() -{ - mGameLaunched = false; - stopVideo(); - manageState(); -} - -void VideoComponent::topWindow(bool isTop) -{ - if (isTop) { - mBlockPlayer = false; - mPause = false; - - // Stop video when closing the menu to force a reload of the - // static image (if the theme is configured as such). - stopVideo(); - } - else { - mBlockPlayer = true; - mPause = true; - } - manageState(); -} - -void VideoComponent::render(const glm::mat4& parentTrans) -{ - if (!isVisible()) - return; - - glm::mat4 trans {parentTrans * getTransform()}; - GuiComponent::renderChildren(trans); - - Renderer::setMatrix(trans); - - // Handle the case where the video is delayed. - handleStartDelay(); - - // Handle looping of the video. - handleLooping(); - - // Pause video in case a game has been launched. - pauseVideo(); -} - -void VideoComponent::renderSnapshot(const glm::mat4& parentTrans) -{ - // This function is called when the video is not currently being played. We need to - // work out if we should display a static image. If the menu is open, then always render - // the static image as the metadata may have been changed. In that case the gamelist - // was reloaded and there would just be a blank space unless we render the image here. - // The side effect of this is that a static image is displayed even for themes that are - // set to start playing the video immediately. Although this may seem a bit inconsistent it - // simply looks better than leaving an empty space where the video would have been located. - if (mWindow->getGuiStackSize() > 1 || (mConfig.showSnapshotNoVideo && mVideoPath.empty()) || - (mStartDelayed && mConfig.showSnapshotDelay)) { - mStaticImage.setOpacity(mOpacity * mThemeOpacity); - mStaticImage.render(parentTrans); - } -} - void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, @@ -327,12 +206,29 @@ std::vector<HelpPrompt> VideoComponent::getHelpPrompts() void VideoComponent::update(int deltaTime) { - if (mBlockPlayer) { - setImage(mStaticImagePath); + if (!mHasVideo) { + // We need this update so the static image gets updated (e.g. used for fade animations). + GuiComponent::update(deltaTime); return; } - manageState(); + // Hack to prevent the video from starting to play if the static image was shown when paused. + if (mPaused) + mStartTime = SDL_GetTicks() + mConfig.startDelay; + + if (mWindow->getGameLaunchedState()) + return; + + bool playVideo {false}; + + if (!mIsPlaying && mConfig.startDelay == 0) { + startVideoStream(); + } + else if (mStartTime == 0 || SDL_GetTicks() > mStartTime) { + mStartTime = 0; + playVideo = true; + startVideoStream(); + } // Fade in videos, the time period is a bit different between the screensaver and media viewer. // For the theme controlled videos in the gamelist and system views, the fade-in time is set @@ -349,90 +245,37 @@ void VideoComponent::update(int deltaTime) mFadeIn = glm::clamp(mFadeIn + (deltaTime / static_cast<float>(mFadeInTime)), 0.0f, 1.0f); } + if (mIsPlaying) + updatePlayer(); + + handleLooping(); + GuiComponent::update(deltaTime); } -void VideoComponent::startVideoWithDelay() +void VideoComponent::startVideoPlayer() { - mPause = false; + if (mIsPlaying) + stopVideoPlayer(); - // If not playing then either start the video or initiate the delay. - if (!mIsPlaying) { - // Set the video that we are going to be playing so we don't attempt to restart it. - mPlayingVideoPath = mVideoPath; - - if (mConfig.startDelay == 0) { - // No delay. Just start the video. - mStartDelayed = false; - startVideo(); - } - else { - // Configure the start delay. - mStartDelayed = true; - mStartTime = SDL_GetTicks() + mConfig.startDelay; - } - mIsPlaying = true; + if (mConfig.startDelay != 0 && mStaticImagePath != "") { + mStartTime = SDL_GetTicks() + mConfig.startDelay; + setImage(mStaticImagePath); } + + mPaused = false; } -void VideoComponent::handleStartDelay() +void VideoComponent::renderSnapshot(const glm::mat4& parentTrans) { - if (mBlockPlayer || mGameLaunched) + if (mLegacyTheme && !mHasVideo && !mConfig.showSnapshotNoVideo) return; - // Only play if any delay has timed out. - if (mStartDelayed) { - // If the setting to override the theme-supplied video delay setting has been enabled, - // then play the video immediately. - if (!Settings::getInstance()->getBool("PlayVideosImmediately")) { - // If there is a video file available but no static image, then start playing the - // video immediately regardless of theme configuration or settings. - if (mStaticImagePath != "") { - if (mStartTime > SDL_GetTicks()) { - // Timeout not yet completed. - return; - } - } - } - // Completed. - mStartDelayed = false; - // Clear the playing flag so startVideo works. - mIsPlaying = false; - startVideo(); + if (mHasVideo && (!mConfig.showSnapshotDelay || mConfig.startDelay == 0)) + return; + + if (mStaticImagePath != "") { + mStaticImage.setOpacity(mOpacity * mThemeOpacity); + mStaticImage.render(parentTrans); } } - -void VideoComponent::manageState() -{ - // We will only show the video if the component is on display and the screensaver - // is not active. - bool show = mShowing && !mScreensaverActive && !mDisable; - - // See if we're already playing. - if (mIsPlaying) { - // If we are not on display then stop the video from playing. - if (!show) { - stopVideo(); - } - else { - if (mVideoPath != mPlayingVideoPath) { - // Path changed. Stop the video. We will start it again below because - // mIsPlaying will be modified by stopVideo to be false. - stopVideo(); - } - } - updatePlayer(); - } - // Need to recheck variable rather than 'else' because it may be modified above. - if (!mIsPlaying) { - // If we are on display then see if we should start the video. - if (show && !mVideoPath.empty()) - startVideoWithDelay(); - } - - // If a game has just been launched and a video is actually shown, then request a - // pause of the video so it doesn't continue to play in the background while the - // game is running. - if (mGameLaunched && show && !mPause) - mPause = true; -} diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 32eb4a4e1..f5fe43fa7 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -52,27 +52,19 @@ public: bool hasStaticVideo() { return !mConfig.staticVideoPath.empty(); } bool hasStaticImage() { return mStaticImage.getTextureSize() != glm::ivec2 {0, 0}; } - - void onShow() override; - void onHide() override; - void onStopVideo() override; - void onPauseVideo() override; - void onUnpauseVideo() override; - bool isVideoPaused() override { return mPause; } - void onScreensaverActivate() override; - void onScreensaverDeactivate() override; - void onGameLaunchedActivate() override; - void onGameLaunchedDeactivate() override; - void topWindow(bool isTop) override; + bool hasStartDelay() + { + if (mLegacyTheme) + return mConfig.showSnapshotDelay && mConfig.startDelay > 0; + else + return mConfig.startDelay > 0; + } // These functions update the embedded static image. void onOriginChanged() override { mStaticImage.setOrigin(mOrigin); } void onPositionChanged() override { mStaticImage.setPosition(mPosition); } void onSizeChanged() override { mStaticImage.onSizeChanged(); } - void render(const glm::mat4& parentTrans) override; - void renderSnapshot(const glm::mat4& parentTrans); - void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, @@ -95,27 +87,21 @@ public: virtual void setMaxSize(float width, float height) = 0; void setMaxSize(const glm::vec2& size) { setMaxSize(size.x, size.y); } -private: - // Start the video immediately. - virtual void startVideo() {} - // Stop the video. - virtual void stopVideo() {} - // Pause the video when a game has been launched. - virtual void pauseVideo() {} + // Basic video controls. + void startVideoPlayer(); + virtual void stopVideoPlayer() {} + virtual void pauseVideoPlayer() {} + // Handle looping of the video. Must be called periodically. virtual void handleLooping() {} + // Used to immediately mute audio even if there are still samples to play in the buffer. + virtual void muteVideoPlayer() {} virtual void updatePlayer() {} - // Start the video after any configured delay. - void startVideoWithDelay(); - // Handle any delay to the start of playing the video clip. Must be called periodically. - void handleStartDelay(); - // Manage the playing state of the component. - void manageState(); - - friend MediaViewer; - protected: + virtual void startVideoStream() {} + void renderSnapshot(const glm::mat4& parentTrans); + ImageComponent mStaticImage; unsigned mVideoWidth; @@ -128,23 +114,17 @@ protected: std::string mDefaultImagePath; std::string mVideoPath; - std::string mPlayingVideoPath; unsigned mStartTime; - bool mStartDelayed; std::atomic<bool> mIsPlaying; std::atomic<bool> mIsActuallyPlaying; - std::atomic<bool> mPause; - bool mShowing; - bool mDisable; + std::atomic<bool> mPaused; bool mMediaViewerMode; - bool mScreensaverActive; bool mScreensaverMode; - bool mGameLaunched; - bool mBlockPlayer; bool mTargetIsMax; bool mDrawPillarboxes; bool mRenderScanlines; bool mLegacyTheme; + bool mHasVideo; float mFadeIn; float mFadeInTime; diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index 451796dc3..752b291fe 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -54,8 +54,6 @@ VideoFFmpegComponent::VideoFFmpegComponent() { } -VideoFFmpegComponent::~VideoFFmpegComponent() { stopVideo(); } - void VideoFFmpegComponent::setResize(float width, float height) { // This resize function is used when stretching videos to full screen in the video screensaver. @@ -126,7 +124,9 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) if (!isVisible() || mThemeOpacity == 0.0f) return; - VideoComponent::render(parentTrans); + if (!mHasVideo && mStaticImagePath == "") + return; + glm::mat4 trans {parentTrans * getTransform()}; GuiComponent::renderChildren(trans); @@ -209,7 +209,8 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) pictureLock.unlock(); } - mTexture->bind(); + if (mTexture != nullptr) + mTexture->bind(); #if defined(USE_OPENGL_21) // Render scanlines if this option is enabled. However, if this is the media viewer @@ -239,7 +240,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) void VideoFFmpegComponent::updatePlayer() { - if (mPause || !mFormatContext) + if (mPaused || !mFormatContext) return; // Output any audio that has been added by the processing thread. @@ -282,7 +283,7 @@ void VideoFFmpegComponent::frameProcessing() if (mAudioCodecContext) audioFilter = setupAudioFilters(); - while (mIsPlaying && !mPause && videoFilter && (!mAudioCodecContext || audioFilter)) { + while (mIsPlaying && !mPaused && videoFilter && (!mAudioCodecContext || audioFilter)) { readFrames(); if (!mIsPlaying) break; @@ -1073,7 +1074,7 @@ bool VideoFFmpegComponent::decoderInitHW() AVCodecContext* checkCodecContext = avcodec_alloc_context3(mHardwareCodec); if (avcodec_parameters_to_context(checkCodecContext, mVideoStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::decoderInitHW(): " "Couldn't fill the video codec context parameters for file \"" << mVideoPath << "\""; avcodec_free_context(&checkCodecContext); @@ -1086,7 +1087,7 @@ bool VideoFFmpegComponent::decoderInitHW() checkCodecContext->hw_device_ctx = av_buffer_ref(mHwContext); if (avcodec_open2(checkCodecContext, mHardwareCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::decoderInitHW(): " "Couldn't initialize the video codec context for file \"" << mVideoPath << "\""; } @@ -1174,7 +1175,7 @@ bool VideoFFmpegComponent::decoderInitHW() mVideoCodecContext = avcodec_alloc_context3(mHardwareCodec); if (!mVideoCodecContext) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::decoderInitHW(): " "Couldn't allocate video codec context for file \"" << mVideoPath << "\""; avcodec_free_context(&mVideoCodecContext); @@ -1182,7 +1183,7 @@ bool VideoFFmpegComponent::decoderInitHW() } if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::decoderInitHW(): " "Couldn't fill the video codec context parameters for file \"" << mVideoPath << "\""; avcodec_free_context(&mVideoCodecContext); @@ -1193,7 +1194,7 @@ bool VideoFFmpegComponent::decoderInitHW() mVideoCodecContext->hw_device_ctx = av_buffer_ref(mHwContext); if (avcodec_open2(mVideoCodecContext, mHardwareCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::decoderInitHW(): " "Couldn't initialize the video codec context for file \"" << mVideoPath << "\""; avcodec_free_context(&mVideoCodecContext); @@ -1203,8 +1204,10 @@ bool VideoFFmpegComponent::decoderInitHW() return false; } -void VideoFFmpegComponent::startVideo() +void VideoFFmpegComponent::startVideoStream() { + mIsPlaying = true; + if (!mFormatContext) { mHardwareCodec = nullptr; mHwContext = nullptr; @@ -1240,14 +1243,14 @@ void VideoFFmpegComponent::startVideo() // File operations and basic setup. if (avformat_open_input(&mFormatContext, filePath.c_str(), nullptr, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't open video file \"" << mVideoPath << "\""; return; } if (avformat_find_stream_info(mFormatContext, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't read stream information from video file \"" << mVideoPath << "\""; return; @@ -1268,7 +1271,7 @@ void VideoFFmpegComponent::startVideo() av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &mHardwareCodec, 0); if (mVideoStreamIndex < 0) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't retrieve video stream for file \"" << mVideoPath << "\""; avformat_close_input(&mFormatContext); @@ -1280,7 +1283,7 @@ void VideoFFmpegComponent::startVideo() mVideoWidth = mFormatContext->streams[mVideoStreamIndex]->codecpar->width; mVideoHeight = mFormatContext->streams[mVideoStreamIndex]->codecpar->height; - LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): " + LOG(LogDebug) << "VideoFFmpegComponent::startVideoStream(): " << "Playing video \"" << mVideoPath << "\" (codec: " << avcodec_get_name( mFormatContext->streams[mVideoStreamIndex]->codecpar->codec_id) @@ -1294,15 +1297,16 @@ void VideoFFmpegComponent::startVideo() if (mSWDecoder) { // The hardware decoder initialization failed, which can happen for a number of reasons. if (hwDecoding) { - LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): Hardware decoding failed, " - "falling back to software decoder"; + LOG(LogDebug) + << "VideoFFmpegComponent::startVideoStream(): Hardware decoding failed, " + "falling back to software decoder"; } mVideoCodec = const_cast<AVCodec*>(avcodec_find_decoder(mVideoStream->codecpar->codec_id)); if (!mVideoCodec) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't find a suitable video codec for file \"" << mVideoPath << "\""; return; @@ -1311,7 +1315,7 @@ void VideoFFmpegComponent::startVideo() mVideoCodecContext = avcodec_alloc_context3(mVideoCodec); if (!mVideoCodecContext) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't allocate video codec context for file \"" << mVideoPath << "\""; return; @@ -1321,14 +1325,14 @@ void VideoFFmpegComponent::startVideo() mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED; if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't fill the video codec context parameters for file \"" << mVideoPath << "\""; return; } if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't initialize the video codec context for file \"" << mVideoPath << "\""; return; @@ -1341,7 +1345,7 @@ void VideoFFmpegComponent::startVideo() av_find_best_stream(mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (mAudioStreamIndex < 0) { - LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): " + LOG(LogDebug) << "VideoFFmpegComponent::startVideoStream(): " "File does not seem to contain any audio streams"; } @@ -1366,14 +1370,14 @@ void VideoFFmpegComponent::startVideo() mAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; if (avcodec_parameters_to_context(mAudioCodecContext, mAudioStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't fill the audio codec context parameters for file \"" << mVideoPath << "\""; return; } if (avcodec_open2(mAudioCodecContext, mAudioCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + LOG(LogError) << "VideoFFmpegComponent::startVideoStream(): " "Couldn't initialize the audio codec context for file \"" << mVideoPath << "\""; return; @@ -1402,17 +1406,17 @@ void VideoFFmpegComponent::startVideo() // Calculate pillarbox/letterbox sizes. calculateBlackRectangle(); - mIsPlaying = true; mFadeIn = 0.0f; } } -void VideoFFmpegComponent::stopVideo() +void VideoFFmpegComponent::stopVideoPlayer() { + muteVideoPlayer(); + mIsPlaying = false; mIsActuallyPlaying = false; - mStartDelayed = false; - mPause = false; + mPaused = false; mEndOfVideo = false; mTexture.reset(); @@ -1451,10 +1455,10 @@ void VideoFFmpegComponent::stopVideo() } } -void VideoFFmpegComponent::pauseVideo() +void VideoFFmpegComponent::pauseVideoPlayer() { - if (mPause && mWindow->getVideoPlayerCount() == 0) - AudioManager::getInstance().muteStream(); + muteVideoPlayer(); + mPaused = true; } void VideoFFmpegComponent::handleLooping() @@ -1467,8 +1471,16 @@ void VideoFFmpegComponent::handleLooping() mWindow->screensaverTriggerNextGame(); } else { - stopVideo(); - startVideo(); + stopVideoPlayer(); + startVideoStream(); } } } + +void VideoFFmpegComponent::muteVideoPlayer() +{ + if (AudioManager::sAudioDevice != 0) { + AudioManager::getInstance().clearStream(); + AudioManager::getInstance().muteStream(); + } +} diff --git a/es-core/src/components/VideoFFmpegComponent.h b/es-core/src/components/VideoFFmpegComponent.h index 06522b22d..ec3ef8459 100644 --- a/es-core/src/components/VideoFFmpegComponent.h +++ b/es-core/src/components/VideoFFmpegComponent.h @@ -33,7 +33,7 @@ class VideoFFmpegComponent : public VideoComponent { public: VideoFFmpegComponent(); - virtual ~VideoFFmpegComponent(); + virtual ~VideoFFmpegComponent() { stopVideoPlayer(); } // Resize the video to fit this size. If one axis is zero, scale that axis to maintain // aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are @@ -45,8 +45,17 @@ public: // This can be set before or after a video is loaded. // Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive. void setMaxSize(float width, float height) override; + // Basic video controls. + void stopVideoPlayer() override; + void pauseVideoPlayer() override; + // Handle looping of the video. Must be called periodically. + void handleLooping() override; + // Used to immediately mute audio even if there are samples to play in the buffer. + void muteVideoPlayer() override; private: + void startVideoStream() override; + // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). // Used internally whenever the resizing parameters or texture change. void resize(); @@ -74,15 +83,6 @@ private: static void detectHWDecoder(); bool decoderInitHW(); - // Start the video immediately. - void startVideo() override; - // Stop the video. - void stopVideo() override; - // Pause the video when a game has been launched. - void pauseVideo() override; - // Handle looping the video. Must be called periodically. - void handleLooping() override; - static enum AVHWDeviceType sDeviceType; static enum AVPixelFormat sPixelFormat; static std::vector<std::string> sSWDecodedVideos;