From 029e8bd0400fe20362217dd2e6d607395d663b9d Mon Sep 17 00:00:00 2001 From: pjft Date: Sat, 25 Mar 2017 17:02:28 +0000 Subject: [PATCH] Fixes and tweaks to OMXPlayer work, by pjft - Correct handling of zombie processes left in memory - Add options to mute video - Fix resizing to work with theme refactorings introduced by jdrassa and zigurana --- .gitmodules | 3 + es-app/src/guis/GuiMenu.cpp | 66 ++++++++++---- .../src/views/gamelist/VideoGameListView.cpp | 2 +- es-core/src/Settings.cpp | 6 +- es-core/src/Window.cpp | 35 ++++++-- es-core/src/Window.h | 4 +- es-core/src/components/VideoComponent.cpp | 84 ++--------------- es-core/src/components/VideoComponent.h | 26 +++--- .../src/components/VideoPlayerComponent.cpp | 50 ++++++++++- es-core/src/components/VideoPlayerComponent.h | 13 +++ es-core/src/components/VideoVlcComponent.cpp | 89 ++++++++++++++++++- es-core/src/components/VideoVlcComponent.h | 15 ++++ external/pugixml | 1 + 13 files changed, 266 insertions(+), 128 deletions(-) create mode 160000 external/pugixml diff --git a/.gitmodules b/.gitmodules index e69de29bb..ab38f31f3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/pugixml"] + path = external/pugixml + url = https://github.com/zeux/pugixml.git diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index b210c283b..98a00526d 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -31,8 +31,8 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN // [version] auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }; - addEntry("SCRAPER", 0x777777FF, true, - [this, openScrapeNow] { + addEntry("SCRAPER", 0x777777FF, true, + [this, openScrapeNow] { auto s = new GuiSettings(mWindow, "SCRAPER"); // scrape from @@ -65,7 +65,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN mWindow->pushGui(s); }); - addEntry("SOUND SETTINGS", 0x777777FF, true, + addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); @@ -74,11 +74,11 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN volume->setValue((float)VolumeControl::getInstance()->getVolume()); s->addWithLabel("SYSTEM VOLUME", volume); s->addSaveFunc([volume] { VolumeControl::getInstance()->setVolume((int)round(volume->getValue())); }); - + // disable sounds auto sounds_enabled = std::make_shared(mWindow); sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds")); - s->addWithLabel("ENABLE SOUNDS", sounds_enabled); + s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled); s->addSaveFunc([sounds_enabled] { Settings::getInstance()->setBool("EnableSounds", sounds_enabled->getState()); }); mWindow->pushGui(s); @@ -147,7 +147,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN s->addWithLabel("THEME SET", theme_set); Window* window = mWindow; - s->addSaveFunc([window, theme_set] + s->addSaveFunc([window, theme_set] { bool needReload = false; if(Settings::getInstance()->getString("ThemeSet") != theme_set->getSelected()) @@ -174,13 +174,43 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN bool needReload = false; if (Settings::getInstance()->getString("GamelistViewStyle") != gamelist_style->getSelected()) needReload = true; - Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected()); + Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected()); if (needReload) ViewController::get()->reloadAll(); }); mWindow->pushGui(s); }); + addEntry("VIDEO PLAYER SETTINGS", 0x777777FF, true, + [this] { + auto s = new GuiSettings(mWindow, "VIDEO PLAYER SETTINGS"); + +#ifdef _RPI_ + // Video Player - VideoOmxPlayer + auto omx_player = std::make_shared(mWindow); + omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); + s->addWithLabel("USE OMX VIDEO PLAYER (HW ACCELERATED)", omx_player); + s->addSaveFunc([omx_player] + { + // need to reload all views to re-create the right video components + bool needReload = false; + if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) + needReload = true; + + Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); + + if(needReload) + ViewController::get()->reloadAll(); + }); +#endif + auto video_audio = std::make_shared(mWindow); + video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); + s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); + s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); }); + + mWindow->pushGui(s); + }); + addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); @@ -205,7 +235,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN mWindow->pushGui(s); }); - addEntry("CONFIGURE INPUT", 0x777777FF, true, + addEntry("CONFIGURE INPUT", 0x777777FF, true, [this] { Window* window = mWindow; window->pushGui(new GuiMsgBox(window, "ARE YOU SURE YOU WANT TO CONFIGURE INPUT?", "YES", @@ -215,10 +245,10 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN ); }); - addEntry("QUIT", 0x777777FF, true, + addEntry("QUIT", 0x777777FF, true, [this] { auto s = new GuiSettings(mWindow, "QUIT"); - + Window* window = mWindow; ComponentListRow row; @@ -234,8 +264,8 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN row.elements.clear(); row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", - [] { + window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", + [] { if(quitES("/tmp/es-sysrestart") != 0) LOG(LogWarning) << "Restart terminated with non-zero result!"; }, "NO", nullptr)); @@ -245,8 +275,8 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN row.elements.clear(); row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY SHUTDOWN?", "YES", - [] { + window->pushGui(new GuiMsgBox(window, "REALLY SHUTDOWN?", "YES", + [] { if(quitES("/tmp/es-shutdown") != 0) LOG(LogWarning) << "Shutdown terminated with non-zero result!"; }, "NO", nullptr)); @@ -258,8 +288,8 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN { row.elements.clear(); row.makeAcceptInputHandler([window] { - window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", - [] { + window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", + [] { SDL_Event ev; ev.type = SDL_QUIT; SDL_PushEvent(&ev); @@ -293,7 +323,7 @@ void GuiMenu::onSizeChanged() void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func) { std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); - + // populate the list ComponentListRow row; row.addElement(std::make_shared(mWindow, name, font, color), true); @@ -303,7 +333,7 @@ void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, con std::shared_ptr bracket = makeArrow(mWindow); row.addElement(bracket, false); } - + row.makeAcceptInputHandler(func); mMenu.addRow(row); diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 7cf23cf69..9967497aa 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -61,7 +61,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f); mVideo->setDefaultZIndex(30); - addChild(&mVideo); + addChild(mVideo); // metadata labels + values mLblRating.setText("Rating: "); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index fa9067f30..aa816fb6c 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -79,11 +79,9 @@ void Settings::setDefaults() // This setting only applies to raspberry pi but set it for all platforms so // we don't get a warning if we encounter it on a different platform -#ifdef _RPI_ - mBoolMap["VideoOmxPlayer"] = true; -#else mBoolMap["VideoOmxPlayer"] = false; -#endif + mBoolMap["VideoAudio"] = true; + } template diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 78fe8ea6c..b8c6bc144 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -9,7 +9,7 @@ #include "components/HelpComponent.h" #include "components/ImageComponent.h" -Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10), +Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10), mAllowSleep(true), mSleeping(false), mTimeSinceLastInput(0) { mHelp = new HelpComponent(this); @@ -23,7 +23,7 @@ Window::~Window() // delete all our GUIs while(peekGui()) delete peekGui(); - + delete mHelp; } @@ -115,10 +115,20 @@ void Window::textInput(const char* text) void Window::input(InputConfig* config, Input input) { + if (mRenderScreenSaver) + { + mRenderScreenSaver = false; + + // Tell the GUI components the screensaver has stopped + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverDeactivate(); + } + if(mSleeping) { // wake up mTimeSinceLastInput = 0; + mSleeping = false; onWake(); return; @@ -157,11 +167,11 @@ void Window::update(int deltaTime) if(mFrameTimeElapsed > 500) { mAverageDeltaTime = mFrameTimeElapsed / mFrameCountElapsed; - + if(Settings::getInstance()->getBool("DrawFramerate")) { std::stringstream ss; - + // fps ss << std::fixed << std::setprecision(1) << (1000.0f * (float)mFrameCountElapsed / (float)mFrameTimeElapsed) << "fps, "; ss << std::fixed << std::setprecision(2) << ((float)mFrameTimeElapsed / (float)mFrameCountElapsed) << "ms"; @@ -218,6 +228,13 @@ void Window::render() unsigned int screensaverTime = (unsigned int)Settings::getInstance()->getInt("ScreenSaverTime"); if(mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) { + if (!mRenderScreenSaver) + { + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverActivate(); + mRenderScreenSaver = true; + } + renderScreenSaver(); if (!isProcessing() && mAllowSleep) @@ -258,7 +275,7 @@ void Window::renderLoadingScreen() auto& font = mDefaultFonts.at(1); TextCache* cache = font->buildTextCache("LOADING...", 0, 0, 0x656565FF); - trans = trans.translate(Eigen::Vector3f(round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f), + trans = trans.translate(Eigen::Vector3f(round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f), round(Renderer::getScreenHeight() * 0.835f), 0.0f)); Renderer::setMatrix(trans); font->renderTextCache(cache); @@ -314,16 +331,16 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt // sort prompts so it goes [dpad_all] [dpad_u/d] [dpad_l/r] [a/b/x/y/l/r] [start/select] std::sort(addPrompts.begin(), addPrompts.end(), [](const HelpPrompt& a, const HelpPrompt& b) -> bool { - + static const char* map[] = { "up/down/left/right", "up/down", "left/right", - "a", "b", "x", "y", "l", "r", - "start", "select", + "a", "b", "x", "y", "l", "r", + "start", "select", NULL }; - + int i = 0; int aVal = 0; int bVal = 0; diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 5eee873a2..6e2c93d3a 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -17,6 +17,7 @@ public: void pushGui(GuiComponent* gui); void removeGui(GuiComponent* gui); GuiComponent* peekGui(); + inline int getGuiStackSize() { return mGuiStack.size(); } void textInput(const char* text); void input(InputConfig* config, Input input); @@ -31,7 +32,7 @@ public: inline bool isSleeping() const { return mSleeping; } bool getAllowSleep(); void setAllowSleep(bool sleep); - + void renderLoadingScreen(); void renderHelpPromptsEarly(); // used to render HelpPrompts before a fade @@ -55,6 +56,7 @@ private: int mFrameTimeElapsed; int mFrameCountElapsed; int mAverageDeltaTime; + bool mRenderScreenSaver; std::unique_ptr mFrameDataText; diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 2c49a44e6..f940ea86a 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -2,6 +2,7 @@ #include "Renderer.h" #include "ThemeData.h" #include "Util.h" +#include "Window.h" #ifdef WIN32 #include #endif @@ -26,6 +27,10 @@ VideoComponent::VideoComponent(Window* window) : mConfig.showSnapshotDelay = false; mConfig.showSnapshotNoVideo = false; mConfig.startDelay = 0; + if (mWindow->getGuiStackSize() > 1) { + topWindow(false); + } + } VideoComponent::~VideoComponent() @@ -42,89 +47,12 @@ void VideoComponent::setOrigin(float originX, float originY) mStaticImage.setOrigin(originX, originY); } -void VideoComponent::setResize(float width, float height) -{ - mTargetSize << width, height; - mTargetIsMax = false; - mStaticImage.setResize(width, height); - resize(); -} - -void VideoComponent::setMaxSize(float width, float height) -{ - mTargetSize << width, height; - mTargetIsMax = true; - mStaticImage.setMaxSize(width, height); - resize(); -} - Eigen::Vector2f VideoComponent::getCenter() const { return Eigen::Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2, mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2); } -void VideoComponent::resize() -{ - if(!mTexture) - return; - - const Eigen::Vector2f textureSize(mVideoWidth, mVideoHeight); - - if(textureSize.isZero()) - return; - - // SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels - // if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks - // so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that - // (you'll see this scattered throughout the function) - // this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request! - - if(mTargetIsMax) - { - - mSize = textureSize; - - Eigen::Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y())); - - if(resizeScale.x() < resizeScale.y()) - { - mSize[0] *= resizeScale.x(); - mSize[1] *= resizeScale.x(); - }else{ - mSize[0] *= resizeScale.y(); - mSize[1] *= resizeScale.y(); - } - - // for SVG rasterization, always calculate width from rounded height (see comment above) - mSize[1] = round(mSize[1]); - mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); - - }else{ - // if both components are set, we just stretch - // if no components are set, we don't resize at all - mSize = mTargetSize.isZero() ? textureSize : mTargetSize; - - // if only one component is set, we resize in a way that maintains aspect ratio - // for SVG rasterization, we always calculate width from rounded height (see comment above) - if(!mTargetSize.x() && mTargetSize.y()) - { - mSize[1] = round(mTargetSize.y()); - mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x(); - }else if(mTargetSize.x() && !mTargetSize.y()) - { - mSize[1] = round((mTargetSize.x() / textureSize.x()) * textureSize.y()); - mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x(); - } - } - - // mSize.y() should already be rounded - mTexture->rasterizeAt((int)round(mSize.x()), (int)round(mSize.y())); - - onSizeChanged(); -} - - void VideoComponent::onSizeChanged() { // Update the embeded static image @@ -161,6 +89,8 @@ void VideoComponent::setImage(std::string path) return; mStaticImage.setImage(path); + // Make the image stretch to fill the video region + mStaticImage.setSize(getSize()); mFadeIn = 0.0f; mStaticImagePath = path; } diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index db3d9500f..e172a1172 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -48,19 +48,6 @@ public: void onSizeChanged() override; void setOpacity(unsigned char opacity) override; - // 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 zero, no resizing. - // Can be set before or after a video is loaded. - // setMaxSize() and setResize() are mutually exclusive. - void setResize(float width, float height); - inline void setResize(const Eigen::Vector2f& size) { setResize(size.x(), size.y()); } - - // Resize the video to be as large as possible but fit within a box of this size. - // 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); - inline void setMaxSize(const Eigen::Vector2f& size) { setMaxSize(size.x(), size.y()); } - void render(const Eigen::Affine3f& parentTrans) override; virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) override; @@ -72,6 +59,19 @@ public: virtual void update(int deltaTime); + // 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 zero, no resizing. + // Can be set before or after a video is loaded. + // setMaxSize() and setResize() are mutually exclusive. + virtual void setResize(float width, float height) = 0; + inline void setResize(const Eigen::Vector2f& size) { setResize(size.x(), size.y()); } + + // Resize the video to be as large as possible but fit within a box of this size. + // Can be set before or after a video is loaded. + // Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive. + virtual void setMaxSize(float width, float height) = 0; + inline void setMaxSize(const Eigen::Vector2f& size) { setMaxSize(size.x(), size.y()); } + private: // Start the video Immediately virtual void startVideo() = 0; diff --git a/es-core/src/components/VideoPlayerComponent.cpp b/es-core/src/components/VideoPlayerComponent.cpp index fb44eb25d..cc4645e1d 100644 --- a/es-core/src/components/VideoPlayerComponent.cpp +++ b/es-core/src/components/VideoPlayerComponent.cpp @@ -2,6 +2,7 @@ #include "components/VideoPlayerComponent.h" #include "Renderer.h" #include "ThemeData.h" +#include "Settings.h" #include "Util.h" #include #include @@ -24,6 +25,24 @@ void VideoPlayerComponent::render(const Eigen::Affine3f& parentTrans) VideoComponent::render(parentTrans); } +void VideoPlayerComponent::setResize(float width, float height) +{ + setSize(width, height); + mTargetSize << width, height; + mTargetIsMax = false; + mStaticImage.setSize(width, height); + onSizeChanged(); +} + +void VideoPlayerComponent::setMaxSize(float width, float height) +{ + setSize(width, height); + mTargetSize << width, height; + mTargetIsMax = true; + mStaticImage.setMaxSize(width, height); + onSizeChanged(); +} + void VideoPlayerComponent::startVideo() { if (!mIsPlaying) { @@ -49,11 +68,13 @@ void VideoPlayerComponent::startVideo() { mPlayerPid = pid; // Update the playing state + signal(SIGCHLD, catch_child); mIsPlaying = true; mFadeIn = 0.0f; } else { + // Find out the pixel position of the video view and build a command line for // omxplayer to position it in the right place char buf[32]; @@ -62,10 +83,25 @@ void VideoPlayerComponent::startVideo() sprintf(buf, "%d,%d,%d,%d", (int)x, (int)y, (int)(x + mSize.x()), (int)(y + mSize.y())); // We need to specify the layer of 10000 or above to ensure the video is displayed on top // of our SDL display - const char* argv[] = { "", "--win", buf, "--layer", "10000", "--loop", "--no-osd", "", NULL }; + + const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", "--aspect-mode", "letterbox", "--vol", "0", "--win", buf, "-b", "", "", "", "", NULL }; + + // check if we want to mute the audio + if (!Settings::getInstance()->getBool("VideoAudio")) + { + argv[8] = "-1000000"; + } + + // if we are rendering a video gamelist + if (!mTargetIsMax) + { + argv[6] = "stretch"; + } + + argv[11] = mPlayingVideoPath.c_str(); + const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL }; - // Fill in the empty argument with the video path - argv[7] = mPlayingVideoPath.c_str(); + // Redirect stdout int fdin = open("/dev/null", O_RDONLY); int fdout = open("/dev/null", O_WRONLY); @@ -73,12 +109,20 @@ void VideoPlayerComponent::startVideo() dup2(fdout, 1); // Run the omxplayer binary execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env); + _exit(EXIT_FAILURE); } } } } +void catch_child(int sig_num) +{ + /* when we get here, we know there's a zombie child waiting */ + int child_status; + wait(&child_status); +} + void VideoPlayerComponent::stopVideo() { mIsPlaying = false; diff --git a/es-core/src/components/VideoPlayerComponent.h b/es-core/src/components/VideoPlayerComponent.h index 45517e91a..27a098115 100644 --- a/es-core/src/components/VideoPlayerComponent.h +++ b/es-core/src/components/VideoPlayerComponent.h @@ -7,6 +7,8 @@ #include "components/VideoComponent.h" +void catch_child(int sig_num); + class VideoPlayerComponent : public VideoComponent { public: @@ -15,6 +17,17 @@ public: void render(const Eigen::Affine3f& parentTrans) override; + // 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 zero, no resizing. + // Can be set before or after a video is loaded. + // setMaxSize() and setResize() are mutually exclusive. + void setResize(float width, float height); + + // Resize the video to be as large as possible but fit within a box of this size. + // 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); + private: // Start the video Immediately virtual void startVideo(); diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp index e0da5003e..47853e97f 100644 --- a/es-core/src/components/VideoVlcComponent.cpp +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -2,6 +2,7 @@ #include "Renderer.h" #include "ThemeData.h" #include "Util.h" +#include "Settings.h" #ifdef WIN32 #include #endif @@ -46,6 +47,82 @@ VideoVlcComponent::~VideoVlcComponent() { } +void VideoVlcComponent::setResize(float width, float height) +{ + mTargetSize << width, height; + mTargetIsMax = false; + mStaticImage.setResize(width, height); + resize(); +} + +void VideoVlcComponent::setMaxSize(float width, float height) +{ + mTargetSize << width, height; + mTargetIsMax = true; + mStaticImage.setMaxSize(width, height); + resize(); +} + +void VideoVlcComponent::resize() +{ + if(!mTexture) + return; + + const Eigen::Vector2f textureSize(mVideoWidth, mVideoHeight); + + if(textureSize.isZero()) + return; + + // SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels + // if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks + // so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that + // (you'll see this scattered throughout the function) + // this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request! + + if(mTargetIsMax) + { + + mSize = textureSize; + + Eigen::Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y())); + + if(resizeScale.x() < resizeScale.y()) + { + mSize[0] *= resizeScale.x(); + mSize[1] *= resizeScale.x(); + }else{ + mSize[0] *= resizeScale.y(); + mSize[1] *= resizeScale.y(); + } + + // for SVG rasterization, always calculate width from rounded height (see comment above) + mSize[1] = round(mSize[1]); + mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); + + }else{ + // if both components are set, we just stretch + // if no components are set, we don't resize at all + mSize = mTargetSize.isZero() ? textureSize : mTargetSize; + + // if only one component is set, we resize in a way that maintains aspect ratio + // for SVG rasterization, we always calculate width from rounded height (see comment above) + if(!mTargetSize.x() && mTargetSize.y()) + { + mSize[1] = round(mTargetSize.y()); + mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x(); + }else if(mTargetSize.x() && !mTargetSize.y()) + { + mSize[1] = round((mTargetSize.x() / textureSize.x()) * textureSize.y()); + mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x(); + } + } + + // mSize.y() should already be rounded + mTexture->rasterizeAt((int)round(mSize.x()), (int)round(mSize.y())); + + onSizeChanged(); +} + void VideoVlcComponent::render(const Eigen::Affine3f& parentTrans) { VideoComponent::render(parentTrans); @@ -55,7 +132,7 @@ void VideoVlcComponent::render(const Eigen::Affine3f& parentTrans) GuiComponent::renderChildren(trans); Renderer::setMatrix(trans); - + if (mIsPlaying && mContext.valid) { float tex_offs_x = 0.0f; @@ -135,6 +212,7 @@ void VideoVlcComponent::setupContext() mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); mContext.mutex = SDL_CreateMutex(); mContext.valid = true; + resize(); } } @@ -153,7 +231,9 @@ void VideoVlcComponent::setupVLC() // If VLC hasn't been initialised yet then do it now if (!mVLC) { - const char* args[] = { "--quiet" }; + const char* args[] = { "--quiet", "", "", "" }; + // check if we want to mute the audio + mVLC = libvlc_new(sizeof(args) / sizeof(args[0]), args); } } @@ -217,6 +297,11 @@ void VideoVlcComponent::startVideo() // Setup the media player mMediaPlayer = libvlc_media_player_new_from_media(mMedia); + if (!Settings::getInstance()->getBool("VideoAudio")) + { + libvlc_audio_set_mute(mMediaPlayer, 1); + } + libvlc_media_player_play(mMediaPlayer); libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display, (void*)&mContext); libvlc_video_set_format(mMediaPlayer, "RGBA", (int)mVideoWidth, (int)mVideoHeight, (int)mVideoWidth * 4); diff --git a/es-core/src/components/VideoVlcComponent.h b/es-core/src/components/VideoVlcComponent.h index 2700317a2..c3c6ad549 100644 --- a/es-core/src/components/VideoVlcComponent.h +++ b/es-core/src/components/VideoVlcComponent.h @@ -33,7 +33,22 @@ public: void render(const Eigen::Affine3f& parentTrans) override; + + // 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 zero, no resizing. + // Can be set before or after a video is loaded. + // setMaxSize() and setResize() are mutually exclusive. + void setResize(float width, float height); + + // Resize the video to be as large as possible but fit within a box of this size. + // 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); + private: + // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). + // Used internally whenever the resizing parameters or texture change. + void resize(); // Start the video Immediately virtual void startVideo(); // Stop the video diff --git a/external/pugixml b/external/pugixml new file mode 160000 index 000000000..d2deb420b --- /dev/null +++ b/external/pugixml @@ -0,0 +1 @@ +Subproject commit d2deb420bc70369faa12785df2b5dd4d390e523d