From 3ed17fbea299dada74961cf05b132442f5011d2d Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 16 May 2021 13:12:31 +0200 Subject: [PATCH] Added a fullscreen game media viewer. --- es-app/CMakeLists.txt | 4 + es-app/src/MediaViewer.cpp | 282 ++++++++++++++++++ es-app/src/MediaViewer.h | 56 ++++ es-app/src/guis/GuiMediaViewerOptions.cpp | 85 ++++++ es-app/src/guis/GuiMediaViewerOptions.h | 21 ++ es-app/src/guis/GuiMenu.cpp | 47 ++- es-app/src/guis/GuiMenu.h | 1 + es-app/src/guis/GuiScreensaverOptions.cpp | 13 - es-app/src/main.cpp | 2 + es-core/src/Settings.cpp | 10 +- es-core/src/Window.cpp | 45 ++- es-core/src/Window.h | 23 +- .../src/components/ScrollableContainer.cpp | 5 +- es-core/src/components/VideoComponent.cpp | 6 + es-core/src/components/VideoComponent.h | 8 +- .../src/components/VideoFFmpegComponent.cpp | 25 +- es-core/src/components/VideoVlcComponent.cpp | 50 +++- 17 files changed, 637 insertions(+), 46 deletions(-) create mode 100644 es-app/src/MediaViewer.cpp create mode 100644 es-app/src/MediaViewer.h create mode 100644 es-app/src/guis/GuiMediaViewerOptions.cpp create mode 100644 es-app/src/guis/GuiMediaViewerOptions.h diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index a38b61316..c3e7b0c80 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -7,6 +7,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FileSorts.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/MediaViewer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/MetaData.h ${CMAKE_CURRENT_SOURCE_DIR}/src/PlatformId.h ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.h @@ -19,6 +20,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistOptions.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInfoPopup.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMediaViewerOptions.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMenu.h @@ -55,6 +57,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/FileSorts.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/MediaViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/MetaData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/PlatformId.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemData.cpp @@ -68,6 +71,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInfoPopup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMediaViewerOptions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.cpp diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp new file mode 100644 index 000000000..62846aff3 --- /dev/null +++ b/es-app/src/MediaViewer.cpp @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// MediaViewer.cpp +// +// Fullscreen game media viewer. +// + +#include "MediaViewer.h" + +#include "components/VideoFFmpegComponent.h" +#include "components/VideoVlcComponent.h" +#include "views/ViewController.h" +#include "AudioManager.h" +#include "Sound.h" + +MediaViewer::MediaViewer(Window* window) : mWindow(window), mVideo(nullptr), mImage(nullptr) +{ + mWindow->setMediaViewer(this); +} + +MediaViewer::~MediaViewer() +{ + if (mVideo) { + delete mVideo; + mVideo = nullptr; + } + if (mImage) { + delete mImage; + mImage = nullptr; + } +} + +bool MediaViewer::startMediaViewer(FileData* game) +{ + mHasVideo = false; + mHasImages = false; + mCurrentImageIndex = 0; + mScreenShotIndex = -1; + + mGame = game; + + initiateViewer(); + + if (mHasVideo) { + ViewController::get()->onPauseVideo(); + AudioManager::getInstance()->clearStream(); + } + + if (mHasVideo || mHasImages) + return true; + else + return false; +} + +void MediaViewer::stopMediaViewer() +{ + NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); + + if (mVideo) { + ViewController::get()->onStopVideo(); + AudioManager::getInstance()->clearStream(); + delete mVideo; + mVideo = nullptr; + } + + if (mImage) { + delete mImage; + mImage = nullptr; + } + + mVideoFile = ""; + mImageFiles.clear(); +} + +void MediaViewer::update(int deltaTime) +{ + if (mVideo) + mVideo->update(deltaTime); +} + +void MediaViewer::render() +{ + Transform4x4f transform = Transform4x4f::Identity(); + Renderer::setMatrix(transform); + + // Render a black background below the game media. + Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + + if (mVideo && !mDisplayingImage) { + mVideo->render(transform); + + #if defined(USE_OPENGL_21) + Renderer::shaderParameters videoParameters; + unsigned int shaders = 0; + if (Settings::getInstance()->getBool("MediaViewerVideoScanlines")) + shaders = Renderer::SHADER_SCANLINES; + if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) { + shaders |= Renderer::SHADER_BLUR_HORIZONTAL; + float heightModifier = Renderer::getScreenHeightModifier(); + if (heightModifier < 1) + videoParameters.blurPasses = 2; // Below 1080 + else if (heightModifier >= 4) + videoParameters.blurPasses = 12; // 8K + else if (heightModifier >= 2.9) + videoParameters.blurPasses = 10; // 6K + else if (heightModifier >= 2.6) + videoParameters.blurPasses = 8; // 5K + else if (heightModifier >= 2) + videoParameters.blurPasses = 5; // 4K + else if (heightModifier >= 1.3) + videoParameters.blurPasses = 3; // 1440 + else if (heightModifier >= 1) + videoParameters.blurPasses = 2; // 1080 + } + Renderer::shaderPostprocessing(shaders, videoParameters); + #endif + } + else if (mImage && mImage->hasImage()) { + mImage->render(transform); + + #if defined(USE_OPENGL_21) + if (mCurrentImageIndex == mScreenShotIndex && + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) + Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); + #endif + + // This is necessary so that the video loops if viewing an image when + // the video ends. + if (mVideo) + mVideo->handleLooping(); + } +} + +void MediaViewer::initiateViewer() +{ + if (mGame->getType() == PLACEHOLDER) + return; + + findMedia(); + + if (!mHasVideo && !mHasImages) { + NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); + return; + } + + if (mHasVideo) + playVideo(); + else + showImage(0); +} + +void MediaViewer::findMedia() +{ + std::string mediaFile; + + if ((mediaFile = mGame->getVideoPath()) != "") { + mVideoFile = mediaFile; + mHasVideo = true; + } + + if (!mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") { + mImageFiles.push_back(mediaFile); + mScreenShotIndex = 0; + } + + if ((mediaFile = mGame->getCoverPath()) != "") + mImageFiles.push_back(mediaFile); + + if (mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") { + mImageFiles.push_back(mediaFile); + mScreenShotIndex = mImageFiles.size() - 1; + } + + if ((mediaFile = mGame->get3DBoxPath()) != "") + mImageFiles.push_back(mediaFile); + + if (!mImageFiles.empty()) + mHasImages = true; +} + +void MediaViewer::showNext() +{ + NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); + + bool showedVideo = false; + + if (mVideo && !mHasImages) { + return; + } + else if (mVideo && !Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) { + AudioManager::getInstance()->clearStream(); + delete mVideo; + mVideo = nullptr; + showedVideo = true; + } + + if (mImage) { + delete mImage; + mImage = nullptr; + } + + if ((mVideo || showedVideo) && !mDisplayingImage) + mCurrentImageIndex = 0; + else if (mImageFiles.size() > mCurrentImageIndex + 1) + mCurrentImageIndex++; + + if (mVideo) + mDisplayingImage = true; + + showImage(mCurrentImageIndex); +} + +void MediaViewer::showPrevious() +{ + NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); + + if (mCurrentImageIndex == 0 && !mHasVideo) { + return; + } + else if (mCurrentImageIndex == 0 && mHasVideo) { + if (mImage) { + delete mImage; + mImage = nullptr; + } + mDisplayingImage = false; + playVideo(); + return; + } + + if (mImage) { + delete mImage; + mImage = nullptr; + } + + mCurrentImageIndex--; + showImage(mCurrentImageIndex); +} + +void MediaViewer::playVideo() +{ + if (mVideo || mVideoFile == "") + return; + + mDisplayingImage = false; + + if (Settings::getInstance()->getString("VideoPlayer") == "ffmpeg") + mVideo = new VideoFFmpegComponent(mWindow); + else + mVideo = new VideoVlcComponent(mWindow); + + mVideo->topWindow(true); + mVideo->setOrigin(0.5f, 0.5f); + mVideo->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); + + if (Settings::getInstance()->getBool("MediaViewerStretchVideos")) + mVideo->setResize(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); + else + mVideo->setMaxSize(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); + + mVideo->setVideo(mVideoFile); + mVideo->setMediaViewerMode(true); + mVideo->onShow(); +} + +void MediaViewer::showImage(int index) +{ + if (mImage) + return; + + if (!mImageFiles.empty() && mImageFiles.size() >= index) { + mImage = new ImageComponent(mWindow, false, false); + mImage->setImage(mImageFiles[index]); + mImage->setOrigin(0.5f, 0.5f); + mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); + mImage->setMaxSize(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); + } +} diff --git a/es-app/src/MediaViewer.h b/es-app/src/MediaViewer.h new file mode 100644 index 000000000..fb6922851 --- /dev/null +++ b/es-app/src/MediaViewer.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// MediaViewer.h +// +// Fullscreen game media viewer. +// + +#ifndef ES_APP_MEDIA_VIEWER_H +#define ES_APP_MEDIA_VIEWER_H + +#include "components/ImageComponent.h" +#include "components/VideoComponent.h" +#include "FileData.h" +#include "Window.h" + +class MediaViewer : public Window::MediaViewer +{ +public: + MediaViewer(Window* window); + virtual ~MediaViewer(); + + virtual bool startMediaViewer(FileData* game); + virtual void stopMediaViewer(); + + virtual void update(int deltaTime); + virtual void render(); + +private: + void initiateViewer(); + void findMedia(); + + void playVideo(); + void showImage(int index); + + virtual void showNext(); + virtual void showPrevious(); + + Window* mWindow; + FileData* mGame; + + bool mHasVideo; + bool mHasImages; + bool mDisplayingImage; + + int mCurrentImageIndex; + int mScreenShotIndex; + + std::string mVideoFile; + std::vector mImageFiles; + + VideoComponent* mVideo; + ImageComponent* mImage; +}; + +#endif // ES_APP_MEDIA_VIEWER_H diff --git a/es-app/src/guis/GuiMediaViewerOptions.cpp b/es-app/src/guis/GuiMediaViewerOptions.cpp new file mode 100644 index 000000000..76aadb7f6 --- /dev/null +++ b/es-app/src/guis/GuiMediaViewerOptions.cpp @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// GuiMediaViewerOptions.cpp +// +// User interface for the media viewer options. +// Submenu to the GuiMenu main menu. +// + +#include "guis/GuiMediaViewerOptions.h" + +#include "components/SwitchComponent.h" +#include "Settings.h" + +GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& title) + : GuiSettings(window, title) +{ + // Keep videos running when viewing images. + auto keep_video_running = std::make_shared(mWindow); + keep_video_running->setState(Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")); + addWithLabel("KEEP VIDEOS RUNNING WHEN VIEWING IMAGES", keep_video_running); + addSaveFunc([keep_video_running, this] { + if (keep_video_running->getState() != + Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) { + Settings::getInstance()->setBool("MediaViewerKeepVideoRunning", + keep_video_running->getState()); + setNeedsSaving(); + } + }); + + // Stretch videos to screen resolution. + auto stretch_videos = std::make_shared(mWindow); + stretch_videos->setState(Settings::getInstance()->getBool("MediaViewerStretchVideos")); + addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", stretch_videos); + addSaveFunc([stretch_videos, this] { + if (stretch_videos->getState() != + Settings::getInstance()->getBool("MediaViewerStretchVideos")) { + Settings::getInstance()->setBool("MediaViewerStretchVideos", + stretch_videos->getState()); + setNeedsSaving(); + } + }); + + #if defined(USE_OPENGL_21) + // Render scanlines for videos using a shader. + auto video_scanlines = std::make_shared(mWindow); + video_scanlines->setState(Settings::getInstance()->getBool("MediaViewerVideoScanlines")); + addWithLabel("RENDER SCANLINES FOR VIDEOS", video_scanlines); + addSaveFunc([video_scanlines, this] { + if (video_scanlines->getState() != + Settings::getInstance()->getBool("MediaViewerVideoScanlines")) { + Settings::getInstance()->setBool("MediaViewerVideoScanlines", + video_scanlines->getState()); + setNeedsSaving(); + } + }); + + // Render blur for videos using a shader. + auto video_blur = std::make_shared(mWindow); + video_blur->setState(Settings::getInstance()->getBool("MediaViewerVideoBlur")); + addWithLabel("RENDER BLUR FOR VIDEOS", video_blur); + addSaveFunc([video_blur, this] { + if (video_blur->getState() != + Settings::getInstance()->getBool("MediaViewerVideoBlur")) { + Settings::getInstance()->setBool("MediaViewerVideoBlur", + video_blur->getState()); + setNeedsSaving(); + } + }); + + // Render scanlines for screenshots using a shader. + auto screenshot_scanlines = std::make_shared(mWindow); + screenshot_scanlines->setState(Settings::getInstance()-> + getBool("MediaViewerScreenshotScanlines")); + addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines); + addSaveFunc([screenshot_scanlines, this] { + if (screenshot_scanlines->getState() != + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) { + Settings::getInstance()->setBool("MediaViewerScreenshotScanlines", + screenshot_scanlines->getState()); + setNeedsSaving(); + } + }); + #endif +} diff --git a/es-app/src/guis/GuiMediaViewerOptions.h b/es-app/src/guis/GuiMediaViewerOptions.h new file mode 100644 index 000000000..5b5b0a834 --- /dev/null +++ b/es-app/src/guis/GuiMediaViewerOptions.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// GuiMediaViewerOptions.h +// +// User interface for the media viewer options. +// Submenu to the GuiMenu main menu. +// + +#ifndef ES_APP_GUIS_GUI_MEDIA_VIEWER_OPTIONS_H +#define ES_APP_GUIS_GUI_MEDIA_VIEWER_OPTIONS_H + +#include "guis/GuiSettings.h" + +class GuiMediaViewerOptions : public GuiSettings +{ +public: + GuiMediaViewerOptions(Window* window, const std::string& title); +}; + +#endif // ES_APP_GUIS_GUI_MEDIA_VIEWER_OPTIONS_H diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 09d934c2f..8144d2e87 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -15,9 +15,10 @@ #include "guis/GuiCollectionSystemsOptions.h" #include "guis/GuiComplexTextEditPopup.h" #include "guis/GuiDetectDevice.h" -#include "guis/GuiScreensaverOptions.h" +#include "guis/GuiMediaViewerOptions.h" #include "guis/GuiMsgBox.h" #include "guis/GuiScraperMenu.h" +#include "guis/GuiScreensaverOptions.h" #include "guis/GuiSettings.h" #include "views/gamelist/IGameListView.h" #include "views/UIModeController.h" @@ -504,6 +505,15 @@ void GuiMenu::openUISettings() } }); + // Media viewer. + ComponentListRow media_viewer_row; + media_viewer_row.elements.clear(); + media_viewer_row.addElement(std::make_shared + (mWindow, "MEDIA VIEWER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + media_viewer_row.addElement(makeArrow(mWindow), false); + media_viewer_row.makeAcceptInputHandler(std::bind(&GuiMenu::openMediaViewerOptions, this)); + s->addRow(media_viewer_row); + // Screensaver. ComponentListRow screensaver_row; screensaver_row.elements.clear(); @@ -668,7 +678,7 @@ void GuiMenu::openSoundSettings() }); #endif - // Video audio. + // Play audio for gamelist videos. auto gamelist_video_audio = std::make_shared(mWindow); gamelist_video_audio->setState(Settings::getInstance()->getBool("GamelistVideoAudio")); s->addWithLabel("PLAY AUDIO FOR VIDEOS IN THE GAMELIST VIEW", gamelist_video_audio); @@ -681,6 +691,34 @@ void GuiMenu::openSoundSettings() } }); + // Play audio for media viewer videos. + auto media_viewer_video_audio = std::make_shared(mWindow); + media_viewer_video_audio->setState(Settings::getInstance()-> + getBool("MediaViewerVideoAudio")); + s->addWithLabel("PLAY AUDIO FOR MEDIA VIEWER VIDEOS", media_viewer_video_audio); + s->addSaveFunc([media_viewer_video_audio, s] { + if (media_viewer_video_audio->getState() != + Settings::getInstance()->getBool("MediaViewerVideoAudio")) { + Settings::getInstance()->setBool("MediaViewerVideoAudio", + media_viewer_video_audio->getState()); + s->setNeedsSaving(); + } + }); + + // Play audio for screensaver videos. + auto screensaver_video_audio = std::make_shared(mWindow); + screensaver_video_audio->setState(Settings::getInstance()-> + getBool("ScreensaverVideoAudio")); + s->addWithLabel("PLAY AUDIO FOR SCREENSAVER VIDEOS", screensaver_video_audio); + s->addSaveFunc([screensaver_video_audio, s] { + if (screensaver_video_audio->getState() != + Settings::getInstance()->getBool("ScreensaverVideoAudio")) { + Settings::getInstance()->setBool("ScreensaverVideoAudio", + screensaver_video_audio->getState()); + s->setNeedsSaving(); + } + }); + // Navigation sounds. auto navigation_sounds = std::make_shared(mWindow); navigation_sounds->setState(Settings::getInstance()-> @@ -1130,6 +1168,11 @@ void GuiMenu::addVersionInfo() addChild(&mVersion); } +void GuiMenu::openMediaViewerOptions() +{ + mWindow->pushGui(new GuiMediaViewerOptions(mWindow, "MEDIA VIEWER SETTINGS")); +} + void GuiMenu::openScreensaverOptions() { mWindow->pushGui(new GuiScreensaverOptions(mWindow, "SCREENSAVER SETTINGS")); diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index 3e5022ad0..5b35d2804 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -32,6 +32,7 @@ private: void openScraperSettings(); void openUISettings(); + void openMediaViewerOptions(); void openScreensaverOptions(); void openSoundSettings(); void openCollectionSystemSettings(); diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index a488c6c65..3c891d4ed 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -225,19 +225,6 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() } }); - // PLay audio for screensaver videos. - auto screensaver_video_audio = std::make_shared(mWindow); - screensaver_video_audio->setState(Settings::getInstance()->getBool("ScreensaverVideoAudio")); - s->addWithLabel("PLAY AUDIO FOR SCREENSAVER VIDEOS", screensaver_video_audio); - s->addSaveFunc([screensaver_video_audio, s] { - if (screensaver_video_audio->getState() != - Settings::getInstance()->getBool("ScreensaverVideoAudio")) { - Settings::getInstance()->setBool("ScreensaverVideoAudio", - screensaver_video_audio->getState()); - s->setNeedsSaving(); - } - }); - // Stretch videos to screen resolution. auto screensaver_stretch_videos = std::make_shared(mWindow); screensaver_stretch_videos-> diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index b2150e198..e2fcd48f6 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -30,6 +30,7 @@ #include "InputManager.h" #include "Log.h" #include "MameNames.h" +#include "MediaViewer.h" #include "Platform.h" #include "Settings.h" #include "Sound.h" @@ -477,6 +478,7 @@ int main(int argc, char* argv[]) Window window; SystemScreensaver screensaver(&window); + MediaViewer mediaViewer(&window); ViewController::init(&window); CollectionSystemsManager::init(&window); window.pushGui(ViewController::get()); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index fab4cacd5..3df09a059 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -139,6 +139,13 @@ void Settings::setDefaults() mBoolMap["PlayVideosImmediately"] = { false, false }; mBoolMap["EnableMenuKidMode"] = { false, false }; + // UI settings -> media viewer settings. + mBoolMap["MediaViewerKeepVideoRunning"] = { true, true }; + mBoolMap["MediaViewerStretchVideos"] = { false, false }; + mBoolMap["MediaViewerVideoScanlines"] = { true, true }; + mBoolMap["MediaViewerVideoBlur"] = { false, false }; + mBoolMap["MediaViewerScreenshotScanlines"] = { true, true }; + // UI settings -> screensaver settings. mIntMap["ScreensaverTimer"] = { 5*60*1000, 5*60*1000 }; // 5 minutes mStringMap["ScreensaverType"] = { "dim", "dim" }; @@ -157,7 +164,6 @@ void Settings::setDefaults() // UI settings -> screensaver settings -> video screensaver settings. mIntMap["ScreensaverSwapVideoTimeout"] = { 0, 0 }; - mBoolMap["ScreensaverVideoAudio"] = { false, false }; mBoolMap["ScreensaverStretchVideos"] = { false, false }; mBoolMap["ScreensaverVideoGameInfo"] = { true, true }; mBoolMap["ScreensaverVideoScanlines"] = { true, true }; @@ -185,6 +191,8 @@ void Settings::setDefaults() mIntMap["SoundVolumeNavigation"] = { 80, 80 }; mIntMap["SoundVolumeVideos"] = { 100, 100 }; mBoolMap["GamelistVideoAudio"] = { true, true }; + mBoolMap["MediaViewerVideoAudio"] = { true, true }; + mBoolMap["ScreensaverVideoAudio"] = { false, false }; mBoolMap["NavigationSounds"] = { true, true }; // Game collection settings. diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 51d9e30e0..bab3ce712 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -24,6 +24,7 @@ Window::Window() : mScreensaver(nullptr), + mMediaViewer(nullptr), mInfoPopup(nullptr), mNormalizeNextUpdate(false), mFrameTimeElapsed(0), @@ -33,6 +34,7 @@ Window::Window() mSleeping(false), mTimeSinceLastInput(0), mRenderScreensaver(false), + mRenderMediaViewer(false), mGameLaunchedState(false), mAllowTextScrolling(true), mCachedBackground(false), @@ -151,6 +153,17 @@ void Window::input(InputConfig* config, Input input) if (Settings::getInstance()->getBool("Debug")) logInput(config, input); + if (mMediaViewer && mRenderMediaViewer) { + if (config->isMappedLike("right", input) && input.value != 0) + mMediaViewer->showNext(); + else if (config->isMappedLike("left", input) && input.value != 0) + mMediaViewer->showPrevious(); + else if (input.value != 0) + // Any other input than left or right stops the media viewer. + stopMediaViewer(); + return; + } + if (mScreensaver) { if (mScreensaver->isScreensaverActive() && Settings::getInstance()->getBool("ScreensaverControls") && @@ -303,8 +316,10 @@ void Window::update(int deltaTime) mChangedThemeSet = false; } - // Update the screensaver. - if (mScreensaver) + if (mMediaViewer && mRenderMediaViewer) + mMediaViewer->update(deltaTime); + + if (mScreensaver && mRenderScreensaver) mScreensaver->update(deltaTime); } @@ -319,7 +334,7 @@ void Window::render() auto& bottom = mGuiStack.front(); auto& top = mGuiStack.back(); - if (mRenderScreensaver) { + if (mRenderMediaViewer || mRenderScreensaver) { bottom->cancelAllAnimations(); bottom->stopAllAnimations(); } @@ -464,8 +479,9 @@ void Window::render() unsigned int screensaverTimer = static_cast(Settings::getInstance()->getInt("ScreensaverTimer")); if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) { - // If a menu is open, reset the screensaver timer so that the screensaver won't start. - if (mGuiStack.front() != mGuiStack.back()) + // If the media viewer is running or if a menu is open, reset the screensaver timer so + // that the screensaver won't start. + if (mRenderMediaViewer || mGuiStack.front() != mGuiStack.back()) mTimeSinceLastInput = 0; // If a game has been launched, reset the screensaver timer as we don't want to start // the screensaver in the background when running a game. @@ -492,6 +508,9 @@ void Window::render() } } + if (mRenderMediaViewer) + mMediaViewer->render(); + if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) { Renderer::setMatrix(Transform4x4f::Identity()); mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get()); @@ -711,3 +730,19 @@ void Window::renderScreensaver() if (mScreensaver) mScreensaver->renderScreensaver(); } + +void Window::startMediaViewer(FileData* game) +{ + if (mMediaViewer) { + if (mMediaViewer->startMediaViewer(game)) + mRenderMediaViewer = true; + } +} + +void Window::stopMediaViewer() +{ + if (mMediaViewer) + mMediaViewer->stopMediaViewer(); + + mRenderMediaViewer = false; +} diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 32d3c2560..dd452da32 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -50,6 +50,19 @@ public: virtual void triggerNextGame() = 0; }; + class MediaViewer + { + public: + virtual bool startMediaViewer(FileData* game) = 0; + virtual void stopMediaViewer() = 0; + + virtual void showNext() = 0; + virtual void showPrevious() = 0; + + virtual void update(int deltaTime) = 0; + virtual void render() = 0; + }; + class InfoPopup { public: @@ -92,11 +105,16 @@ public: void setInfoPopup(InfoPopup* infoPopup); void stopInfoPopup(); - bool isScreensaverActive() { return mRenderScreensaver; }; void startScreensaver(); bool stopScreensaver(); void renderScreensaver(); void screensaverTriggerNextGame() { mScreensaver->triggerNextGame(); }; + bool isScreensaverActive() { return mRenderScreensaver; }; + + void startMediaViewer(FileData* game); + void stopMediaViewer(); + void setMediaViewer(MediaViewer* mediaViewer) { mMediaViewer = mediaViewer; } + bool isMediaViewerActive() { return mRenderMediaViewer; }; void setLaunchedGame(); void unsetLaunchedGame(); @@ -124,6 +142,9 @@ private: std::vector> mDefaultFonts; std::unique_ptr mFrameDataText; + MediaViewer* mMediaViewer; + bool mRenderMediaViewer; + std::string mListScrollText; std::shared_ptr mListScrollFont; unsigned char mListScrollOpacity; diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index ffad86046..f5a3805aa 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -88,8 +88,9 @@ void ScrollableContainer::reset() void ScrollableContainer::update(int deltaTime) { - // Don't scroll if the screensaver is active or text scrolling is disabled; - if (mWindow->isScreensaverActive() || !mWindow->getAllowTextScrolling()) { + // Don't scroll if the media viewer or screensaver is active or if text scrolling is disabled; + if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() || + !mWindow->getAllowTextScrolling()) { if (mScrollPos != 0) reset(); return; diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 36154bcaa..eff542dbd 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -30,6 +30,7 @@ VideoComponent::VideoComponent( mPause(false), mShowing(false), mDisable(false), + mMediaViewerMode(false), mScreensaverActive(false), mScreensaverMode(false), mGameLaunched(false), @@ -97,6 +98,11 @@ void VideoComponent::setScreensaverMode(bool isScreensaver) mScreensaverMode = isScreensaver; } +void VideoComponent::setMediaViewerMode(bool isMediaViewer) +{ + mMediaViewerMode = isMediaViewer; +} + void VideoComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 26d54be2f..56201ec02 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -14,6 +14,7 @@ #include +class MediaViewer; class TextureResource; class VideoComponent : public GuiComponent @@ -36,7 +37,9 @@ public: void setDefaultVideo(); // Loads a static image that is displayed if the video cannot be played. void setImage(std::string path); - // Sets whether it's going to render in screensaver mode. + // Sets whether we're in media viewer mode. + void setMediaViewerMode(bool isMediaViewer); + // Sets whether we're in screensaver mode. void setScreensaverMode(bool isScreensaver); // Set the opacity for the embedded static image. void setOpacity(unsigned char opacity) override; @@ -98,6 +101,8 @@ private: // Manage the playing state of the component. void manageState(); + friend MediaViewer; + protected: Window* mWindow; unsigned mVideoWidth; @@ -118,6 +123,7 @@ protected: bool mPause; bool mShowing; bool mDisable; + bool mMediaViewerMode; bool mScreensaverActive; bool mScreensaverMode; bool mGameLaunched; diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index bffad8cfc..01be0113d 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -158,10 +158,11 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans) mTexture->bind(); #if defined(USE_OPENGL_21) - // Render scanlines if this option is enabled. However, if this is the video - // screensaver, then skip this as screensaver scanline rendering is handled in - // SystemScreenSaver as a postprocessing step. - if (!mScreensaverMode && Settings::getInstance()->getBool("GamelistVideoScanlines")) + // Render scanlines if this option is enabled. However, if this is the media viewer + // or the video screensaver, then skip this as the scanline rendering is then handled + // in those modules as a postprocessing step. + if ((!mScreensaverMode && !mMediaViewerMode) && + Settings::getInstance()->getBool("GamelistVideoScanlines")) vertices[0].shaders = Renderer::SHADER_SCANLINES; #endif @@ -466,9 +467,19 @@ void VideoFFmpegComponent::outputFrames() // LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " << // mAudioFrameCount << " / " << std::to_string(mAudioFrameQueue.size()); - if ((Settings::getInstance()->getBool("GamelistVideoAudio") && - !mScreensaverMode) || (Settings::getInstance()-> - getBool("ScreensaverVideoAudio") && mScreensaverMode)) { + bool outputSound = false; + + if ((!mScreensaverMode && !mMediaViewerMode) && + Settings::getInstance()->getBool("GamelistVideoAudio")) + outputSound = true; + else if (mScreensaverMode && Settings::getInstance()-> + getBool("ScreensaverVideoAudio")) + outputSound = true; + else if (mMediaViewerMode && Settings::getInstance()-> + getBool("MediaViewerVideoAudio")) + outputSound = true; + + if (outputSound) { AudioManager::getInstance()->processStream( &mAudioFrameQueue.front().resampledData.at(0), mAudioFrameQueue.front().resampledDataSize); diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp index 8438bbbe7..c2316ecf4 100644 --- a/es-core/src/components/VideoVlcComponent.cpp +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -225,10 +225,11 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans) mTexture->bind(); #if defined(USE_OPENGL_21) - // Render scanlines if this option is enabled. However, if this is the video - // screensaver, then skip this as screensaver scanline rendering is handled in - // SystemScreenSaver as a postprocessing step. - if (!mScreensaverMode && Settings::getInstance()->getBool("GamelistVideoScanlines")) + // Render scanlines if this option is enabled. However, if this is the media viewer + // or the video screensaver, then skip this as the scanline rendering is then handled + // in those modules as a postprocessing step. + if ((!mScreensaverMode && !mMediaViewerMode) && + Settings::getInstance()->getBool("GamelistVideoScanlines")) vertices[0].shaders = Renderer::SHADER_SCANLINES; #endif @@ -303,18 +304,29 @@ void VideoVlcComponent::setAudioVolume() if (mMediaPlayer && libvlc_media_player_get_state(mMediaPlayer) == libvlc_Playing) { // This small delay may avoid a race condition in libVLC that could crash the application. SDL_Delay(2); - if ((!Settings::getInstance()->getBool("GamelistVideoAudio") && - !mScreensaverMode) || - (!Settings::getInstance()->getBool("ScreensaverVideoAudio") && - mScreensaverMode)) { - libvlc_audio_set_volume(mMediaPlayer, 0); - } - else { + + bool outputSound = false; + + if ((!mScreensaverMode && !mMediaViewerMode) && + Settings::getInstance()->getBool("GamelistVideoAudio")) + outputSound = true; + else if (mScreensaverMode && Settings::getInstance()-> + getBool("ScreensaverVideoAudio")) + outputSound = true; + else if (mMediaViewerMode && Settings::getInstance()-> + getBool("MediaViewerVideoAudio")) + outputSound = true; + + if (outputSound) { if (libvlc_audio_get_mute(mMediaPlayer) == 1) libvlc_audio_set_mute(mMediaPlayer, 0); libvlc_audio_set_volume(mMediaPlayer, Settings::getInstance()->getInt("SoundVolumeVideos")); } + else { + libvlc_audio_set_volume(mMediaPlayer, 0); + } + mHasSetAudioVolume = true; } } @@ -510,9 +522,19 @@ void VideoVlcComponent::handleLooping() libvlc_media_player_set_media(mMediaPlayer, mMedia); libvlc_media_player_play(mMediaPlayer); - if ((!Settings::getInstance()->getBool("GamelistVideoAudio") && - !mScreensaverMode) || (!Settings::getInstance()-> - getBool("ScreensaverVideoAudio") && mScreensaverMode)) + bool outputSound = false; + + if ((!mScreensaverMode && !mMediaViewerMode) && + Settings::getInstance()->getBool("GamelistVideoAudio")) + outputSound = true; + else if (mScreensaverMode && Settings::getInstance()-> + getBool("ScreensaverVideoAudio")) + outputSound = true; + else if (mMediaViewerMode && Settings::getInstance()-> + getBool("MediaViewerVideoAudio")) + outputSound = true; + + if (!outputSound) libvlc_audio_set_volume(mMediaPlayer, 0); } }