Merge branch 'master' into 575-theme-add-a-modern-clean-switch-like-theme-as-an-official-theme-in-es-de-to-choose-from

This commit is contained in:
Sophia Hadash 2021-11-08 12:13:33 +01:00
commit b3e42a4a17
13 changed files with 240 additions and 140 deletions

View file

@ -35,6 +35,7 @@ option(RPI "Set to ON to enable Raspberry Pi specific build" ${RPI})
option(CEC "Set to ON to enable CEC" ${CEC}) option(CEC "Set to ON to enable CEC" ${CEC})
option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER}) option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER})
option(CLANG_TIDY "Set to ON to build using the clang-tidy static analyzer" ${CLANG_TIDY}) option(CLANG_TIDY "Set to ON to build using the clang-tidy static analyzer" ${CLANG_TIDY})
option(VIDEO_HW_DECODING "Set to OFF to disable FFmpeg HW decoding" ON)
if(CLANG_TIDY) if(CLANG_TIDY)
find_program(CLANG_TIDY_BINARY NAMES clang-tidy) find_program(CLANG_TIDY_BINARY NAMES clang-tidy)
@ -58,14 +59,7 @@ endif()
#--------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------
# OpenGL setup. # OpenGL setup.
# Check if we're running on a Raspberry Pi. if(GLES)
if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h")
message("-- Building on a Raspberry Pi (bcm_host.h found)")
# Setting BCMHOST seems to break OpenGL ES on the RPi 4 so set RPI instead.
#set(BCMHOST found)
set(RPI ON)
set(GLSYSTEM "Embedded OpenGL" CACHE STRING "The OpenGL system to be used")
elseif(GLES OR RPI)
set(GLSYSTEM "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") set(GLSYSTEM "Embedded OpenGL" CACHE STRING "The OpenGL system to be used")
else() else()
set(GLSYSTEM "Desktop OpenGL" CACHE STRING "The OpenGL system to be used") set(GLSYSTEM "Desktop OpenGL" CACHE STRING "The OpenGL system to be used")
@ -73,6 +67,32 @@ endif()
set_property(CACHE GLSYSTEM PROPERTY STRINGS "Desktop OpenGL" "Embedded OpenGL") set_property(CACHE GLSYSTEM PROPERTY STRINGS "Desktop OpenGL" "Embedded OpenGL")
#---------------------------------------------------------------------------------------------------
# Raspberry Pi setup.
# If manually set to RPI (used for testing purposes).
if(RPI)
set(VIDEO_HW_DECODING OFF)
endif()
# Raspberry Pi OS 32-bit (armv7l)
if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h")
set(RPI ON)
set(RPI_32 ON)
set(VIDEO_HW_DECODING OFF)
set(BCMHOST found)
message("-- Building on a Raspberry Pi (32-bit OS)")
endif()
# Raspberry Pi OS 64-bit (aarch64)
if(EXISTS "/usr/include/bcm_host.h")
set(RPI ON)
set(RPI_64 ON)
set(VIDEO_HW_DECODING OFF)
set(BCMHOST found)
message("-- Building on a Raspberry Pi (64-bit OS)")
endif()
#--------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------
# Package dependencies. # Package dependencies.
@ -196,18 +216,22 @@ else()
add_definitions(-DUSE_OPENGLES_10) add_definitions(-DUSE_OPENGLES_10)
endif() endif()
if(RPI)
add_definitions(-D_RPI_)
endif()
if(VLC_PLAYER) if(VLC_PLAYER)
add_definitions(-DBUILD_VLC_PLAYER) add_definitions(-DBUILD_VLC_PLAYER)
endif() endif()
if(DEFINED BCMHOST OR RPI)
add_definitions(-D_RPI_)
endif()
if(DEFINED libCEC_FOUND) if(DEFINED libCEC_FOUND)
add_definitions(-DHAVE_LIBCEC) add_definitions(-DHAVE_LIBCEC)
endif() endif()
if(VIDEO_HW_DECODING)
add_definitions(-DVIDEO_HW_DECODING)
endif()
# GLM library options. # GLM library options.
add_definitions(-DGLM_FORCE_CXX17) add_definitions(-DGLM_FORCE_CXX17)
add_definitions(-DGLM_FORCE_XYZW_ONLY) add_definitions(-DGLM_FORCE_XYZW_ONLY)
@ -279,7 +303,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND COMMON_INCLUDE_DIRS ${ALSA_INCLUDE_DIRS}) list(APPEND COMMON_INCLUDE_DIRS ${ALSA_INCLUDE_DIRS})
endif() endif()
if(DEFINED BCMHOST OR RPI) if(RPI_32)
list(APPEND COMMON_INCLUDE_DIRS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include" list(APPEND COMMON_INCLUDE_DIRS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include"
"${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vcos" "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vcos"
"${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vmcs_host/linux" "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vmcs_host/linux"
@ -358,9 +382,6 @@ endif()
# Add libCEC libraries. # Add libCEC libraries.
if(DEFINED libCEC_FOUND) if(DEFINED libCEC_FOUND)
if(DEFINED BCMHOST OR RPI)
list(APPEND COMMON_LIBRARIES bcm_host vchiq_arm)
endif()
list(APPEND COMMON_LIBRARIES dl ${libCEC_LIBRARIES}) list(APPEND COMMON_LIBRARIES dl ${libCEC_LIBRARIES})
endif() endif()
@ -369,14 +390,22 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND COMMON_LIBRARIES ${ALSA_LIBRARY}) list(APPEND COMMON_LIBRARIES ${ALSA_LIBRARY})
endif() endif()
# Raspberry Pi.
if(DEFINED BCMHOST) if(DEFINED BCMHOST)
link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") list(APPEND COMMON_LIBRARIES bcm_host vchiq_arm)
list(APPEND COMMON_LIBRARIES bcm_host brcmEGL ${OPENGLES_LIBRARIES}) if(RPI_32)
elseif(RPI) link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib")
link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") endif()
endif()
# Note: Building with GLES support on the Raspberry Pi currently seems to be broken.
if(GLES AND RPI_32)
list(APPEND COMMON_LIBRARIES brcmEGL ${OPENGLES_LIBRARIES})
elseif(GLES AND RPI_64)
list(APPEND COMMON_LIBRARIES ${OPENGLES_LIBRARIES}) list(APPEND COMMON_LIBRARIES ${OPENGLES_LIBRARIES})
endif() endif()
# OpenGL.
if(GLSYSTEM MATCHES "Desktop OpenGL") if(GLSYSTEM MATCHES "Desktop OpenGL")
list(APPEND COMMON_LIBRARIES ${OPENGL_LIBRARIES}) list(APPEND COMMON_LIBRARIES ${OPENGL_LIBRARIES})
else() else()

View file

@ -133,9 +133,6 @@ GuiGameScraper::GuiGameScraper(Window* window,
static_cast<float>(Renderer::getScreenHeight()) * 0.04f + static_cast<float>(Renderer::getScreenHeight()) * 0.04f +
mButtonGrid->getSize().y + Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f; mButtonGrid->getSize().y + Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f;
// TODO: Temporary hack, see below.
height -= 7.0f * Renderer::getScreenHeightModifier();
setSize(width, height); setSize(width, height);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f); (Renderer::getScreenHeight() - mSize.y) / 2.0f);
@ -154,17 +151,11 @@ void GuiGameScraper::onSizeChanged()
mSize.y / 2.0f); mSize.y / 2.0f);
mGrid.setRowHeightPerc(2, mSystemName->getFont()->getLetterHeight() / mSize.y, false); mGrid.setRowHeightPerc(2, mSystemName->getFont()->getLetterHeight() / mSize.y, false);
mGrid.setRowHeightPerc(3, 0.04f, false); mGrid.setRowHeightPerc(3, 0.04f, false);
mGrid.setRowHeightPerc(4, ((Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f)) / mSize.y, false); mGrid.setRowHeightPerc(4, (Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f) / mSize.y, false);
// TODO: Replace this temporary hack with a proper solution. There is some kind of rounding
// issue somewhere that causes a small alignment error. This code partly compensates for this
// at higher resolutions than 1920x1080.
if (Renderer::getScreenHeightModifier() > 1.0f)
mSize.y -= 3.0f * Renderer::getScreenHeightModifier();
mGrid.setColWidthPerc(1, 0.04f); mGrid.setColWidthPerc(1, 0.04f);
mGrid.setSize(mSize); mGrid.setSize(glm::round(mSize));
mBox.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); mBox.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
// Add some extra margins to the game name. // Add some extra margins to the game name.

View file

@ -1010,7 +1010,7 @@ void GuiMenu::openOtherOptions()
} }
#endif #endif
#if !defined(_RPI_) #if defined(VIDEO_HW_DECODING)
// Whether to enable hardware decoding for the FFmpeg video player. // Whether to enable hardware decoding for the FFmpeg video player.
auto video_hardware_decoding = std::make_shared<SwitchComponent>(mWindow); auto video_hardware_decoding = std::make_shared<SwitchComponent>(mWindow);
video_hardware_decoding->setState(Settings::getInstance()->getBool("VideoHardwareDecoding")); video_hardware_decoding->setState(Settings::getInstance()->getBool("VideoHardwareDecoding"));

View file

@ -32,6 +32,11 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title)
// just in case the scraper from settings has vanished. // just in case the scraper from settings has vanished.
for (auto it = scrapers.cbegin(); it != scrapers.cend(); it++) for (auto it = scrapers.cbegin(); it != scrapers.cend(); it++)
mScraper->add(*it, *it, *it == Settings::getInstance()->getString("Scraper")); mScraper->add(*it, *it, *it == Settings::getInstance()->getString("Scraper"));
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the scraper to "screenscraper" in this case.
if (mScraper->getSelectedObjects().size() == 0)
mScraper->selectEntry(0);
mMenu.addWithLabel("SCRAPE FROM", mScraper); mMenu.addWithLabel("SCRAPE FROM", mScraper);
// Search filters, getSearches() will generate a queue of games to scrape // Search filters, getSearches() will generate a queue of games to scrape

View file

@ -32,7 +32,14 @@ const std::map<std::string, generate_scraper_requests_func> scraper_request_func
std::unique_ptr<ScraperSearchHandle> startScraperSearch(const ScraperSearchParams& params) std::unique_ptr<ScraperSearchHandle> startScraperSearch(const ScraperSearchParams& params)
{ {
const std::string& name = Settings::getInstance()->getString("Scraper"); std::string name = Settings::getInstance()->getString("Scraper");
// Handle a potentially invalid entry in the configuration file.
if (name != "screenscraper" && name != "thegamesdb") {
name = "screenscraper";
Settings::getInstance()->setString("Scraper", name);
Settings::getInstance()->saveFile();
}
std::unique_ptr<ScraperSearchHandle> handle(new ScraperSearchHandle()); std::unique_ptr<ScraperSearchHandle> handle(new ScraperSearchHandle());
// Check if the scraper in the settings still exists as a registered scraping source. // Check if the scraper in the settings still exists as a registered scraping source.

View file

@ -39,6 +39,12 @@ UIModeController::UIModeController()
{ {
mPassKeySequence = Settings::getInstance()->getString("UIMode_passkey"); mPassKeySequence = Settings::getInstance()->getString("UIMode_passkey");
mCurrentUIMode = Settings::getInstance()->getString("UIMode"); mCurrentUIMode = Settings::getInstance()->getString("UIMode");
// Handle a potentially invalid entry in the configuration file.
if (mCurrentUIMode != "full" && mCurrentUIMode != "kid" && mCurrentUIMode != "kiosk") {
mCurrentUIMode = "full";
Settings::getInstance()->setString("UIMode", mCurrentUIMode);
Settings::getInstance()->saveFile();
}
} }
void UIModeController::monitorUIMode() void UIModeController::monitorUIMode()

View file

@ -98,7 +98,7 @@ void Settings::setDefaults()
// Scraper. // Scraper.
mStringMap["Scraper"] = {"screenscraper", "screenscraper"}; mStringMap["Scraper"] = {"screenscraper", "screenscraper"};
mBoolMap["ScraperUseAccountScreenScraper"] = {false, false}; mBoolMap["ScraperUseAccountScreenScraper"] = {true, true};
mStringMap["ScraperUsernameScreenScraper"] = {"", ""}; mStringMap["ScraperUsernameScreenScraper"] = {"", ""};
mStringMap["ScraperPasswordScreenScraper"] = {"", ""}; mStringMap["ScraperPasswordScreenScraper"] = {"", ""};
@ -154,7 +154,11 @@ void Settings::setDefaults()
// UI settings -> media viewer settings. // UI settings -> media viewer settings.
mBoolMap["MediaViewerKeepVideoRunning"] = {true, true}; mBoolMap["MediaViewerKeepVideoRunning"] = {true, true};
mBoolMap["MediaViewerStretchVideos"] = {false, false}; mBoolMap["MediaViewerStretchVideos"] = {false, false};
#if defined(_RPI_)
mBoolMap["MediaViewerVideoScanlines"] = {false, false};
#else
mBoolMap["MediaViewerVideoScanlines"] = {true, true}; mBoolMap["MediaViewerVideoScanlines"] = {true, true};
#endif
mBoolMap["MediaViewerVideoBlur"] = {false, false}; mBoolMap["MediaViewerVideoBlur"] = {false, false};
mBoolMap["MediaViewerScreenshotScanlines"] = {true, true}; mBoolMap["MediaViewerScreenshotScanlines"] = {true, true};
@ -177,7 +181,11 @@ void Settings::setDefaults()
mIntMap["ScreensaverSwapVideoTimeout"] = {0, 0}; mIntMap["ScreensaverSwapVideoTimeout"] = {0, 0};
mBoolMap["ScreensaverStretchVideos"] = {false, false}; mBoolMap["ScreensaverStretchVideos"] = {false, false};
mBoolMap["ScreensaverVideoGameInfo"] = {true, true}; mBoolMap["ScreensaverVideoGameInfo"] = {true, true};
#if defined(_RPI_)
mBoolMap["ScreensaverVideoScanlines"] = {false, false};
#else
mBoolMap["ScreensaverVideoScanlines"] = {true, true}; mBoolMap["ScreensaverVideoScanlines"] = {true, true};
#endif
mBoolMap["ScreensaverVideoBlur"] = {false, false}; mBoolMap["ScreensaverVideoBlur"] = {false, false};
mBoolMap["MenuBlurBackground"] = {true, true}; mBoolMap["MenuBlurBackground"] = {true, true};
@ -199,8 +207,8 @@ void Settings::setDefaults()
mBoolMap["EnableMenuKidMode"] = {false, false}; mBoolMap["EnableMenuKidMode"] = {false, false};
// Sound settings. // Sound settings.
mIntMap["SoundVolumeNavigation"] = {80, 80}; mIntMap["SoundVolumeNavigation"] = {70, 70};
mIntMap["SoundVolumeVideos"] = {100, 100}; mIntMap["SoundVolumeVideos"] = {80, 80};
mBoolMap["GamelistVideoAudio"] = {true, true}; mBoolMap["GamelistVideoAudio"] = {true, true};
mBoolMap["MediaViewerVideoAudio"] = {true, true}; mBoolMap["MediaViewerVideoAudio"] = {true, true};
mBoolMap["ScreensaverVideoAudio"] = {false, false}; mBoolMap["ScreensaverVideoAudio"] = {false, false};
@ -221,7 +229,7 @@ void Settings::setDefaults()
// Other settings. // Other settings.
mStringMap["MediaDirectory"] = {"", ""}; mStringMap["MediaDirectory"] = {"", ""};
#if defined(_RPI_) #if defined(_RPI_)
mIntMap["MaxVRAM"] = {80, 80}; mIntMap["MaxVRAM"] = {180, 180};
#else #else
mIntMap["MaxVRAM"] = {256, 256}; mIntMap["MaxVRAM"] = {256, 256};
#endif #endif
@ -230,12 +238,7 @@ void Settings::setDefaults()
mStringMap["FullscreenMode"] = {"normal", "normal"}; mStringMap["FullscreenMode"] = {"normal", "normal"};
#endif #endif
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
#if defined(_RPI_)
// As the FFmpeg video player is not HW accelerated, use VLC as default on this weak device.
mStringMap["VideoPlayer"] = {"vlc", "vlc"};
#else
mStringMap["VideoPlayer"] = {"ffmpeg", "ffmpeg"}; mStringMap["VideoPlayer"] = {"ffmpeg", "ffmpeg"};
#endif
#endif #endif
mStringMap["ExitButtonCombo"] = {"F4", "F4"}; mStringMap["ExitButtonCombo"] = {"F4", "F4"};
mStringMap["SaveGamelistsMode"] = {"always", "always"}; mStringMap["SaveGamelistsMode"] = {"always", "always"};
@ -246,7 +249,7 @@ void Settings::setDefaults()
#if defined(_WIN64) #if defined(_WIN64)
mBoolMap["LaunchWorkaround"] = {true, true}; mBoolMap["LaunchWorkaround"] = {true, true};
#endif #endif
#if !defined(_RPI_) #if !defined(VIDEO_HW_DECODING)
mBoolMap["VideoHardwareDecoding"] = {false, false}; mBoolMap["VideoHardwareDecoding"] = {false, false};
#endif #endif
mBoolMap["VideoUpscaleFrameRate"] = {false, false}; mBoolMap["VideoUpscaleFrameRate"] = {false, false};
@ -358,16 +361,9 @@ void Settings::saveFile()
void Settings::loadFile() void Settings::loadFile()
{ {
// Prior to ES-DE v1.1, the configuration file had the .cfg suffix instead of .xml
const std::string legacyConfigFile =
Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.cfg";
const std::string configFile = const std::string configFile =
Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml"; Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml";
if (Utils::FileSystem::exists(legacyConfigFile) && !Utils::FileSystem::exists(configFile))
Utils::FileSystem::copyFile(legacyConfigFile, configFile, false);
if (!Utils::FileSystem::exists(configFile)) if (!Utils::FileSystem::exists(configFile))
return; return;

View file

@ -128,6 +128,10 @@ bool Window::init()
mBackgroundOverlay->setResize(static_cast<float>(Renderer::getScreenWidth()), mBackgroundOverlay->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())); static_cast<float>(Renderer::getScreenHeight()));
#if defined(USE_OPENGL_21)
mPostprocessedBackground = TextureResource::get("");
#endif
mListScrollFont = Font::get(FONT_SIZE_LARGE); mListScrollFont = Font::get(FONT_SIZE_LARGE);
// Update our help because font sizes probably changed. // Update our help because font sizes probably changed.
@ -143,6 +147,10 @@ void Window::deinit()
for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); it++) for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); it++)
(*it)->onHide(); (*it)->onHide();
#if defined(USE_OPENGL_21)
mPostprocessedBackground.reset();
#endif
InputManager::getInstance()->deinit(); InputManager::getInstance()->deinit();
ResourceManager::getInstance()->unloadAll(); ResourceManager::getInstance()->unloadAll();
#if defined(BUILD_VLC_PLAYER) #if defined(BUILD_VLC_PLAYER)
@ -429,9 +437,6 @@ void Window::render()
#if (CLOCK_BACKGROUND_CREATION) #if (CLOCK_BACKGROUND_CREATION)
const auto backgroundStartTime = std::chrono::system_clock::now(); const auto backgroundStartTime = std::chrono::system_clock::now();
#endif #endif
std::shared_ptr<TextureResource> mPostprocessedBackground;
mPostprocessedBackground = TextureResource::get("");
unsigned char* processedTexture = unsigned char* processedTexture =
new unsigned char[Renderer::getScreenWidth() * Renderer::getScreenHeight() * 4]; new unsigned char[Renderer::getScreenWidth() * Renderer::getScreenHeight() * 4];

View file

@ -167,6 +167,10 @@ private:
std::queue<std::pair<std::string, int>> mInfoPopupQueue; std::queue<std::pair<std::string, int>> mInfoPopupQueue;
#if defined(USE_OPENGL_21)
std::shared_ptr<TextureResource> mPostprocessedBackground;
#endif
std::string mListScrollText; std::string mListScrollText;
std::shared_ptr<Font> mListScrollFont; std::shared_ptr<Font> mListScrollFont;
unsigned char mListScrollOpacity; unsigned char mListScrollOpacity;

View file

@ -289,7 +289,7 @@ void ComponentList::render(const glm::mat4& parentTrans)
dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x; dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x;
dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y; dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
const int clipRectPosX{static_cast<int>(std::round(trans[3].x))}; const int clipRectPosX{static_cast<int>(std::ceil(trans[3].x))};
const int clipRectPosY{static_cast<int>(std::round(trans[3].y))}; const int clipRectPosY{static_cast<int>(std::round(trans[3].y))};
const int clipRectSizeX{static_cast<int>(std::round(dim.x))}; const int clipRectSizeX{static_cast<int>(std::round(dim.x))};
const int clipRectSizeY{static_cast<int>(std::round(dim.y))}; const int clipRectSizeY{static_cast<int>(std::round(dim.y))};
@ -383,12 +383,12 @@ void ComponentList::render(const glm::mat4& parentTrans)
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data);
if (opacity == 1) { if (opacity == 1) {
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0xFFFFFFFF, Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight,
0xFFFFFFFF, false, opacity, trans, 0xFFFFFFFF, 0xFFFFFFFF, false, opacity, trans,
Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x, selectedRowHeight, 0x777777FF, Renderer::drawRect(0.0f, mSelectorBarOffset, std::ceil(mSize.x), selectedRowHeight,
0x777777FF, false, opacity, trans, Renderer::Blend::ONE, 0x777777FF, 0x777777FF, false, opacity, trans, Renderer::Blend::ONE,
Renderer::Blend::ONE); Renderer::Blend::ONE);
} }
@ -403,13 +403,13 @@ void ComponentList::render(const glm::mat4& parentTrans)
// Draw separators. // Draw separators.
float y = 0; float y = 0;
for (unsigned int i = 0; i < mEntries.size(); i++) { for (unsigned int i = 0; i < mEntries.size(); i++) {
Renderer::drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF, Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(),
0xC6C7C6FF, false, opacity, trans); 0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
y += getRowHeight(mEntries.at(i).data); y += getRowHeight(mEntries.at(i).data);
} }
Renderer::drawRect(0.0f, y, mSize.x, 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF, Renderer::drawRect(0.0f, y, std::ceil(mSize.x), 1.0f * Renderer::getScreenHeightModifier(),
0xC6C7C6FF, false, opacity, trans); 0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
Renderer::popClipRect(); Renderer::popClipRect();
} }

View file

@ -16,6 +16,7 @@
#include "resources/TextureResource.h" #include "resources/TextureResource.h"
#include <algorithm> #include <algorithm>
#include <iomanip>
enum AVHWDeviceType VideoFFmpegComponent::sDeviceType = AV_HWDEVICE_TYPE_NONE; enum AVHWDeviceType VideoFFmpegComponent::sDeviceType = AV_HWDEVICE_TYPE_NONE;
enum AVPixelFormat VideoFFmpegComponent::sPixelFormat = AV_PIX_FMT_NONE; enum AVPixelFormat VideoFFmpegComponent::sPixelFormat = AV_PIX_FMT_NONE;
@ -165,17 +166,22 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
mPictureMutex.lock(); mPictureMutex.lock();
if (!mOutputPicture.hasBeenRendered) { if (!mOutputPicture.hasBeenRendered) {
// Copy the contents of mOutputPicture to a temporary vector in order to call // Move the contents of mOutputPicture to a temporary vector in order to call
// initFromPixels() only after the mutex unlock. This significantly reduces the // initFromPixels() only after the mutex unlock. This significantly reduces the
// lock waits in outputFrames(). // lock waits in outputFrames().
size_t pictureSize = mOutputPicture.pictureRGBA.size(); size_t pictureSize = mOutputPicture.pictureRGBA.size();
std::vector<uint8_t> tempPictureRGBA(pictureSize); std::vector<uint8_t> tempPictureRGBA;
int pictureWidth = 0; int pictureWidth = 0;
int pictureHeight = 0; int pictureHeight = 0;
if (pictureSize > 0) { if (pictureSize > 0) {
tempPictureRGBA.insert(tempPictureRGBA.begin(), mOutputPicture.pictureRGBA.begin(), tempPictureRGBA.insert(tempPictureRGBA.begin(),
mOutputPicture.pictureRGBA.end()); std::make_move_iterator(mOutputPicture.pictureRGBA.begin()),
std::make_move_iterator(mOutputPicture.pictureRGBA.end()));
mOutputPicture.pictureRGBA.erase(mOutputPicture.pictureRGBA.begin(),
mOutputPicture.pictureRGBA.end());
pictureWidth = mOutputPicture.width; pictureWidth = mOutputPicture.width;
pictureHeight = mOutputPicture.height; pictureHeight = mOutputPicture.height;
@ -528,90 +534,126 @@ void VideoFFmpegComponent::readFrames()
if (mVideoFrameQueue.size() > 300 || mAudioFrameQueue.size() > 600) if (mVideoFrameQueue.size() > 300 || mAudioFrameQueue.size() > 600)
return; return;
int readLoops = 1;
// If we can't keep up the audio processing, then drop video frames as it's much worse
// to have stuttering audio than a lower video framerate.
if (mAudioStreamIndex >= 0 && mAudioFrameCount > mAudioTargetQueueSize / 2) {
if (static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize / 6)
readLoops = 5;
else if (static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize / 4)
readLoops = 3;
else if (static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize / 2)
readLoops = 2;
}
if (mVideoCodecContext && mFormatContext) { if (mVideoCodecContext && mFormatContext) {
if (static_cast<int>(mVideoFrameQueue.size()) < mVideoTargetQueueSize || for (int i = 0; i < readLoops; i++) {
(mAudioStreamIndex >= 0 && if (static_cast<int>(mVideoFrameQueue.size()) < mVideoTargetQueueSize ||
static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize)) { (mAudioStreamIndex >= 0 &&
while ((readFrameReturn = av_read_frame(mFormatContext, mPacket)) >= 0) { static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize)) {
if (mPacket->stream_index == mVideoStreamIndex) { while ((readFrameReturn = av_read_frame(mFormatContext, mPacket)) >= 0) {
if (!avcodec_send_packet(mVideoCodecContext, mPacket) && if (mPacket->stream_index == mVideoStreamIndex) {
!avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) { if (!avcodec_send_packet(mVideoCodecContext, mPacket) &&
!avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) {
int returnValue = 0; int returnValue = 0;
mVideoFrameReadCount++;
if (mSWDecoder) { if (mSWDecoder) {
returnValue = av_buffersrc_add_frame_flags( // Drop the frame if necessary.
mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT); if (i == 0 || mAudioFrameCount == 0) {
} returnValue = av_buffersrc_add_frame_flags(
else { mVBufferSrcContext, mVideoFrame,
AVFrame* destFrame = nullptr; AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT);
destFrame = av_frame_alloc();
if (mVideoFrame->format == sPixelFormat) {
if (av_hwframe_transfer_data(destFrame, mVideoFrame, 0) < 0) {
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
"Couldn't transfer decoded video frame to "
"system memory";
av_frame_free(&destFrame);
av_packet_unref(mPacket);
break;
} }
else { else {
destFrame->pts = mVideoFrame->pts; mVideoFrameDroppedCount++;
destFrame->pkt_dts = mVideoFrame->pkt_dts;
destFrame->pict_type = mVideoFrame->pict_type;
destFrame->chroma_location = mVideoFrame->chroma_location;
destFrame->pkt_pos = mVideoFrame->pkt_pos;
destFrame->pkt_duration = mVideoFrame->pkt_duration;
destFrame->pkt_size = mVideoFrame->pkt_size;
} }
} }
else { else {
LOG(LogError) << "VideoFFmpegComponent::readFrames(): " if (i == 0 || mAudioFrameCount == 0) {
"Couldn't decode video frame"; AVFrame* destFrame = nullptr;
destFrame = av_frame_alloc();
if (mVideoFrame->format == sPixelFormat) {
if (av_hwframe_transfer_data(destFrame, mVideoFrame, 0) <
0) {
LOG(LogError)
<< "VideoFFmpegComponent::readFrames(): "
"Couldn't transfer decoded video frame to "
"system memory";
av_frame_free(&destFrame);
av_packet_unref(mPacket);
break;
}
else {
destFrame->pts = mVideoFrame->pts;
destFrame->pkt_dts = mVideoFrame->pkt_dts;
destFrame->pict_type = mVideoFrame->pict_type;
destFrame->chroma_location =
mVideoFrame->chroma_location;
destFrame->pkt_pos = mVideoFrame->pkt_pos;
destFrame->pkt_duration = mVideoFrame->pkt_duration;
destFrame->pkt_size = mVideoFrame->pkt_size;
}
}
else {
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
"Couldn't decode video frame";
}
returnValue = av_buffersrc_add_frame_flags(
mVBufferSrcContext, destFrame,
AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT);
av_frame_free(&destFrame);
}
else {
mVideoFrameDroppedCount++;
}
} }
returnValue = av_buffersrc_add_frame_flags( if (returnValue < 0) {
mVBufferSrcContext, destFrame, AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT); LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
av_frame_free(&destFrame); "Couldn't add video frame to buffer source";
} }
if (returnValue < 0) { av_packet_unref(mPacket);
LOG(LogError) << "VideoFFmpegComponent::readFrames(): " break;
"Couldn't add video frame to buffer source";
} }
else {
av_packet_unref(mPacket);
}
}
else if (mPacket->stream_index == mAudioStreamIndex) {
if (!avcodec_send_packet(mAudioCodecContext, mPacket) &&
!avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) {
av_packet_unref(mPacket); // We have an audio frame that needs conversion and resampling.
break; int returnValue = av_buffersrc_add_frame_flags(
mABufferSrcContext, mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
if (returnValue < 0) {
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
"Couldn't add audio frame to buffer source";
}
av_packet_unref(mPacket);
continue;
}
else {
av_packet_unref(mPacket);
}
} }
else { else {
// Ignore any stream that is not video or audio.
av_packet_unref(mPacket); av_packet_unref(mPacket);
} }
} }
else if (mPacket->stream_index == mAudioStreamIndex) { }
if (!avcodec_send_packet(mAudioCodecContext, mPacket) && else {
!avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) { // The target queue sizes have been reached.
break;
// We have an audio frame that needs conversion and resampling.
int returnValue = av_buffersrc_add_frame_flags(
mABufferSrcContext, mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
if (returnValue < 0) {
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
"Couldn't add audio frame to buffer source";
}
av_packet_unref(mPacket);
continue;
}
else {
av_packet_unref(mPacket);
}
}
else {
// Ignore any stream that is not video or audio.
av_packet_unref(mPacket);
}
} }
} }
} }
@ -649,10 +691,11 @@ void VideoFFmpegComponent::getProcessedFrames()
int bufferSize = mVideoFrameResampled->width * mVideoFrameResampled->height * 4; int bufferSize = mVideoFrameResampled->width * mVideoFrameResampled->height * 4;
currFrame.frameRGBA.insert(currFrame.frameRGBA.begin(), &mVideoFrameResampled->data[0][0], currFrame.frameRGBA.insert(
&mVideoFrameResampled->data[0][bufferSize]); currFrame.frameRGBA.begin(), std::make_move_iterator(&mVideoFrameResampled->data[0][0]),
std::make_move_iterator(&mVideoFrameResampled->data[0][bufferSize]));
mVideoFrameQueue.push(currFrame); mVideoFrameQueue.push(std::move(currFrame));
av_frame_unref(mVideoFrameResampled); av_frame_unref(mVideoFrameResampled);
} }
@ -680,7 +723,7 @@ void VideoFFmpegComponent::getProcessedFrames()
&mAudioFrameResampled->data[0][0], &mAudioFrameResampled->data[0][0],
&mAudioFrameResampled->data[0][bufferSize]); &mAudioFrameResampled->data[0][bufferSize]);
mAudioFrameQueue.push(currFrame); mAudioFrameQueue.push(std::move(currFrame));
av_frame_unref(mAudioFrameResampled); av_frame_unref(mAudioFrameResampled);
} }
} }
@ -751,6 +794,14 @@ void VideoFFmpegComponent::outputFrames()
LOG(LogDebug) << "Total video frames processed / video frame queue size: " LOG(LogDebug) << "Total video frames processed / video frame queue size: "
<< mVideoFrameCount << " / " << mVideoFrameCount << " / "
<< std::to_string(mVideoFrameQueue.size()); << std::to_string(mVideoFrameQueue.size());
if (mVideoFrameDroppedCount > 0) {
LOG(LogDebug) << "Video frames dropped: " << mVideoFrameDroppedCount << " of "
<< mVideoFrameReadCount << " (" << std::setprecision(2)
<< (static_cast<float>(mVideoFrameDroppedCount) /
static_cast<float>(mVideoFrameReadCount)) *
100.0f
<< "%)";
}
} }
mPictureMutex.lock(); mPictureMutex.lock();
@ -1136,6 +1187,8 @@ void VideoFFmpegComponent::startVideo()
mEndOfVideo = false; mEndOfVideo = false;
mVideoFrameCount = 0; mVideoFrameCount = 0;
mAudioFrameCount = 0; mAudioFrameCount = 0;
mVideoFrameReadCount = 0;
mVideoFrameDroppedCount = 0;
mOutputPicture = {}; mOutputPicture = {};
// Get an empty texture for rendering the video. // Get an empty texture for rendering the video.
@ -1174,7 +1227,7 @@ void VideoFFmpegComponent::startVideo()
// Video stream setup. // Video stream setup.
#if defined(_RPI_) #if !defined(VIDEO_HW_DECODING)
bool hwDecoding = false; bool hwDecoding = false;
#else #else
bool hwDecoding = Settings::getInstance()->getBool("VideoHardwareDecoding"); bool hwDecoding = Settings::getInstance()->getBool("VideoHardwareDecoding");

View file

@ -158,6 +158,8 @@ private:
int mAudioFrameCount; int mAudioFrameCount;
int mVideoFrameCount; int mVideoFrameCount;
int mVideoFrameReadCount;
int mVideoFrameDroppedCount;
double mAccumulatedTime; double mAccumulatedTime;
bool mStartTimeAccumulation; bool mStartTimeAccumulation;

View file

@ -105,8 +105,9 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
nsvgDeleteRasterizer(rast); nsvgDeleteRasterizer(rast);
mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), mDataRGBA.insert(mDataRGBA.begin(), std::make_move_iterator(tempVector.data()),
tempVector.data() + (mWidth * mHeight * 4)); std::make_move_iterator(tempVector.data() + (mWidth * mHeight * 4)));
tempVector.erase(tempVector.begin(), tempVector.end());
ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight);
mPendingRasterization = false; mPendingRasterization = false;
@ -160,7 +161,8 @@ bool TextureData::initFromRGBA(const unsigned char* dataRGBA, size_t width, size
return true; return true;
mDataRGBA.reserve(width * height * 4); mDataRGBA.reserve(width * height * 4);
mDataRGBA.insert(mDataRGBA.begin(), dataRGBA, dataRGBA + (width * height * 4)); mDataRGBA.insert(mDataRGBA.begin(), std::make_move_iterator(dataRGBA),
std::make_move_iterator(dataRGBA + (width * height * 4)));
mWidth = static_cast<int>(width); mWidth = static_cast<int>(width);
mHeight = static_cast<int>(height); mHeight = static_cast<int>(height);