From ab39b3759ed5e37866315ae5822b94ae765770e1 Mon Sep 17 00:00:00 2001 From: Leon Styhre <leon@leonstyhre.com> Date: Tue, 9 Jul 2024 21:42:24 +0200 Subject: [PATCH] Dramatically improved start times for the video and slideshow screensavers on devices with poor disk I/O performance --- es-app/src/FileData.h | 8 ++-- es-app/src/Screensaver.cpp | 86 +++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index 23d654a64..1da662861 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -154,6 +154,10 @@ public: const std::string& getSortTypeString() const { return mSortTypeString; } const FileData::SortType& getSortTypeFromString(const std::string& desc) const; + static inline std::vector<std::string> sImageExtensions {".png", ".jpg"}; + static inline std::vector<std::string> sVideoExtensions {".mp4", ".mkv", ".avi", + ".mp4", ".wmv", ".mov"}; + protected: FileData* mSourceFileData; FileData* mParent; @@ -171,9 +175,7 @@ private: std::vector<FileData*> mChildrenLastPlayed; std::vector<FileData*> mChildrenMostPlayed; std::function<void()> mUpdateListCallback; - static inline std::vector<std::string> sImageExtensions {".png", ".jpg"}; - static inline std::vector<std::string> sVideoExtensions {".mp4", ".mkv", ".avi", - ".mp4", ".wmv", ".mov"}; + // The pair includes all games, and favorite games. std::pair<unsigned int, unsigned int> mGameCount; bool mOnlyFolders; diff --git a/es-app/src/Screensaver.cpp b/es-app/src/Screensaver.cpp index be15e5088..2184a3a34 100644 --- a/es-app/src/Screensaver.cpp +++ b/es-app/src/Screensaver.cpp @@ -418,6 +418,32 @@ void Screensaver::generateImageList() if (!(*it)->isGameSystem() || (*it)->isCollection()) continue; + // This method of building an inventory of all image files isn't pretty, but to use the + // FileData::getImagePath() function leads to unacceptable performance issues on some + // platforms like Android that offer very poor disk I/O performance. To instead list + // all files recursively is much faster as this avoids stat() function calls which are + // very expensive on such problematic platforms. + const std::string mediaDirMiximages { + FileData::getMediaDirectory() + (*it)->getRootFolder()->getSystemName() + "/miximages"}; + const std::string mediaDirScreenshots {FileData::getMediaDirectory() + + (*it)->getRootFolder()->getSystemName() + + "/screenshots"}; + const std::string mediaDirTitlescreens {FileData::getMediaDirectory() + + (*it)->getRootFolder()->getSystemName() + + "/titlescreens"}; + const std::string mediaDirCovers {FileData::getMediaDirectory() + + (*it)->getRootFolder()->getSystemName() + "/covers"}; + const Utils::FileSystem::StringList dirContentMiximages { + Utils::FileSystem::getDirContent(mediaDirMiximages, true)}; + const Utils::FileSystem::StringList dirContentScreenshots { + Utils::FileSystem::getDirContent(mediaDirScreenshots, true)}; + const Utils::FileSystem::StringList dirContentTitlescreens { + Utils::FileSystem::getDirContent(mediaDirTitlescreens, true)}; + const Utils::FileSystem::StringList dirContentCovers { + Utils::FileSystem::getDirContent(mediaDirCovers, true)}; + + std::string subFolders; + std::vector<FileData*> allFiles {(*it)->getRootFolder()->getFilesRecursive(GAME, true)}; for (auto it2 = allFiles.cbegin(); it2 != allFiles.cend(); ++it2) { // Only include games suitable for children if we're in Kid UI mode. @@ -426,9 +452,36 @@ void Screensaver::generateImageList() continue; if (favoritesOnly && (*it2)->metadata.get("favorite") != "true") continue; - std::string imagePath {(*it2)->getImagePath()}; - if (imagePath != "") - mImageFiles.push_back((*it2)); + + subFolders = Utils::String::replace(Utils::FileSystem::getParent((*it2)->getPath()), + (*it)->getStartPath(), ""); + const std::string gamePath {subFolders + "/" + (*it2)->getDisplayName()}; + + for (auto& extension : FileData::sImageExtensions) { + if (std::find(dirContentMiximages.cbegin(), dirContentMiximages.cend(), + mediaDirMiximages + gamePath + extension) != + dirContentMiximages.cend()) { + mImageFiles.push_back((*it2)); + break; + } + if (std::find(dirContentScreenshots.cbegin(), dirContentScreenshots.cend(), + mediaDirScreenshots + gamePath + extension) != + dirContentScreenshots.cend()) { + mImageFiles.push_back((*it2)); + break; + } + if (std::find(dirContentTitlescreens.cbegin(), dirContentTitlescreens.cend(), + mediaDirTitlescreens + gamePath + extension) != + dirContentTitlescreens.cend()) { + mImageFiles.push_back((*it2)); + break; + } + if (std::find(dirContentCovers.cbegin(), dirContentCovers.cend(), + mediaDirCovers + gamePath + extension) != dirContentCovers.cend()) { + mImageFiles.push_back((*it2)); + break; + } + } } } @@ -445,6 +498,18 @@ void Screensaver::generateVideoList() if (!(*it)->isGameSystem() || (*it)->isCollection()) continue; + // This method of building an inventory of all video files isn't pretty, but to use the + // FileData::getVideoPath() function leads to unacceptable performance issues on some + // platforms like Android that offer very poor disk I/O performance. To instead list + // all files recursively is much faster as this avoids stat() function calls which are + // very expensive on such problematic platforms. + const std::string mediaDir {FileData::getMediaDirectory() + + (*it)->getRootFolder()->getSystemName() + "/videos"}; + const Utils::FileSystem::StringList dirContent { + Utils::FileSystem::getDirContent(mediaDir, true)}; + + std::string subFolders; + std::vector<FileData*> allFiles {(*it)->getRootFolder()->getFilesRecursive(GAME, true)}; for (auto it2 = allFiles.cbegin(); it2 != allFiles.cend(); ++it2) { // Only include games suitable for children if we're in Kid UI mode. @@ -453,9 +518,18 @@ void Screensaver::generateVideoList() continue; if (favoritesOnly && (*it2)->metadata.get("favorite") != "true") continue; - std::string videoPath {(*it2)->getVideoPath()}; - if (videoPath != "") - mVideoFiles.push_back((*it2)); + + subFolders = Utils::String::replace(Utils::FileSystem::getParent((*it2)->getPath()), + (*it)->getStartPath(), ""); + const std::string gamePath {subFolders + "/" + (*it2)->getDisplayName()}; + + for (auto& extension : FileData::sVideoExtensions) { + if (std::find(dirContent.cbegin(), dirContent.cend(), + mediaDir + gamePath + extension) != dirContent.cend()) { + mVideoFiles.push_back((*it2)); + break; + } + } } }