From 34ea9caa89c19473b5ca6f5cfbe76e8229b7d5c8 Mon Sep 17 00:00:00 2001 From: fieldofcows Date: Wed, 25 Jan 2017 15:00:56 +0000 Subject: [PATCH] Adding experimental OMX video player component. Squashed from fieldofcows' work (by pjft). --- .gitmodules | 3 - .../src/views/gamelist/VideoGameListView.cpp | 44 ++- es-app/src/views/gamelist/VideoGameListView.h | 2 +- es-core/CMakeLists.txt | 4 + es-core/src/GuiComponent.cpp | 18 +- es-core/src/GuiComponent.h | 9 +- es-core/src/Settings.cpp | 10 +- es-core/src/Window.cpp | 8 + es-core/src/components/VideoComponent.cpp | 251 ++---------------- es-core/src/components/VideoComponent.h | 42 +-- .../src/components/VideoPlayerComponent.cpp | 98 +++++++ es-core/src/components/VideoPlayerComponent.h | 30 +++ es-core/src/components/VideoVlcComponent.cpp | 247 +++++++++++++++++ es-core/src/components/VideoVlcComponent.h | 55 ++++ external/pugixml | 1 - 15 files changed, 545 insertions(+), 277 deletions(-) create mode 100644 es-core/src/components/VideoPlayerComponent.cpp create mode 100644 es-core/src/components/VideoPlayerComponent.h create mode 100644 es-core/src/components/VideoVlcComponent.cpp create mode 100644 es-core/src/components/VideoVlcComponent.h delete mode 160000 external/pugixml diff --git a/.gitmodules b/.gitmodules index ab38f31f3..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "external/pugixml"] - path = external/pugixml - url = https://github.com/zeux/pugixml.git diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 620c288f1..7cf23cf69 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -4,13 +4,18 @@ #include "animations/LambdaAnimation.h" #include #include +#ifdef _RPI_ +#include "components/VideoPlayerComponent.h" +#include "Settings.h" +#endif +#include "components/VideoVlcComponent.h" VideoGameListView::VideoGameListView(Window* window, FileData* root) : BasicGameListView(window, root), mDescContainer(window), mDescription(window), mMarquee(window), mImage(window), - mVideo(window), + mVideo(nullptr), mVideoPlaying(false), mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), @@ -21,6 +26,16 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : { const float padding = 0.01f; + // Create the correct type of video window +#ifdef _RPI_ + if (Settings::getInstance()->getBool("VideoOmxPlayer")) + mVideo = new VideoPlayerComponent(window); + else + mVideo = new VideoVlcComponent(window); +#else + mVideo = new VideoVlcComponent(window); +#endif + mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y()); mList.setAlignment(TextListComponent::ALIGN_LEFT); @@ -42,10 +57,10 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : addChild(&mImage); // video - mVideo.setOrigin(0.5f, 0.5f); - 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); + mVideo->setOrigin(0.5f, 0.5f); + 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); // metadata labels + values @@ -91,6 +106,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : VideoGameListView::~VideoGameListView() { + delete mVideo; } void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) @@ -100,7 +116,7 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) using namespace ThemeFlags; mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX); mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX); - mVideo.applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX); + mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX); initMDLabels(); std::vector labels = getMDLabels(); @@ -214,8 +230,8 @@ void VideoGameListView::updateInfoPanel() bool fadingOut; if(file == NULL) { - mVideo.setVideo(""); - mVideo.setImage(""); + mVideo->setVideo(""); + mVideo->setImage(""); mVideoPlaying = false; //mMarquee.setImage(""); //mDescription.setText(""); @@ -244,13 +260,13 @@ void VideoGameListView::updateInfoPanel() thumbnail_path.erase(0, 1); thumbnail_path.insert(0, getHomePath()); } - if (!mVideo.setVideo(video_path)) + if (!mVideo->setVideo(video_path)) { - mVideo.setDefaultVideo(); + mVideo->setDefaultVideo(); } mVideoPlaying = true; - mVideo.setImage(thumbnail_path); + mVideo->setImage(thumbnail_path); mMarquee.setImage(marquee_path); mImage.setImage(thumbnail_path); @@ -275,7 +291,7 @@ void VideoGameListView::updateInfoPanel() std::vector comps = getMDValues(); comps.push_back(&mMarquee); - comps.push_back(&mVideo); + comps.push_back(mVideo); comps.push_back(&mDescription); comps.push_back(&mImage); std::vector labels = getMDLabels(); @@ -304,7 +320,7 @@ void VideoGameListView::launch(FileData* game) { Eigen::Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); if(mMarquee.hasImage()) - target << mVideo.getCenter().x(), mVideo.getCenter().y(), 0; + target << mVideo->getCenter().x(), mVideo->getCenter().y(), 0; ViewController::get()->launch(game, target); } @@ -340,7 +356,7 @@ std::vector VideoGameListView::getMDValues() void VideoGameListView::update(int deltaTime) { BasicGameListView::update(deltaTime); - mVideo.update(deltaTime); + mVideo->update(deltaTime); } void VideoGameListView::onShow() diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index 0857f6ce2..09b12f602 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -30,7 +30,7 @@ private: void initMDValues(); ImageComponent mMarquee; - VideoComponent mVideo; + VideoComponent* mVideo; ImageComponent mImage; TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount; diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index f05aec77c..dd8d1ae41 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -43,6 +43,8 @@ set(CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoPlayerComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.h # Guis ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.h @@ -99,6 +101,8 @@ set(CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoPlayerComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp # Guis ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.cpp diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index 21d093d3c..c419aedab 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -5,7 +5,7 @@ #include "animations/AnimationController.h" #include "ThemeData.h" -GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL), mOpacity(255), +GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL), mOpacity(255), mPosition(Eigen::Vector3f::Zero()), mSize(Eigen::Vector2f::Zero()), mTransform(Eigen::Affine3f::Identity()), mIsProcessing(false) { @@ -390,4 +390,20 @@ void GuiComponent::onHide() getChild(i)->onHide(); } +void GuiComponent::onScreenSaverActivate() +{ + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->onScreenSaverActivate(); +} +void GuiComponent::onScreenSaverDeactivate() +{ + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->onScreenSaverDeactivate(); +} + +void GuiComponent::topWindow(bool isTop) +{ + for(unsigned int i = 0; i < getChildCount(); i++) + getChild(i)->topWindow(isTop); +} \ No newline at end of file diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index ec57662e0..66b921b88 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -45,7 +45,7 @@ public: void setSize(const Eigen::Vector2f& size); void setSize(float w, float h); virtual void onSizeChanged() {}; - + float getZIndex() const; void setZIndex(float zIndex); @@ -84,9 +84,12 @@ public: virtual void onFocusGained() {}; virtual void onFocusLost() {}; - + virtual void onShow(); virtual void onHide(); + virtual void onScreenSaverActivate(); + virtual void onScreenSaverDeactivate(); + virtual void topWindow(bool isTop); // Default implementation just handles and tags as normalized float pairs. // You probably want to keep this behavior for any derived classes as well as add your own. @@ -97,7 +100,7 @@ public: // Called whenever help prompts change. void updateHelpPrompts(); - + virtual HelpStyle getHelpStyle(); // Returns true if the component is busy doing background processing (e.g. HTTP downloads) diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index d85a1b991..fa9067f30 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -47,7 +47,7 @@ void Settings::setDefaults() mBoolMap["SplashScreen"] = true; #ifdef _RPI_ - // don't enable VSync by default on the Pi, since it already + // don't enable VSync by default on the Pi, since it already // has trouble trying to render things at 60fps in certain menus mBoolMap["VSync"] = false; #else @@ -76,6 +76,14 @@ void Settings::setDefaults() mStringMap["ScreenSaverBehavior"] = "dim"; mStringMap["Scraper"] = "TheGamesDB"; mStringMap["GamelistViewStyle"] = "automatic"; + + // 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 } template diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index a15405e0c..78fe8ea6c 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -29,6 +29,11 @@ Window::~Window() void Window::pushGui(GuiComponent* gui) { + if (mGuiStack.size() > 0) + { + auto& top = mGuiStack.back(); + top->topWindow(false); + } mGuiStack.push_back(gui); gui->updateHelpPrompts(); } @@ -42,7 +47,10 @@ void Window::removeGui(GuiComponent* gui) i = mGuiStack.erase(i); if(i == mGuiStack.end() && mGuiStack.size()) // we just popped the stack and the stack is not empty + { mGuiStack.back()->updateHelpPrompts(); + mGuiStack.back()->topWindow(true); + } return; } diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 6d7f9e0e7..2c49a44e6 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -8,54 +8,24 @@ #define FADE_TIME_MS 200 -libvlc_instance_t* VideoComponent::mVLC = NULL; - -// VLC prepares to render a video frame. -static void *lock(void *data, void **p_pixels) { - struct VideoContext *c = (struct VideoContext *)data; - SDL_LockMutex(c->mutex); - SDL_LockSurface(c->surface); - *p_pixels = c->surface->pixels; - return NULL; // Picture identifier, not needed here. -} - -// VLC just rendered a video frame. -static void unlock(void *data, void *id, void *const *p_pixels) { - struct VideoContext *c = (struct VideoContext *)data; - SDL_UnlockSurface(c->surface); - SDL_UnlockMutex(c->mutex); -} - -// VLC wants to display a video frame. -static void display(void *data, void *id) { - //Data to be displayed -} - VideoComponent::VideoComponent(Window* window) : GuiComponent(window), mStaticImage(window), - mMediaPlayer(nullptr), mVideoHeight(0), mVideoWidth(0), mStartDelayed(false), mIsPlaying(false), mShowing(false), + mScreensaverActive(false), + mDisable(false), mTargetIsMax(false), mOrigin(0, 0), mTargetSize(0, 0) { - memset(&mContext, 0, sizeof(mContext)); - // Setup the default configuration mConfig.showSnapshotDelay = false; mConfig.showSnapshotNoVideo = false; mConfig.startDelay = 0; - - // Get an empty texture for rendering the video - mTexture = TextureResource::get(""); - - // Make sure VLC has been initialised - setupVLC(); } VideoComponent::~VideoComponent() @@ -163,7 +133,7 @@ void VideoComponent::onSizeChanged() bool VideoComponent::setVideo(std::string path) { - // Convert the path into a format VLC can understand + // Convert the path into a generic format boost::filesystem::path fullPath = getCanonicalPath(path); fullPath.make_preferred().native(); @@ -189,7 +159,7 @@ void VideoComponent::setImage(std::string path) // Check that the image has changed if (path == mStaticImagePath) return; - + mStaticImage.setImage(path); mFadeIn = 0.0f; mStaticImagePath = path; @@ -215,83 +185,14 @@ void VideoComponent::render(const Eigen::Affine3f& parentTrans) GuiComponent::renderChildren(trans); Renderer::setMatrix(trans); - + // Handle the case where the video is delayed handleStartDelay(); // Handle looping of the video handleLooping(); - if (mIsPlaying && mContext.valid) - { - float tex_offs_x = 0.0f; - float tex_offs_y = 0.0f; - float x2; - float y2; - - x = -(float)mSize.x() * mOrigin.x(); - y = -(float)mSize.y() * mOrigin.y(); - x2 = x+mSize.x(); - y2 = y+mSize.y(); - - // Define a structure to contain the data for each vertex - struct Vertex - { - Eigen::Vector2f pos; - Eigen::Vector2f tex; - Eigen::Vector4f colour; - } vertices[6]; - - // We need two triangles to cover the rectangular area - vertices[0].pos[0] = x; vertices[0].pos[1] = y; - vertices[1].pos[0] = x; vertices[1].pos[1] = y2; - vertices[2].pos[0] = x2; vertices[2].pos[1] = y; - - vertices[3].pos[0] = x2; vertices[3].pos[1] = y; - vertices[4].pos[0] = x; vertices[4].pos[1] = y2; - vertices[5].pos[0] = x2; vertices[5].pos[1] = y2; - - // Texture coordinates - vertices[0].tex[0] = -tex_offs_x; vertices[0].tex[1] = -tex_offs_y; - vertices[1].tex[0] = -tex_offs_x; vertices[1].tex[1] = 1.0f + tex_offs_y; - vertices[2].tex[0] = 1.0f + tex_offs_x; vertices[2].tex[1] = -tex_offs_y; - - vertices[3].tex[0] = 1.0f + tex_offs_x; vertices[3].tex[1] = -tex_offs_y; - vertices[4].tex[0] = -tex_offs_x; vertices[4].tex[1] = 1.0f + tex_offs_y; - vertices[5].tex[0] = 1.0f + tex_offs_x; vertices[5].tex[1] = 1.0f + tex_offs_y; - - // Colours - use this to fade the video in and out - for (int i = 0; i < (4 * 6); ++i) { - if ((i%4) < 3) - vertices[i / 4].colour[i % 4] = mFadeIn; - else - vertices[i / 4].colour[i % 4] = 1.0f; - } - - glEnable(GL_TEXTURE_2D); - - // Build a texture for the video frame - mTexture->initFromPixels((unsigned char*)mContext.surface->pixels, mContext.surface->w, mContext.surface->h); - mTexture->bind(); - - // Render it - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glColorPointer(4, GL_FLOAT, sizeof(Vertex), &vertices[0].colour); - glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].pos); - glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].tex); - - glDrawArrays(GL_TRIANGLES, 0, 6); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glDisable(GL_TEXTURE_2D); - } - else + if (!mIsPlaying) { // This is the case where the video is not currently being displayed. Work out // if we need to display a static image @@ -361,38 +262,6 @@ std::vector VideoComponent::getHelpPrompts() return ret; } -void VideoComponent::setupContext() -{ - if (!mContext.valid) - { - // Create an RGBA surface to render the video into - mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); - mContext.mutex = SDL_CreateMutex(); - mContext.valid = true; - resize(); - } -} - -void VideoComponent::freeContext() -{ - if (mContext.valid) - { - SDL_FreeSurface(mContext.surface); - SDL_DestroyMutex(mContext.mutex); - mContext.valid = false; - } -} - -void VideoComponent::setupVLC() -{ - // If VLC hasn't been initialised yet then do it now - if (!mVLC) - { - const char* args[] = { "--quiet" }; - mVLC = libvlc_new(sizeof(args) / sizeof(args[0]), args); - } -} - void VideoComponent::handleStartDelay() { // Only play if any delay has timed out @@ -413,74 +282,6 @@ void VideoComponent::handleStartDelay() void VideoComponent::handleLooping() { - if (mIsPlaying && mMediaPlayer) - { - libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer); - if (state == libvlc_Ended) - { - //libvlc_media_player_set_position(mMediaPlayer, 0.0f); - libvlc_media_player_set_media(mMediaPlayer, mMedia); - libvlc_media_player_play(mMediaPlayer); - } - } -} - -void VideoComponent::startVideo() -{ - if (!mIsPlaying) { - mVideoWidth = 0; - mVideoHeight = 0; - -#ifdef WIN32 - std::wstring_convert, wchar_t> wton; - std::string path = wton.to_bytes(mVideoPath.c_str()); -#else - std::string path(mVideoPath.c_str()); -#endif - // Make sure we have a video path - if (mVLC && (path.size() > 0)) - { - // Set the video that we are going to be playing so we don't attempt to restart it - mPlayingVideoPath = mVideoPath; - - // Open the media - mMedia = libvlc_media_new_path(mVLC, path.c_str()); - if (mMedia) - { - unsigned track_count; - // Get the media metadata so we can find the aspect ratio - libvlc_media_parse(mMedia); - libvlc_media_track_t** tracks; - track_count = libvlc_media_tracks_get(mMedia, &tracks); - for (unsigned track = 0; track < track_count; ++track) - { - if (tracks[track]->i_type == libvlc_track_video) - { - mVideoWidth = tracks[track]->video->i_width; - mVideoHeight = tracks[track]->video->i_height; - break; - } - } - libvlc_media_tracks_release(tracks, track_count); - - // Make sure we found a valid video track - if ((mVideoWidth > 0) && (mVideoHeight > 0)) - { - setupContext(); - - // Setup the media player - mMediaPlayer = libvlc_media_player_new_from_media(mMedia); - 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); - - // Update the playing state - mIsPlaying = true; - mFadeIn = 0.0f; - } - } - } - } } void VideoComponent::startVideoWithDelay() @@ -508,21 +309,6 @@ void VideoComponent::startVideoWithDelay() } } -void VideoComponent::stopVideo() -{ - mIsPlaying = false; - mStartDelayed = false; - // Release the media player so it stops calling back to us - if (mMediaPlayer) - { - libvlc_media_player_stop(mMediaPlayer); - libvlc_media_player_release(mMediaPlayer); - libvlc_media_release(mMedia); - mMediaPlayer = NULL; - freeContext(); - } -} - void VideoComponent::update(int deltaTime) { manageState(); @@ -532,10 +318,10 @@ void VideoComponent::update(int deltaTime) if (mStartDelayed) { Uint32 ticks = SDL_GetTicks(); - if (mStartTime > ticks) + if (mStartTime > ticks) { Uint32 diff = mStartTime - ticks; - if (diff < FADE_TIME_MS) + if (diff < FADE_TIME_MS) { mFadeIn = (float)diff / (float)FADE_TIME_MS; return; @@ -554,8 +340,9 @@ void VideoComponent::update(int deltaTime) void VideoComponent::manageState() { - // We will only show if the component is on display - bool show = mShowing; + // We will only show if the component is on display and the screensaver + // is not active + bool show = mShowing && !mScreensaverActive && !mDisable; // See if we're already playing if (mIsPlaying) @@ -598,4 +385,20 @@ void VideoComponent::onHide() manageState(); } +void VideoComponent::onScreenSaverActivate() +{ + mScreensaverActive = true; + manageState(); +} +void VideoComponent::onScreenSaverDeactivate() +{ + mScreensaverActive = false; + manageState(); +} + +void VideoComponent::topWindow(bool isTop) +{ + mDisable = !isTop; + manageState(); +} diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 14179defa..db3d9500f 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -8,18 +8,10 @@ #include "ImageComponent.h" #include #include -#include "resources/TextureResource.h" -#include #include #include #include -struct VideoContext { - SDL_Surface* surface; - SDL_mutex* mutex; - bool valid; -}; - class VideoComponent : public GuiComponent { // Structure that groups together the configuration of the video component @@ -32,8 +24,6 @@ class VideoComponent : public GuiComponent }; public: - static void setupVLC(); - VideoComponent(Window* window); virtual ~VideoComponent(); @@ -44,9 +34,12 @@ public: // Configures the component to show the default video void setDefaultVideo(); - + virtual void onShow() override; virtual void onHide() override; + virtual void onScreenSaverActivate() override; + virtual void onScreenSaverDeactivate() override; + virtual void topWindow(bool isTop) override; //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center) void setOrigin(float originX, float originY); @@ -80,34 +73,23 @@ public: virtual void update(int deltaTime); 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 - void startVideo(); + virtual void startVideo() = 0; + // Stop the video + virtual void stopVideo() { }; + // Handle looping the video. Must be called periodically + virtual void handleLooping(); + // Start the video after any configured delay void startVideoWithDelay(); - // Stop the video - void stopVideo(); - - void setupContext(); - void freeContext(); // Handle any delay to the start of playing the video clip. Must be called periodically void handleStartDelay(); - // Handle looping the video. Must be called periodically - void handleLooping(); - // Manage the playing state of the component void manageState(); -private: - static libvlc_instance_t* mVLC; - libvlc_media_t* mMedia; - libvlc_media_player_t* mMediaPlayer; - VideoContext mContext; +protected: unsigned mVideoWidth; unsigned mVideoHeight; Eigen::Vector2f mOrigin; @@ -123,6 +105,8 @@ private: unsigned mStartTime; bool mIsPlaying; bool mShowing; + bool mDisable; + bool mScreensaverActive; bool mTargetIsMax; Configuration mConfig; diff --git a/es-core/src/components/VideoPlayerComponent.cpp b/es-core/src/components/VideoPlayerComponent.cpp new file mode 100644 index 000000000..fb44eb25d --- /dev/null +++ b/es-core/src/components/VideoPlayerComponent.cpp @@ -0,0 +1,98 @@ +#ifdef _RPI_ +#include "components/VideoPlayerComponent.h" +#include "Renderer.h" +#include "ThemeData.h" +#include "Util.h" +#include +#include +#include +#include +#include + +VideoPlayerComponent::VideoPlayerComponent(Window* window) : + VideoComponent(window), + mPlayerPid(-1) +{ +} + +VideoPlayerComponent::~VideoPlayerComponent() +{ +} + +void VideoPlayerComponent::render(const Eigen::Affine3f& parentTrans) +{ + VideoComponent::render(parentTrans); +} + +void VideoPlayerComponent::startVideo() +{ + if (!mIsPlaying) { + mVideoWidth = 0; + mVideoHeight = 0; + + std::string path(mVideoPath.c_str()); + + // Make sure we have a video path + if ((path.size() > 0) && (mPlayerPid == -1)) + { + // Set the video that we are going to be playing so we don't attempt to restart it + mPlayingVideoPath = mVideoPath; + + // Start the player process + pid_t pid = fork(); + if (pid == -1) + { + // Failed + mPlayingVideoPath = ""; + } + else if (pid > 0) + { + mPlayerPid = pid; + // Update the playing state + 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]; + float x = mPosition.x() - (mOrigin.x() * mSize.x()); + float y = mPosition.y() - (mOrigin.y() * mSize.y()); + 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* 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); + dup2(fdin, 0); + dup2(fdout, 1); + // Run the omxplayer binary + execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env); + _exit(EXIT_FAILURE); + } + } + } +} + +void VideoPlayerComponent::stopVideo() +{ + mIsPlaying = false; + mStartDelayed = false; + + // Stop the player process + if (mPlayerPid != -1) + { + int status; + kill(mPlayerPid, SIGKILL); + waitpid(mPlayerPid, &status, WNOHANG); + mPlayerPid = -1; + } +} + +#endif + diff --git a/es-core/src/components/VideoPlayerComponent.h b/es-core/src/components/VideoPlayerComponent.h new file mode 100644 index 000000000..45517e91a --- /dev/null +++ b/es-core/src/components/VideoPlayerComponent.h @@ -0,0 +1,30 @@ +#ifdef _RPI_ +#ifndef _VIDEOPLAYERCOMPONENT_H_ +#define _VIDEOPLAYERCOMPONENT_H_ + +#include "platform.h" +#include GLHEADER + +#include "components/VideoComponent.h" + +class VideoPlayerComponent : public VideoComponent +{ +public: + VideoPlayerComponent(Window* window); + virtual ~VideoPlayerComponent(); + + void render(const Eigen::Affine3f& parentTrans) override; + +private: + // Start the video Immediately + virtual void startVideo(); + // Stop the video + virtual void stopVideo(); + +private: + pid_t mPlayerPid; +}; + +#endif +#endif + diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp new file mode 100644 index 000000000..e0da5003e --- /dev/null +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -0,0 +1,247 @@ +#include "components/VideoVlcComponent.h" +#include "Renderer.h" +#include "ThemeData.h" +#include "Util.h" +#ifdef WIN32 +#include +#endif + +libvlc_instance_t* VideoVlcComponent::mVLC = NULL; + +// VLC prepares to render a video frame. +static void *lock(void *data, void **p_pixels) { + struct VideoContext *c = (struct VideoContext *)data; + SDL_LockMutex(c->mutex); + SDL_LockSurface(c->surface); + *p_pixels = c->surface->pixels; + return NULL; // Picture identifier, not needed here. +} + +// VLC just rendered a video frame. +static void unlock(void *data, void *id, void *const *p_pixels) { + struct VideoContext *c = (struct VideoContext *)data; + SDL_UnlockSurface(c->surface); + SDL_UnlockMutex(c->mutex); +} + +// VLC wants to display a video frame. +static void display(void *data, void *id) { + //Data to be displayed +} + +VideoVlcComponent::VideoVlcComponent(Window* window) : + VideoComponent(window), + mMediaPlayer(nullptr) +{ + memset(&mContext, 0, sizeof(mContext)); + + // Get an empty texture for rendering the video + mTexture = TextureResource::get(""); + + // Make sure VLC has been initialised + setupVLC(); +} + +VideoVlcComponent::~VideoVlcComponent() +{ +} + +void VideoVlcComponent::render(const Eigen::Affine3f& parentTrans) +{ + VideoComponent::render(parentTrans); + float x, y; + + Eigen::Affine3f trans = parentTrans * getTransform(); + GuiComponent::renderChildren(trans); + + Renderer::setMatrix(trans); + + if (mIsPlaying && mContext.valid) + { + float tex_offs_x = 0.0f; + float tex_offs_y = 0.0f; + float x2; + float y2; + + x = -(float)mSize.x() * mOrigin.x(); + y = -(float)mSize.y() * mOrigin.y(); + x2 = x+mSize.x(); + y2 = y+mSize.y(); + + // Define a structure to contain the data for each vertex + struct Vertex + { + Eigen::Vector2f pos; + Eigen::Vector2f tex; + Eigen::Vector4f colour; + } vertices[6]; + + // We need two triangles to cover the rectangular area + vertices[0].pos[0] = x; vertices[0].pos[1] = y; + vertices[1].pos[0] = x; vertices[1].pos[1] = y2; + vertices[2].pos[0] = x2; vertices[2].pos[1] = y; + + vertices[3].pos[0] = x2; vertices[3].pos[1] = y; + vertices[4].pos[0] = x; vertices[4].pos[1] = y2; + vertices[5].pos[0] = x2; vertices[5].pos[1] = y2; + + // Texture coordinates + vertices[0].tex[0] = -tex_offs_x; vertices[0].tex[1] = -tex_offs_y; + vertices[1].tex[0] = -tex_offs_x; vertices[1].tex[1] = 1.0f + tex_offs_y; + vertices[2].tex[0] = 1.0f + tex_offs_x; vertices[2].tex[1] = -tex_offs_y; + + vertices[3].tex[0] = 1.0f + tex_offs_x; vertices[3].tex[1] = -tex_offs_y; + vertices[4].tex[0] = -tex_offs_x; vertices[4].tex[1] = 1.0f + tex_offs_y; + vertices[5].tex[0] = 1.0f + tex_offs_x; vertices[5].tex[1] = 1.0f + tex_offs_y; + + // Colours - use this to fade the video in and out + for (int i = 0; i < (4 * 6); ++i) { + if ((i%4) < 3) + vertices[i / 4].colour[i % 4] = mFadeIn; + else + vertices[i / 4].colour[i % 4] = 1.0f; + } + + glEnable(GL_TEXTURE_2D); + + // Build a texture for the video frame + mTexture->initFromPixels((unsigned char*)mContext.surface->pixels, mContext.surface->w, mContext.surface->h); + mTexture->bind(); + + // Render it + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glColorPointer(4, GL_FLOAT, sizeof(Vertex), &vertices[0].colour); + glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].pos); + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].tex); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_TEXTURE_2D); + } +} + +void VideoVlcComponent::setupContext() +{ + if (!mContext.valid) + { + // Create an RGBA surface to render the video into + mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); + mContext.mutex = SDL_CreateMutex(); + mContext.valid = true; + } +} + +void VideoVlcComponent::freeContext() +{ + if (mContext.valid) + { + SDL_FreeSurface(mContext.surface); + SDL_DestroyMutex(mContext.mutex); + mContext.valid = false; + } +} + +void VideoVlcComponent::setupVLC() +{ + // If VLC hasn't been initialised yet then do it now + if (!mVLC) + { + const char* args[] = { "--quiet" }; + mVLC = libvlc_new(sizeof(args) / sizeof(args[0]), args); + } +} + +void VideoVlcComponent::handleLooping() +{ + if (mIsPlaying && mMediaPlayer) + { + libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer); + if (state == libvlc_Ended) + { + //libvlc_media_player_set_position(mMediaPlayer, 0.0f); + libvlc_media_player_set_media(mMediaPlayer, mMedia); + libvlc_media_player_play(mMediaPlayer); + } + } +} + +void VideoVlcComponent::startVideo() +{ + if (!mIsPlaying) { + mVideoWidth = 0; + mVideoHeight = 0; + +#ifdef WIN32 + std::wstring_convert, wchar_t> wton; + std::string path = wton.to_bytes(mVideoPath.c_str()); +#else + std::string path(mVideoPath.c_str()); +#endif + // Make sure we have a video path + if (mVLC && (path.size() > 0)) + { + // Set the video that we are going to be playing so we don't attempt to restart it + mPlayingVideoPath = mVideoPath; + + // Open the media + mMedia = libvlc_media_new_path(mVLC, path.c_str()); + if (mMedia) + { + unsigned track_count; + // Get the media metadata so we can find the aspect ratio + libvlc_media_parse(mMedia); + libvlc_media_track_t** tracks; + track_count = libvlc_media_tracks_get(mMedia, &tracks); + for (unsigned track = 0; track < track_count; ++track) + { + if (tracks[track]->i_type == libvlc_track_video) + { + mVideoWidth = tracks[track]->video->i_width; + mVideoHeight = tracks[track]->video->i_height; + break; + } + } + libvlc_media_tracks_release(tracks, track_count); + + // Make sure we found a valid video track + if ((mVideoWidth > 0) && (mVideoHeight > 0)) + { + setupContext(); + + // Setup the media player + mMediaPlayer = libvlc_media_player_new_from_media(mMedia); + 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); + + // Update the playing state + mIsPlaying = true; + mFadeIn = 0.0f; + } + } + } + } +} + +void VideoVlcComponent::stopVideo() +{ + mIsPlaying = false; + mStartDelayed = false; + // Release the media player so it stops calling back to us + if (mMediaPlayer) + { + libvlc_media_player_stop(mMediaPlayer); + libvlc_media_player_release(mMediaPlayer); + libvlc_media_release(mMedia); + mMediaPlayer = NULL; + freeContext(); + } +} + diff --git a/es-core/src/components/VideoVlcComponent.h b/es-core/src/components/VideoVlcComponent.h new file mode 100644 index 000000000..2700317a2 --- /dev/null +++ b/es-core/src/components/VideoVlcComponent.h @@ -0,0 +1,55 @@ +#ifndef _VIDEOVLCCOMPONENT_H_ +#define _VIDEOVLCCOMPONENT_H_ + +#include "platform.h" +#include GLHEADER + +#include "VideoComponent.h" +#include +#include "resources/TextureResource.h" + +struct VideoContext { + SDL_Surface* surface; + SDL_mutex* mutex; + bool valid; +}; + +class VideoVlcComponent : public VideoComponent +{ + // Structure that groups together the configuration of the video component + struct Configuration + { + unsigned startDelay; + bool showSnapshotNoVideo; + bool showSnapshotDelay; + std::string defaultVideoPath; + }; + +public: + static void setupVLC(); + + VideoVlcComponent(Window* window); + virtual ~VideoVlcComponent(); + + void render(const Eigen::Affine3f& parentTrans) override; + +private: + // Start the video Immediately + virtual void startVideo(); + // Stop the video + virtual void stopVideo(); + // Handle looping the video. Must be called periodically + virtual void handleLooping(); + + void setupContext(); + void freeContext(); + +private: + static libvlc_instance_t* mVLC; + libvlc_media_t* mMedia; + libvlc_media_player_t* mMediaPlayer; + VideoContext mContext; + std::shared_ptr mTexture; +}; + +#endif diff --git a/external/pugixml b/external/pugixml deleted file mode 160000 index d2deb420b..000000000 --- a/external/pugixml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d2deb420bc70369faa12785df2b5dd4d390e523d